From 7321daf4f6f141efae218a34b2b69c482d1dfeed Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 18 Feb 2022 08:41:34 +0100 Subject: [PATCH 001/306] Update README with approximate pipeline outline --- README.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f33f2c..f57666d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ ## Introduction -**nf-core/taxprofiler** is a bioinformatics best-practice analysis pipeline for Taxonomic profiling of shotgun metagenomic data. +**nf-core/taxprofiler** is a bioinformatics best-practice analysis pipeline for taxonomic profiling of shotgun metagenomic data. It allows for in-parallel profiling against multiple profiling tools and databases and produces standardised output tables. 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! @@ -29,7 +29,23 @@ On release, automated continuous integration tests run the pipeline on a full-si 1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) -2. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) +2. Performs optional read pre-processing + - Adapter clipping and merging + - Low complexity filtering + - Host read removal + - Run merging +3. Performs taxonomic profiling a choice of: + - Kraken2 + - MetaPhlAn3 + - MALT + - DIAMOND + - Centrifuge + - Kaiju + - mOTUs +4. Perform optional post-processing with: + - bracken +5. Standardises output tables +6. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) ## Quick Start From c68afbbf90c98e87716dc526d6c5a9631a341201 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Fri, 18 Feb 2022 08:45:06 +0100 Subject: [PATCH 002/306] Update nextflow.config --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index c2d51b5..186979c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -121,7 +121,7 @@ if (!params.igenomes_ignore) { } // Export these variables to prevent local Python/R libraries from conflicting with those in the container -// The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. +// The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. // See https://apeltzer.github.io/post/03-julia-lang-nextflow/ for details on that. Once we have a common agreement on where to keep Julia packages, this is adjustable. env { From f867c057a4e1f0c7f4fc23db44356ee120de261f Mon Sep 17 00:00:00 2001 From: maxibor Date: Fri, 18 Feb 2022 11:53:13 +0100 Subject: [PATCH 003/306] add more columns to samplesheet --- bin/check_samplesheet.py | 138 +++++++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 20 deletions(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 41dd9aa..eb6a7dc 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -3,6 +3,7 @@ # TODO nf-core: Update the script to check the samplesheet # This script is based on the example at: https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv +from distutils import extension import os import sys import errno @@ -10,7 +11,9 @@ import argparse def parse_args(args=None): - Description = "Reformat nf-core/taxprofiler samplesheet file and check its contents." + Description = ( + "Reformat nf-core/taxprofiler samplesheet file and check its contents." + ) Epilog = "Example usage: python check_samplesheet.py " parser = argparse.ArgumentParser(description=Description, epilog=Epilog) @@ -43,25 +46,62 @@ def check_samplesheet(file_in, file_out): """ This function checks that the samplesheet follows the following structure: - sample,fastq_1,fastq_2 - SAMPLE_PE,SAMPLE_PE_RUN1_1.fastq.gz,SAMPLE_PE_RUN1_2.fastq.gz - SAMPLE_PE,SAMPLE_PE_RUN2_1.fastq.gz,SAMPLE_PE_RUN2_2.fastq.gz - SAMPLE_SE,SAMPLE_SE_RUN1_1.fastq.gz, + sample,run_accession,instrument_platform,fastq_1,fastq_2,fasta + 2611,ERR5766174,ILLUMINA,NA,NA,FA_EXTENSIONSERX5474930_ERR5766174_1.fa.gz + 2612,ERR5766176,ILLUMINA,ERX5474932_ERR5766176_1.fastq.gz,ERX5474932_ERR5766176_2.fastq.gz,NA + 2612,ERR5766174,ILLUMINA,ERX5474936_ERR5766180_1.fastq.gz,NA,NA + 2613,ERR5766181,ILLUMINA,ERX5474930_ERR5766174_1.fa.gz,ERX5474930_ERR5766174_2.fa.gz,NA For an example see: https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv """ + FQ_EXTENSIONS = (".fq", ".fq.gz", ".fastq", ".fastq.gz") + FA_EXTENSIONS = ( + ".fa", + ".fa.gz", + ".fasta", + ".fasta.gz", + ".fna", + ".fna.gz", + ".fas", + ".fas.gz", + ) + INSTRUMENT_PLATFORMS = [ + "ABI_SOLID", + "BGISEQ", + "CAPILLARY", + "COMPLETE_GENOMICS", + "DNBSEQ", + "HELICOS", + "ILLUMINA", + "ION_TORRENT", + "LS454", + "OXFORD_NANOPORE", + "PACBIO_SMRT", + ] + sample_mapping_dict = {} with open(file_in, "r") as fin: ## Check header - MIN_COLS = 2 + MIN_COLS = 4 # TODO nf-core: Update the column names for the input samplesheet - HEADER = ["sample", "fastq_1", "fastq_2"] + HEADER = [ + "sample", + "run_accession", + "instrument_platform", + "fastq_1", + "fastq_2", + "fasta", + ] header = [x.strip('"') for x in fin.readline().strip().split(",")] if header[: len(HEADER)] != HEADER: - print("ERROR: Please check samplesheet header -> {} != {}".format(",".join(header), ",".join(HEADER))) + print( + "ERROR: Please check samplesheet header -> {} != {}".format( + ",".join(header), ",".join(HEADER) + ) + ) sys.exit(1) ## Check sample entries @@ -78,13 +118,22 @@ def check_samplesheet(file_in, file_out): num_cols = len([x for x in lspl if x]) if num_cols < MIN_COLS: print_error( - "Invalid number of populated columns (minimum = {})!".format(MIN_COLS), + "Invalid number of populated columns (minimum = {})!".format( + MIN_COLS + ), "Line", line, ) ## Check sample name entries - sample, fastq_1, fastq_2 = lspl[: len(HEADER)] + ( + sample, + run_accession, + instrument_platform, + fastq_1, + fastq_2, + fasta, + ) = lspl[: len(HEADER)] sample = sample.replace(" ", "_") if not sample: print_error("Sample entry has not been specified!", "Line", line) @@ -94,23 +143,55 @@ def check_samplesheet(file_in, file_out): if fastq: if fastq.find(" ") != -1: print_error("FastQ file contains spaces!", "Line", line) - if not fastq.endswith(".fastq.gz") and not fastq.endswith(".fq.gz"): + if not fastq.endswith(FQ_EXTENSIONS): print_error( - "FastQ file does not have extension '.fastq.gz' or '.fq.gz'!", + f"FastQ file does not have extension {' or '.join(list(FQ_EXTENSIONS))} !", "Line", line, ) + if fasta: + if fasta.find(" ") != -1: + print_error("FastA file contains spaces!", "Line", line) + if not fasta.endswith(FA_EXTENSIONS): + print_error( + f"FastA file does not have extension {' or '.join(list(FA_EXTENSIONS))}!", + "Line", + line, + ) + sample_info = [] + + # Check run_accession + if not run_accession: + print_error("Run accession has not been specified!", "Line", line) + else: + sample_info.append(run_accession) + + # Check instrument_platform + if not instrument_platform: + print_error("Instrument platform has not been specified!", "Line", line) + else: + if instrument_platform not in INSTRUMENT_PLATFORMS: + print_error( + f"Instrument platform {instrument_platform} is not supported!", + f"List of supported platforms {', '.join(INSTRUMENT_PLATFORMS)}", + "Line", + line, + ) + sample_info.append(instrument_platform) ## Auto-detect paired-end/single-end - sample_info = [] ## [single_end, fastq_1, fastq_2] if sample and fastq_1 and fastq_2: ## Paired-end short reads - sample_info = ["0", fastq_1, fastq_2] + sample_info.extend(["0", fastq_1, fastq_2, fasta]) elif sample and fastq_1 and not fastq_2: ## Single-end short reads - sample_info = ["1", fastq_1, fastq_2] + sample_info.extend(["1", fastq_1, fastq_2, fasta]) + elif ( + sample and fasta and not fastq_1 and not fastq_2 + ): ## Single-end long reads + sample_info.extend(["1", fastq_1, fastq_2, fasta]) else: print_error("Invalid combination of columns provided!", "Line", line) - ## Create sample mapping dictionary = { sample: [ single_end, fastq_1, fastq_2 ] } + ## Create sample mapping dictionary = { sample: [ single_end, fastq_1, fastq_2 , fasta, run_accession, instrument_platform] } if sample not in sample_mapping_dict: sample_mapping_dict[sample] = [sample_info] else: @@ -120,19 +201,36 @@ def check_samplesheet(file_in, file_out): sample_mapping_dict[sample].append(sample_info) ## Write validated samplesheet with appropriate columns + HEADER_OUT = [ + "sample", + "run_accession", + "instrument_platform", + "single_end", + "fastq_1", + "fastq_2", + "fasta", + ] if len(sample_mapping_dict) > 0: out_dir = os.path.dirname(file_out) make_dir(out_dir) with open(file_out, "w") as fout: - fout.write(",".join(["sample", "single_end", "fastq_1", "fastq_2"]) + "\n") + fout.write(",".join(HEADER_OUT) + "\n") for sample in sorted(sample_mapping_dict.keys()): ## Check that multiple runs of the same sample are of the same datatype - if not all(x[0] == sample_mapping_dict[sample][0][0] for x in sample_mapping_dict[sample]): - print_error("Multiple runs of a sample must be of the same datatype!", "Sample: {}".format(sample)) + if not all( + x[0] == sample_mapping_dict[sample][0][0] + for x in sample_mapping_dict[sample] + ): + print_error( + "Multiple runs of a sample must be of the same datatype!", + "Sample: {}".format(sample), + ) for idx, val in enumerate(sample_mapping_dict[sample]): - fout.write(",".join(["{}_T{}".format(sample, idx + 1)] + val) + "\n") + fout.write( + ",".join(["{}_T{}".format(sample, idx + 1)] + val) + "\n" + ) else: print_error("No entries to process!", "Samplesheet: {}".format(file_in)) From 54a1a4fd459afa7e9987c72e5e1d4b7ac67683d0 Mon Sep 17 00:00:00 2001 From: maxibor Date: Fri, 18 Feb 2022 13:11:18 +0100 Subject: [PATCH 004/306] update samplesheet specs --- bin/check_samplesheet.py | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index eb6a7dc..77a5107 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -47,13 +47,10 @@ def check_samplesheet(file_in, file_out): This function checks that the samplesheet follows the following structure: sample,run_accession,instrument_platform,fastq_1,fastq_2,fasta - 2611,ERR5766174,ILLUMINA,NA,NA,FA_EXTENSIONSERX5474930_ERR5766174_1.fa.gz - 2612,ERR5766176,ILLUMINA,ERX5474932_ERR5766176_1.fastq.gz,ERX5474932_ERR5766176_2.fastq.gz,NA - 2612,ERR5766174,ILLUMINA,ERX5474936_ERR5766180_1.fastq.gz,NA,NA - 2613,ERR5766181,ILLUMINA,ERX5474930_ERR5766174_1.fa.gz,ERX5474930_ERR5766174_2.fa.gz,NA - - For an example see: - https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv + 2611,ERR5766174,ILLUMINA,,,ERX5474930_ERR5766174_1.fa.gz + 2612,ERR5766176,ILLUMINA,ERX5474932_ERR5766176_1.fastq.gz,ERX5474932_ERR5766176_2.fastq.gz, + 2612,ERR5766174,ILLUMINA,ERX5474936_ERR5766180_1.fastq.gz,, + 2613,ERR5766181,ILLUMINA,ERX5474937_ERR5766181_1.fastq.gz,ERX5474937_ERR5766181_2.fastq.gz, """ FQ_EXTENSIONS = (".fq", ".fq.gz", ".fastq", ".fastq.gz") @@ -216,21 +213,9 @@ def check_samplesheet(file_in, file_out): with open(file_out, "w") as fout: fout.write(",".join(HEADER_OUT) + "\n") for sample in sorted(sample_mapping_dict.keys()): - - ## Check that multiple runs of the same sample are of the same datatype - if not all( - x[0] == sample_mapping_dict[sample][0][0] - for x in sample_mapping_dict[sample] - ): - print_error( - "Multiple runs of a sample must be of the same datatype!", - "Sample: {}".format(sample), - ) - for idx, val in enumerate(sample_mapping_dict[sample]): - fout.write( - ",".join(["{}_T{}".format(sample, idx + 1)] + val) + "\n" - ) + fout.write(f"{sample},{','.join(val)}\n") + # fout.write(f",".join(["{}".format(sample)] + val) + "\n") else: print_error("No entries to process!", "Samplesheet: {}".format(file_in)) From a6cfa0a1ba3b9c0ad2263d8225bcbc05117f4bc3 Mon Sep 17 00:00:00 2001 From: maxibor Date: Fri, 18 Feb 2022 13:15:30 +0100 Subject: [PATCH 005/306] cleanup --- bin/check_samplesheet.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 77a5107..0f19523 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -1,8 +1,5 @@ #!/usr/bin/env python -# TODO nf-core: Update the script to check the samplesheet -# This script is based on the example at: https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv - from distutils import extension import os import sys @@ -83,7 +80,6 @@ def check_samplesheet(file_in, file_out): ## Check header MIN_COLS = 4 - # TODO nf-core: Update the column names for the input samplesheet HEADER = [ "sample", "run_accession", @@ -188,7 +184,7 @@ def check_samplesheet(file_in, file_out): else: print_error("Invalid combination of columns provided!", "Line", line) - ## Create sample mapping dictionary = { sample: [ single_end, fastq_1, fastq_2 , fasta, run_accession, instrument_platform] } + ## Create sample mapping dictionary = { sample: [ run_accession, instrument_platform, single_end, fastq_1, fastq_2 , fasta ] } if sample not in sample_mapping_dict: sample_mapping_dict[sample] = [sample_info] else: @@ -215,7 +211,6 @@ def check_samplesheet(file_in, file_out): for sample in sorted(sample_mapping_dict.keys()): for idx, val in enumerate(sample_mapping_dict[sample]): fout.write(f"{sample},{','.join(val)}\n") - # fout.write(f",".join(["{}".format(sample)] + val) + "\n") else: print_error("No entries to process!", "Samplesheet: {}".format(file_in)) From 1b893cb039fee4544cef4d716668ef62c8551d17 Mon Sep 17 00:00:00 2001 From: maxibor Date: Fri, 18 Feb 2022 13:27:10 +0100 Subject: [PATCH 006/306] add check for fastq with fasta --- bin/check_samplesheet.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 0f19523..d6e7123 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -181,6 +181,12 @@ def check_samplesheet(file_in, file_out): sample and fasta and not fastq_1 and not fastq_2 ): ## Single-end long reads sample_info.extend(["1", fastq_1, fastq_2, fasta]) + elif fasta and (fastq_1 or fastq_2): + print_error( + "FastQ and FastA files cannot be specified together in the same library!", + "Line", + line, + ) else: print_error("Invalid combination of columns provided!", "Line", line) From cf55cc592cf41868f8cd480f53116525ab08f960 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 18 Feb 2022 16:51:01 +0100 Subject: [PATCH 007/306] Get skeleton read processing to input for profiling --- README.md | 6 +- conf/modules.config | 39 +++++++++++ conf/test.config | 4 +- lib/WorkflowTaxprofiler.groovy | 9 +-- modules.json | 6 ++ modules/nf-core/modules/cat/fastq/main.nf | 51 ++++++++++++++ modules/nf-core/modules/cat/fastq/meta.yml | 39 +++++++++++ modules/nf-core/modules/fastp/main.nf | 75 ++++++++++++++++++++ modules/nf-core/modules/fastp/meta.yml | 68 ++++++++++++++++++ nextflow.config | 5 +- nextflow_schema.json | 9 --- subworkflows/local/input_check.nf | 43 ++++++++++-- workflows/taxprofiler.nf | 80 +++++++++++++++++++++- 13 files changed, 407 insertions(+), 27 deletions(-) create mode 100644 modules/nf-core/modules/cat/fastq/main.nf create mode 100644 modules/nf-core/modules/cat/fastq/meta.yml create mode 100644 modules/nf-core/modules/fastp/main.nf create mode 100644 modules/nf-core/modules/fastp/meta.yml diff --git a/README.md b/README.md index f57666d..5f00709 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,9 @@ On release, automated continuous integration tests run the pipeline on a full-si - Centrifuge - Kaiju - mOTUs -4. Perform optional post-processing with: - - bracken -5. Standardises output tables +4. Perform optional post-processing with: + - bracken +5. Standardises output tables 6. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) ## Quick Start diff --git a/conf/modules.config b/conf/modules.config index a0506a4..2d533e9 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -28,8 +28,47 @@ process { withName: FASTQC { ext.args = '--quiet' + ext.prefix = { "${meta.id}_${meta.run_accession}_raw" } + publishDir = [ + path: { "${params.outdir}/fastqc/raw" }, + mode: 'copy', + pattern: '*.html' + ] } + withName: FASTP { + ext.prefix = { "${meta.id}_${meta.run_accession}" } + // TODO also include option to NOT merge + ext.args = [ + { ${meta.single_end} } == 0 ? "-m" : '', + params.fastp_exclude_unmerged ? '' : "--include_unmerged" + ].join(' ').trim() + publishDir = [ + path: { "${params.outdir}/fastp" }, + mode: 'copy', + pattern: '*.fastq.gz' + ] + } + + withName: FASTQC_POST { + ext.args = '--quiet' + ext.prefix = { "${meta.id}_${meta.run_accession}_processed" } + publishDir = [ + path: { "${params.outdir}/fastqc/processed" }, + mode: 'copy', + pattern: '*.html' + ] + } + + withName: CAT_FASTQ { + publishDir = [ + path: { "${params.outdir}/prepared_sequences" }, + mode: 'copy', + pattern: '*.fastq.gz' + ] + } + + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ path: { "${params.outdir}/pipeline_info" }, diff --git a/conf/test.config b/conf/test.config index 45f87af..5db566a 100644 --- a/conf/test.config +++ b/conf/test.config @@ -22,8 +22,6 @@ params { // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv' + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' - // Genome references - genome = 'R64-1-1' } diff --git a/lib/WorkflowTaxprofiler.groovy b/lib/WorkflowTaxprofiler.groovy index 53482ac..57a95e3 100755 --- a/lib/WorkflowTaxprofiler.groovy +++ b/lib/WorkflowTaxprofiler.groovy @@ -10,10 +10,11 @@ class WorkflowTaxprofiler { public static void initialise(params, log) { genomeExistsError(params, log) - if (!params.fasta) { - log.error "Genome fasta file not specified with e.g. '--fasta genome.fa' or via a detectable config file." - System.exit(1) - } + // TODO update as necessary + //if (!params.fasta) { + // log.error "Genome fasta file not specified with e.g. '--fasta genome.fa' or via a detectable config file." + // System.exit(1) + //} } // diff --git a/modules.json b/modules.json index 939b85f..6cf2b3e 100644 --- a/modules.json +++ b/modules.json @@ -3,9 +3,15 @@ "homePage": "https://github.com/nf-core/taxprofiler", "repos": { "nf-core/modules": { + "cat/fastq": { + "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + }, "custom/dumpsoftwareversions": { "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, + "fastp": { + "git_sha": "d0a1cbb703a130c19f6796c3fce24fbe7dfce789" + }, "fastqc": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, diff --git a/modules/nf-core/modules/cat/fastq/main.nf b/modules/nf-core/modules/cat/fastq/main.nf new file mode 100644 index 0000000..bf0877c --- /dev/null +++ b/modules/nf-core/modules/cat/fastq/main.nf @@ -0,0 +1,51 @@ +process CAT_FASTQ { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "conda-forge::sed=4.7" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + 'biocontainers/biocontainers:v1.2.0_cv1' }" + + input: + tuple val(meta), path(reads, stageAs: "input*/*") + + output: + tuple val(meta), path("*.merged.fastq.gz"), emit: reads + 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}" + def readList = reads.collect{ it.toString() } + if (meta.single_end) { + if (readList.size > 1) { + """ + cat ${readList.join(' ')} > ${prefix}.merged.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$(echo \$(cat --version 2>&1) | sed 's/^.*coreutils) //; s/ .*\$//') + END_VERSIONS + """ + } + } else { + if (readList.size > 2) { + def read1 = [] + def read2 = [] + readList.eachWithIndex{ v, ix -> ( ix & 1 ? read2 : read1 ) << v } + """ + cat ${read1.join(' ')} > ${prefix}_1.merged.fastq.gz + cat ${read2.join(' ')} > ${prefix}_2.merged.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$(echo \$(cat --version 2>&1) | sed 's/^.*coreutils) //; s/ .*\$//') + END_VERSIONS + """ + } + } +} diff --git a/modules/nf-core/modules/cat/fastq/meta.yml b/modules/nf-core/modules/cat/fastq/meta.yml new file mode 100644 index 0000000..c836598 --- /dev/null +++ b/modules/nf-core/modules/cat/fastq/meta.yml @@ -0,0 +1,39 @@ +name: cat_fastq +description: Concatenates fastq files +keywords: + - fastq + - concatenate +tools: + - cat: + description: | + The cat utility reads files sequentially, writing them to the standard output. + documentation: https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html + licence: ["GPL-3.0-or-later"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: list + description: | + List of input FastQ files to be concatenated. +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: Merged fastq file + pattern: "*.{merged.fastq.gz}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/modules/fastp/main.nf b/modules/nf-core/modules/fastp/main.nf new file mode 100644 index 0000000..5c9e3b8 --- /dev/null +++ b/modules/nf-core/modules/fastp/main.nf @@ -0,0 +1,75 @@ +process FASTP { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? 'bioconda::fastp=0.23.2' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/fastp:0.23.2--h79da9fb_0' : + 'quay.io/biocontainers/fastp:0.23.2--h79da9fb_0' }" + + input: + tuple val(meta), path(reads) + val save_trimmed_fail + val save_merged + + output: + tuple val(meta), path('*.trim.fastq.gz') , optional:true, emit: reads + tuple val(meta), path('*.json') , emit: json + tuple val(meta), path('*.html') , emit: html + tuple val(meta), path('*.log') , emit: log + path "versions.yml" , emit: versions + tuple val(meta), path('*.fail.fastq.gz') , optional:true, emit: reads_fail + tuple val(meta), path('*.merged.fastq.gz'), optional:true, emit: reads_merged + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + // Added soft-links to original fastqs for consistent naming in MultiQC + def prefix = task.ext.prefix ?: "${meta.id}" + if (meta.single_end) { + def fail_fastq = save_trimmed_fail ? "--failed_out ${prefix}.fail.fastq.gz" : '' + """ + [ ! -f ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz + fastp \\ + --in1 ${prefix}.fastq.gz \\ + --out1 ${prefix}.trim.fastq.gz \\ + --thread $task.cpus \\ + --json ${prefix}.fastp.json \\ + --html ${prefix}.fastp.html \\ + $fail_fastq \\ + $args \\ + 2> ${prefix}.fastp.log + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastp: \$(fastp --version 2>&1 | sed -e "s/fastp //g") + END_VERSIONS + """ + } else { + def fail_fastq = save_trimmed_fail ? "--unpaired1 ${prefix}_1.fail.fastq.gz --unpaired2 ${prefix}_2.fail.fastq.gz" : '' + def merge_fastq = save_merged ? "-m --merged_out ${prefix}.merged.fastq.gz" : '' + """ + [ ! -f ${prefix}_1.fastq.gz ] && ln -s ${reads[0]} ${prefix}_1.fastq.gz + [ ! -f ${prefix}_2.fastq.gz ] && ln -s ${reads[1]} ${prefix}_2.fastq.gz + fastp \\ + --in1 ${prefix}_1.fastq.gz \\ + --in2 ${prefix}_2.fastq.gz \\ + --out1 ${prefix}_1.trim.fastq.gz \\ + --out2 ${prefix}_2.trim.fastq.gz \\ + --json ${prefix}.fastp.json \\ + --html ${prefix}.fastp.html \\ + $fail_fastq \\ + $merge_fastq \\ + --thread $task.cpus \\ + --detect_adapter_for_pe \\ + $args \\ + 2> ${prefix}.fastp.log + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastp: \$(fastp --version 2>&1 | sed -e "s/fastp //g") + END_VERSIONS + """ + } +} diff --git a/modules/nf-core/modules/fastp/meta.yml b/modules/nf-core/modules/fastp/meta.yml new file mode 100644 index 0000000..3274e41 --- /dev/null +++ b/modules/nf-core/modules/fastp/meta.yml @@ -0,0 +1,68 @@ +name: fastp +description: Perform adapter/quality trimming on sequencing reads +keywords: + - trimming + - quality control + - fastq +tools: + - fastp: + description: | + A tool designed to provide fast all-in-one preprocessing for FastQ files. This tool is developed in C++ with multithreading supported to afford high performance. + documentation: https://github.com/OpenGene/fastp + doi: https://doi.org/10.1093/bioinformatics/bty560 + licence: ["MIT"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + - save_trimmed_fail: + type: boolean + description: Specify true to save files that failed to pass trimming thresholds ending in `*.fail.fastq.gz` + - save_merged: + type: boolean + description: Specify true to save all merged reads to the a file ending in `*.merged.fastq.gz` + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: The trimmed/modified/unmerged fastq reads + pattern: "*trim.fastq.gz" + - json: + type: file + description: Results in JSON format + pattern: "*.json" + - html: + type: file + description: Results in HTML format + pattern: "*.html" + - log: + type: file + description: fastq log file + pattern: "*.log" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - reads_fail: + type: file + description: Reads the failed the preprocessing + pattern: "*fail.fastq.gz" + - reads_merged: + type: file + description: Reads that were successfully merged + pattern: "*.{merged.fastq.gz}" +authors: + - "@drpatelh" + - "@kevinmenden" diff --git a/nextflow.config b/nextflow.config index 186979c..160dba7 100644 --- a/nextflow.config +++ b/nextflow.config @@ -33,7 +33,7 @@ params { help = false validate_params = true show_hidden_params = false - schema_ignore_params = 'genomes' + schema_ignore_params = 'genomes,fasta' enable_conda = false // Config options @@ -50,6 +50,9 @@ params { max_cpus = 16 max_time = '240.h' + // FASTQ preprocessing + fastp_clip_merge = false + fastp_exclude_unmerged = true } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index 535e08d..cc43add 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -56,15 +56,6 @@ "fa_icon": "fas fa-book", "help_text": "If using a reference genome configured in the pipeline using iGenomes, use this parameter to give the ID for the reference. This is then used to build the full paths for all required reference genome files e.g. `--genome GRCh38`. \n\nSee the [nf-core website docs](https://nf-co.re/usage/reference_genomes) for more details." }, - "fasta": { - "type": "string", - "format": "file-path", - "mimetype": "text/plain", - "pattern": "^\\S+\\.fn?a(sta)?(\\.gz)?$", - "description": "Path to FASTA genome file.", - "help_text": "This parameter is *mandatory* if `--genome` is not specified. If you don't have a BWA index available this will be generated for you automatically. Combine with `--save_reference` to save BWA index for future runs.", - "fa_icon": "far fa-file-code" - }, "igenomes_base": { "type": "string", "format": "directory-path", diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index cddcbb3..241a8a7 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -9,22 +9,38 @@ workflow INPUT_CHECK { samplesheet // file: /path/to/samplesheet.csv main: - SAMPLESHEET_CHECK ( samplesheet ) + parsed_samplesheet = SAMPLESHEET_CHECK ( samplesheet ) .csv .splitCsv ( header:true, sep:',' ) + .dump(tag: "split_csv_out") + .branch { + fasta: it['fasta'] != '' + fastq: true + } + + parsed_samplesheet.fastq .map { create_fastq_channels(it) } - .set { reads } + .dump(tag: "fastq_channel_init") + .set { fastq } + + parsed_samplesheet.fasta + .map { create_fasta_channels(it) } + .dump(tag: "fasta_channel_init") + .set { fasta } emit: - reads // channel: [ val(meta), [ reads ] ] + fastq // channel: [ val(meta), [ reads ] ] + fasta // channel: [ val(meta), fasta ] versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] } // Function to get list of [ meta, [ fastq_1, fastq_2 ] ] def create_fastq_channels(LinkedHashMap row) { def meta = [:] - meta.id = row.sample - meta.single_end = row.single_end.toBoolean() + meta.id = row.sample + meta.run_accession = row.run_accession + meta.instrument_platform = row.instrument_platform + meta.single_end = row.single_end.toBoolean() def array = [] if (!file(row.fastq_1).exists()) { @@ -40,3 +56,20 @@ def create_fastq_channels(LinkedHashMap row) { } return array } + +// Function to get list of [ meta, fasta ] +def create_fasta_channels(LinkedHashMap row) { + def meta = [:] + meta.id = row.sample + meta.run_accession = row.run_accession + meta.instrument_platform = row.instrument_platform + meta.single_end = true + + def array = [] + if (!file(row.fasta).exists()) { + exit 1, "ERROR: Please check input samplesheet -> FastA file does not exist!\n${row.fasta}" + } + array = [ meta, [ file(row.fasta) ] ] + + return array +} diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 56c532b..3f10a9d 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -11,7 +11,7 @@ WorkflowTaxprofiler.initialise(params, log) // TODO nf-core: Add all file path parameters for the pipeline to the list below // Check input path parameters to see if they exist -def checkPathParamList = [ params.input, params.multiqc_config, params.fasta ] +def checkPathParamList = [ params.input, params.multiqc_config ] for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } // Check mandatory parameters @@ -50,6 +50,11 @@ include { FASTQC } from '../modules/nf-core/modules/fastqc/ include { MULTIQC } from '../modules/nf-core/modules/multiqc/main' include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' +include { FASTP as FASTP_SINGLE } from '../modules/nf-core/modules/fastp/main' +include { FASTP as FASTP_PAIRED } from '../modules/nf-core/modules/fastp/main' +include { FASTQC as FASTQC_POST } from '../modules/nf-core/modules/fastqc/main' +include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fastq/main' + /* ======================================================================================== RUN MAIN WORKFLOW @@ -75,7 +80,7 @@ workflow TAXPROFILER { // MODULE: Run FastQC // FASTQC ( - INPUT_CHECK.out.reads + INPUT_CHECK.out.fastq ) ch_versions = ch_versions.mix(FASTQC.out.versions.first()) @@ -83,6 +88,71 @@ workflow TAXPROFILER { ch_versions.unique().collectFile(name: 'collated_versions.yml') ) + // + // MODULE: Run Clip/Merge/Complexity + // + // TODO give option to clip only and retain pairs + // TODO give option to retain singletons (probably fastp option likely) + // TODO move to subworkflow + if ( params.fastp_clip_merge ) { + + ch_input_for_fastp = INPUT_CHECK.out.fastq + .dump(tag: "pre-fastp_branch") + .branch{ + single: it[0]['single_end'] == true + paired: it[0]['single_end'] == false + } + + ch_input_for_fastp.single.dump(tag: "input_fastp_single") + ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") + + FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) + FASTP_PAIRED ( ch_input_for_fastp.paired, false, true ) + + ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged + .mix( FASTP_SINGLE.out.reads ) + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] + } + + FASTQC_POST ( ch_fastp_reads_prepped ) + + ch_versions = ch_versions.mix(FASTP_SINGLE.out.versions.first()) + ch_versions = ch_versions.mix(FASTP_PAIRED.out.versions.first()) + + ch_processed_reads = ch_fastp_reads_prepped + + } else { + ch_processed_reads = INPUT_CHECK.out.fastq + } + + + // MODULE: Cat merge runs of same sample + ch_processed_for_combine = ch_processed_reads + .dump(tag: "prep_for_combine_grouping") + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['run_accession'] = 'combined' + [ meta_new, reads ] + } + .groupTuple ( by: 0 ) + .branch{ + combine: it[1].size() >= 2 + skip: it[1].size() < 2 + } + + CAT_FASTQ ( ch_processed_for_combine.combine ) + + // Ready for profiling! + ch_reads_for_profiling = ch_processed_for_combine.skip + .dump(tag: "skip_combine") + .mix( CAT_FASTQ.out.reads ) + .dump(tag: "files_for_profiling") + // // MODULE: MultiQC // @@ -95,6 +165,12 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) + if (params.fastp_clip_merge) { + ch_multiqc_files = ch_multiqc_files.mix(FASTP_SINGLE.out.json.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(FASTP_PAIRED.out.json.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(FASTQC_POST.out.zip.collect{it[1]}.ifEmpty([])) + } + MULTIQC ( ch_multiqc_files.collect() From 278f5605ca831338384b5045e34f500f0b5ca1a2 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 19 Feb 2022 12:36:08 +0100 Subject: [PATCH 008/306] Added database preparation and final channel for profiling --- modules/local/database_check.nf | 25 ++++++++++ nextflow.config | 3 ++ subworkflows/local/db_check.nf | 40 ++++++++++++++++ subworkflows/local/input_check.nf | 2 +- subworkflows/local/preprocessing.nf | 73 +++++++++++++++++++++++++++++ workflows/taxprofiler.nf | 65 ++++++++----------------- 6 files changed, 161 insertions(+), 47 deletions(-) create mode 100644 modules/local/database_check.nf create mode 100644 subworkflows/local/db_check.nf create mode 100644 subworkflows/local/preprocessing.nf diff --git a/modules/local/database_check.nf b/modules/local/database_check.nf new file mode 100644 index 0000000..4da4313 --- /dev/null +++ b/modules/local/database_check.nf @@ -0,0 +1,25 @@ +process DATABASE_CHECK { + tag "$databasesheet" + + conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) + 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' }" + + input: + path databasesheet + + output: + path '*.csv' , emit: csv + path "versions.yml", emit: versions + + script: // This script is bundled with the pipeline, in nf-core/taxprofiler/bin/ + """ + cat $databasesheet >> database_sheet.valid.csv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS + """ +} diff --git a/nextflow.config b/nextflow.config index 160dba7..60b5a42 100644 --- a/nextflow.config +++ b/nextflow.config @@ -50,6 +50,9 @@ params { max_cpus = 16 max_time = '240.h' + // Databaess + databases = null + // FASTQ preprocessing fastp_clip_merge = false fastp_exclude_unmerged = true diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf new file mode 100644 index 0000000..909d98f --- /dev/null +++ b/subworkflows/local/db_check.nf @@ -0,0 +1,40 @@ +// +// Check input samplesheet and get read channels +// + +include { DATABASE_CHECK } from '../../modules/local/database_check' + +workflow DB_CHECK { + take: + dbsheet // file: /path/to/dbsheet.csv + + main: + + // TODO: make database sheet check + parsed_samplesheet = DATABASE_CHECK ( dbsheet ) + .csv + .splitCsv ( header:true, sep:',' ) + .dump(tag: "db_split_csv_out") + .map { create_db_channels(it) } + .dump(tag: "db_channel_prepped") + .set{ dbs } + + emit: + dbs // channel: [ val(meta), [ db ] ] + versions = DATABASE_CHECK.out.versions // channel: [ versions.yml ] +} + +def create_db_channels(LinkedHashMap row) { + def meta = [:] + meta.tool = row.tool + meta.db_name = row.db_name + meta.db_params = row.db_params + + def array = [] + if (!file(row.db_path, type: 'dir').exists()) { + exit 1, "ERROR: Please check input samplesheet -> database could not be found!\n${row.db_path}" + } + array = [ meta, file(row.db_path) ] + + return array +} diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 241a8a7..8497faa 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -12,7 +12,7 @@ workflow INPUT_CHECK { parsed_samplesheet = SAMPLESHEET_CHECK ( samplesheet ) .csv .splitCsv ( header:true, sep:',' ) - .dump(tag: "split_csv_out") + .dump(tag: "input_split_csv_out") .branch { fasta: it['fasta'] != '' fastq: true diff --git a/subworkflows/local/preprocessing.nf b/subworkflows/local/preprocessing.nf new file mode 100644 index 0000000..5832824 --- /dev/null +++ b/subworkflows/local/preprocessing.nf @@ -0,0 +1,73 @@ +// +// Check input samplesheet and get read channels +// + + +include { FASTP as FASTP_SINGLE } from '../../modules/nf-core/modules/fastp/main' +include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fastp/main' +include { FASTQC as FASTQC_POST } from '../../modules/nf-core/modules/fastqc/main' + +workflow FASTQ_PREPROCESSING { + take: + reads // file: /path/to/samplesheet.csv + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + // + // STEP: Read clipping and merging + // + // TODO give option to clip only and retain pairs + // TODO give option to retain singletons (probably fastp option likely) + // TODO move to subworkflow + + + if ( params.fastp_clip_merge ) { + + ch_input_for_fastp = reads + .dump(tag: "pre-fastp_branch") + .branch{ + single: it[0]['single_end'] == true + paired: it[0]['single_end'] == false + } + + ch_input_for_fastp.single.dump(tag: "input_fastp_single") + ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") + + FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) + FASTP_PAIRED ( ch_input_for_fastp.paired, false, true ) + + ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged + .mix( FASTP_SINGLE.out.reads ) + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] + } + + FASTQC_POST ( ch_fastp_reads_prepped ) + + ch_versions = ch_versions.mix(FASTP_SINGLE.out.versions.first()) + ch_versions = ch_versions.mix(FASTP_PAIRED.out.versions.first()) + + ch_processed_reads = ch_fastp_reads_prepped + + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_POST.out.zip.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( FASTP_SINGLE.out.json.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( FASTP_PAIRED.out.json.collect{it[1]} ) + + ch_multiqc_files.dump(tag: "preprocessing_mqc_final") + + } else { + ch_processed_reads = reads + } + + + emit: + reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} + diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 3f10a9d..4a356a5 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -11,11 +11,12 @@ WorkflowTaxprofiler.initialise(params, log) // TODO nf-core: Add all file path parameters for the pipeline to the list below // Check input path parameters to see if they exist -def checkPathParamList = [ params.input, params.multiqc_config ] +def checkPathParamList = [ params.input, params.databases, params.multiqc_config ] 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 samplesheet not specified!' } +if (params.input ) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet not specified!' } +if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } /* ======================================================================================== @@ -35,7 +36,11 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { INPUT_CHECK } from '../subworkflows/local/input_check' + +include { DB_CHECK } from '../subworkflows/local/db_check' +include { FASTQ_PREPROCESSING } from '../subworkflows/local/preprocessing' + /* ======================================================================================== @@ -50,9 +55,6 @@ include { FASTQC } from '../modules/nf-core/modules/fastqc/ include { MULTIQC } from '../modules/nf-core/modules/multiqc/main' include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' -include { FASTP as FASTP_SINGLE } from '../modules/nf-core/modules/fastp/main' -include { FASTP as FASTP_PAIRED } from '../modules/nf-core/modules/fastp/main' -include { FASTQC as FASTQC_POST } from '../modules/nf-core/modules/fastqc/main' include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fastq/main' /* @@ -76,6 +78,10 @@ workflow TAXPROFILER { ) ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) + DB_CHECK ( + ch_databases + ) + // // MODULE: Run FastQC // @@ -91,47 +97,12 @@ workflow TAXPROFILER { // // MODULE: Run Clip/Merge/Complexity // - // TODO give option to clip only and retain pairs - // TODO give option to retain singletons (probably fastp option likely) - // TODO move to subworkflow if ( params.fastp_clip_merge ) { - - ch_input_for_fastp = INPUT_CHECK.out.fastq - .dump(tag: "pre-fastp_branch") - .branch{ - single: it[0]['single_end'] == true - paired: it[0]['single_end'] == false - } - - ch_input_for_fastp.single.dump(tag: "input_fastp_single") - ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") - - FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) - FASTP_PAIRED ( ch_input_for_fastp.paired, false, true ) - - ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged - .mix( FASTP_SINGLE.out.reads ) - .map { - meta, reads -> - def meta_new = meta.clone() - meta_new['single_end'] = 1 - [ meta_new, reads ] - } - - FASTQC_POST ( ch_fastp_reads_prepped ) - - ch_versions = ch_versions.mix(FASTP_SINGLE.out.versions.first()) - ch_versions = ch_versions.mix(FASTP_PAIRED.out.versions.first()) - - ch_processed_reads = ch_fastp_reads_prepped - - } else { - ch_processed_reads = INPUT_CHECK.out.fastq + FASTQ_PREPROCESSING ( INPUT_CHECK.out.fastq ) } - // MODULE: Cat merge runs of same sample - ch_processed_for_combine = ch_processed_reads + ch_processed_for_combine = FASTQ_PREPROCESSING.out.reads .dump(tag: "prep_for_combine_grouping") .map { meta, reads -> @@ -153,6 +124,10 @@ workflow TAXPROFILER { .mix( CAT_FASTQ.out.reads ) .dump(tag: "files_for_profiling") + // Combine reads with possible databases + + ch_reads_for_profiling.combine(DB_CHECK.out.dbs).dump(tag: "reads_plus_db") + // // MODULE: MultiQC // @@ -166,9 +141,7 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) if (params.fastp_clip_merge) { - ch_multiqc_files = ch_multiqc_files.mix(FASTP_SINGLE.out.json.collect{it[1]}.ifEmpty([])) - ch_multiqc_files = ch_multiqc_files.mix(FASTP_PAIRED.out.json.collect{it[1]}.ifEmpty([])) - ch_multiqc_files = ch_multiqc_files.mix(FASTQC_POST.out.zip.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(FASTQ_PREPROCESSING.out.mqc) } From 2c183ed2edc61ab058580cb63441917d516eb564 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 3 Mar 2022 17:42:02 +0100 Subject: [PATCH 009/306] Add Kraken2 and MALT/run as Proof of Concept (currnetly MQC issue) --- conf/modules.config | 20 ++++++ modules.json | 6 ++ .../nf-core/modules/kraken2/kraken2/main.nf | 49 +++++++++++++ .../nf-core/modules/kraken2/kraken2/meta.yml | 60 ++++++++++++++++ modules/nf-core/modules/malt/run/main.nf | 49 +++++++++++++ modules/nf-core/modules/malt/run/meta.yml | 58 ++++++++++++++++ nextflow.config | 11 ++- workflows/taxprofiler.nf | 69 +++++++++++++++++-- 8 files changed, 315 insertions(+), 7 deletions(-) create mode 100644 modules/nf-core/modules/kraken2/kraken2/main.nf create mode 100644 modules/nf-core/modules/kraken2/kraken2/meta.yml create mode 100644 modules/nf-core/modules/malt/run/main.nf create mode 100644 modules/nf-core/modules/malt/run/meta.yml diff --git a/conf/modules.config b/conf/modules.config index 2d533e9..dbc926c 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -68,6 +68,26 @@ process { ] } + withName: MALT_RUN { + publishDir = [ + path: { "${params.outdir}/malt/${meta.db_name}" }, + mode: 'copy', + pattern: '*.{rma6,tab,text,sam,log}' + ] + ext.args = { "${meta.db_params}" } + ext.when = params.run_malt + } + + withName: KRAKEN2_KRAKEN2 { + publishDir = [ + path: { "${params.outdir}/kraken2/${meta.db_name}" }, + mode: 'copy', + pattern: '.{fastq.gz,txt}' + ] + ext.args = { "${meta.db_params}" } + ext.when = params.run_kraken2 + ext.prefix = { "${meta.id}-${meta.db_name}" } + } withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ diff --git a/modules.json b/modules.json index 6cf2b3e..844b33a 100644 --- a/modules.json +++ b/modules.json @@ -15,6 +15,12 @@ "fastqc": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, + "kraken2/kraken2": { + "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + }, + "malt/run": { + "git_sha": "76cdd46f3f8a77fb5023fb5a39c4ab99925b8b56" + }, "multiqc": { "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" } diff --git a/modules/nf-core/modules/kraken2/kraken2/main.nf b/modules/nf-core/modules/kraken2/kraken2/main.nf new file mode 100644 index 0000000..3ec5df5 --- /dev/null +++ b/modules/nf-core/modules/kraken2/kraken2/main.nf @@ -0,0 +1,49 @@ +process KRAKEN2_KRAKEN2 { + tag "$meta.id" + label 'process_high' + + conda (params.enable_conda ? 'bioconda::kraken2=2.1.2 conda-forge::pigz=2.6' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0' : + 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0' }" + + input: + tuple val(meta), path(reads) + path db + + output: + tuple val(meta), path('*classified*') , emit: classified + tuple val(meta), path('*unclassified*'), emit: unclassified + tuple val(meta), path('*report.txt') , emit: txt + 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}" + def paired = meta.single_end ? "" : "--paired" + def classified = meta.single_end ? "${prefix}.classified.fastq" : "${prefix}.classified#.fastq" + def unclassified = meta.single_end ? "${prefix}.unclassified.fastq" : "${prefix}.unclassified#.fastq" + """ + kraken2 \\ + --db $db \\ + --threads $task.cpus \\ + --unclassified-out $unclassified \\ + --classified-out $classified \\ + --report ${prefix}.kraken2.report.txt \\ + --gzip-compressed \\ + $paired \\ + $args \\ + $reads + + pigz -p $task.cpus *.fastq + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + kraken2: \$(echo \$(kraken2 --version 2>&1) | sed 's/^.*Kraken version //; s/ .*\$//') + pigz: \$( pigz --version 2>&1 | sed 's/pigz //g' ) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/kraken2/kraken2/meta.yml b/modules/nf-core/modules/kraken2/kraken2/meta.yml new file mode 100644 index 0000000..9d6a385 --- /dev/null +++ b/modules/nf-core/modules/kraken2/kraken2/meta.yml @@ -0,0 +1,60 @@ +name: kraken2_kraken2 +description: Classifies metagenomic sequence data +keywords: + - classify + - metagenomics + - fastq + - db +tools: + - kraken2: + description: | + Kraken2 is a taxonomic sequence classifier that assigns taxonomic labels to sequence reads + homepage: https://ccb.jhu.edu/software/kraken2/ + documentation: https://github.com/DerrickWood/kraken2/wiki/Manual + doi: 10.1186/s13059-019-1891-0 + licence: ["MIT"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + - db: + type: directory + description: Kraken2 database +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - classified: + type: file + description: | + Reads classified to belong to any of the taxa + on the Kraken2 database. + pattern: "*{fastq.gz}" + - unclassified: + type: file + description: | + Reads not classified to belong to any of the taxa + on the Kraken2 database. + pattern: "*{fastq.gz}" + - txt: + type: file + description: | + Kraken2 report containing stats about classified + and not classifed reads. + pattern: "*.{report.txt}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/modules/malt/run/main.nf b/modules/nf-core/modules/malt/run/main.nf new file mode 100644 index 0000000..61c02ec --- /dev/null +++ b/modules/nf-core/modules/malt/run/main.nf @@ -0,0 +1,49 @@ +process MALT_RUN { + tag "$meta.id" + label 'process_high' + + conda (params.enable_conda ? "bioconda::malt=0.53" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/malt:0.53--hdfd78af_0' : + 'quay.io/biocontainers/malt:0.53--hdfd78af_0' }" + + input: + tuple val(meta), path(fastqs) + val mode + path index + + output: + tuple val(meta), path("*.rma6") , emit: rma6 + tuple val(meta), path("*.{tab,text,sam}"), optional:true, emit: alignments + tuple val(meta), path("*.log") , emit: log + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def avail_mem = 6 + if (!task.memory) { + log.info '[MALT_RUN] Available memory not known - defaulting to 6GB. Specify process memory requirements to change this.' + } else { + avail_mem = task.memory.giga + } + + """ + malt-run \\ + -J-Xmx${avail_mem}g \\ + -t $task.cpus \\ + -v \\ + -o . \\ + $args \\ + --inFile ${fastqs.join(' ')} \\ + -m $mode \\ + --index $index/ |&tee malt-run.log + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + malt: \$(malt-run --help 2>&1 | grep -o 'version.* ' | cut -f 1 -d ',' | cut -f2 -d ' ') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/malt/run/meta.yml b/modules/nf-core/modules/malt/run/meta.yml new file mode 100644 index 0000000..ae4277a --- /dev/null +++ b/modules/nf-core/modules/malt/run/meta.yml @@ -0,0 +1,58 @@ +name: malt_run +description: MALT, an acronym for MEGAN alignment tool, is a sequence alignment and analysis tool designed for processing high-throughput sequencing data, especially in the context of metagenomics. +keywords: + - malt + - alignment + - metagenomics + - ancient DNA + - aDNA + - palaeogenomics + - archaeogenomics + - microbiome +tools: + - malt: + description: A tool for mapping metagenomic data + homepage: https://www.wsi.uni-tuebingen.de/lehrstuehle/algorithms-in-bioinformatics/software/malt/ + documentation: https://software-ab.informatik.uni-tuebingen.de/download/malt/manual.pdf + tool_dev_url: None + doi: "10.1038/s41559-017-0446-6" + licence: ["GPL v3"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - fastqs: + type: file + description: Input FASTQ files + pattern: "*.{fastq.gz,fq.gz}" + - mode: + type: string + description: Program mode + pattern: "Unknown|BlastN|BlastP|BlastX|Classifier" + - index: + type: directory + description: Index/database directory from malt-build + pattern: "*/" +output: + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - rma6: + type: file + description: MEGAN6 RMA6 file + pattern: "*.rma6" + - sam: + type: file + description: Alignment files in Tab, Text or MEGAN-compatible SAM format + pattern: "*.{tab,txt,sam}" + - log: + type: file + description: Log of verbose MALT stdout + pattern: "malt-run.log" + +authors: + - "@jfy133" diff --git a/nextflow.config b/nextflow.config index 60b5a42..7991bdf 100644 --- a/nextflow.config +++ b/nextflow.config @@ -54,8 +54,15 @@ params { databases = null // FASTQ preprocessing - fastp_clip_merge = false - fastp_exclude_unmerged = true + fastp_clip_merge = false + fastp_exclude_unmerged = true + + // MALT + run_malt = false + malt_mode = 'BlastN' + + // kraken2 + run_kraken2 = false } // Load base.config by default for all pipelines diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 4a356a5..bd25563 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -56,6 +56,9 @@ include { MULTIQC } from '../modules/nf-core/modules/multiqc include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fastq/main' +include { MALT_RUN } from '../modules/nf-core/modules/malt/run/main' +include { KRAKEN2_KRAKEN2 } from '../modules/nf-core/modules/kraken2/kraken2/main' + /* ======================================================================================== @@ -95,13 +98,15 @@ workflow TAXPROFILER { ) // - // MODULE: Run Clip/Merge/Complexity + // PERFORM PREPROCESSING // if ( params.fastp_clip_merge ) { FASTQ_PREPROCESSING ( INPUT_CHECK.out.fastq ) } - // MODULE: Cat merge runs of same sample + // + // PERFORM RUN MERGING + // ch_processed_for_combine = FASTQ_PREPROCESSING.out.reads .dump(tag: "prep_for_combine_grouping") .map { @@ -118,15 +123,61 @@ workflow TAXPROFILER { CAT_FASTQ ( ch_processed_for_combine.combine ) - // Ready for profiling! ch_reads_for_profiling = ch_processed_for_combine.skip .dump(tag: "skip_combine") .mix( CAT_FASTQ.out.reads ) .dump(tag: "files_for_profiling") - // Combine reads with possible databases + // + // COMBINE READS WITH POSSIBLE DATABASES + // + + // output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] + ch_input_for_profiling = ch_reads_for_profiling + .combine(DB_CHECK.out.dbs) + .dump(tag: "reads_plus_db") + .branch { + malt: it[2]['tool'] == 'malt' + kraken2: it[2]['tool'] == 'kraken2' + unknown: true + } + + // + // PREP PROFILER INPUT CHANNELS ON PER TOOL BASIS + // + + // We groupTuple to have all samples in one channel for MALT as database + // loading takes a long time, so we only want to run it once per database + ch_input_for_malt = ch_input_for_profiling.malt + .map { + it -> + def temp_meta = [ id: it[2]['db_name']] + it[2] + def db = it[3] + [ temp_meta, it[1], db ] + } + .groupTuple(by: [0,2]) + .dump(tag: "input for malt") + .multiMap { + it -> + reads: [ it[0], it[1].flatten() ] + db: it[2] + } + + // We can run Kraken2 one-by-one sample-wise + ch_input_for_kraken2 = ch_input_for_profiling.kraken2 + .dump(tag: "input for kraken") + .multiMap { + it -> + reads: [ it[0] + it[2], it[1] ] + db: it[3] + } + + // + // RUN PROFILING + // + MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) + KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) - ch_reads_for_profiling.combine(DB_CHECK.out.dbs).dump(tag: "reads_plus_db") // // MODULE: MultiQC @@ -143,6 +194,14 @@ workflow TAXPROFILER { if (params.fastp_clip_merge) { ch_multiqc_files = ch_multiqc_files.mix(FASTQ_PREPROCESSING.out.mqc) } + if (params.run_kraken2) { + ch_multiqc_files = ch_multiqc_files.mix(KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([])) + ch_versions = ch_versions.mix(KRAKEN2_KRAKEN2.out.versions.first()) + } + if (params.run_malt) { + ch_multiqc_files = ch_multiqc_files.mix(MALT_RUN.out.log.collect{it[1]}.ifEmpty([])) + ch_versions = ch_versions.mix(MALT_RUN.out.versions.first()) + } MULTIQC ( From e39c6a8ccb337b5889271df4f1e57bcbd3dd2c74 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 3 Mar 2022 18:04:03 +0100 Subject: [PATCH 010/306] Fix MALT multiqc report clash --- modules.json | 2 +- modules/nf-core/modules/malt/run/main.nf | 3 ++- modules/nf-core/modules/malt/run/meta.yml | 2 +- workflows/taxprofiler.nf | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules.json b/modules.json index 844b33a..6a785b8 100644 --- a/modules.json +++ b/modules.json @@ -19,7 +19,7 @@ "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, "malt/run": { - "git_sha": "76cdd46f3f8a77fb5023fb5a39c4ab99925b8b56" + "git_sha": "72b96f4e504eef673f2b5c13560a9d90b669129b" }, "multiqc": { "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" diff --git a/modules/nf-core/modules/malt/run/main.nf b/modules/nf-core/modules/malt/run/main.nf index 61c02ec..4e2e50c 100644 --- a/modules/nf-core/modules/malt/run/main.nf +++ b/modules/nf-core/modules/malt/run/main.nf @@ -23,6 +23,7 @@ process MALT_RUN { script: def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" def avail_mem = 6 if (!task.memory) { log.info '[MALT_RUN] Available memory not known - defaulting to 6GB. Specify process memory requirements to change this.' @@ -39,7 +40,7 @@ process MALT_RUN { $args \\ --inFile ${fastqs.join(' ')} \\ -m $mode \\ - --index $index/ |&tee malt-run.log + --index $index/ |&tee ${prefix}-malt-run.log cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/malt/run/meta.yml b/modules/nf-core/modules/malt/run/meta.yml index ae4277a..66f2d7a 100644 --- a/modules/nf-core/modules/malt/run/meta.yml +++ b/modules/nf-core/modules/malt/run/meta.yml @@ -52,7 +52,7 @@ output: - log: type: file description: Log of verbose MALT stdout - pattern: "malt-run.log" + pattern: "*-malt-run.log" authors: - "@jfy133" diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index bd25563..bf3e6ee 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -203,7 +203,8 @@ workflow TAXPROFILER { ch_versions = ch_versions.mix(MALT_RUN.out.versions.first()) } - + // TODO MALT results overwriting per database? + // TODO Versions for Karken/MALT not report? MULTIQC ( ch_multiqc_files.collect() ) From 424f11f5ed0898a0e0524a7386b35164f58beccb Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Thu, 17 Mar 2022 13:16:16 +0100 Subject: [PATCH 011/306] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 60baf67..5d0c74b 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,10 @@ On release, automated continuous integration tests run the pipeline on a full-si 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. - > * 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. + > - 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! From 0f0ed6cd4698df3bea9d4168c056085a820eb056 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 10:45:06 +0100 Subject: [PATCH 012/306] Fix function name --- subworkflows/local/input_check.nf | 1 + 1 file changed, 1 insertion(+) diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index d66fb3a..481028f 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -15,6 +15,7 @@ workflow INPUT_CHECK { .dump(tag: "input_split_csv_out") .branch { fasta: it['fasta'] != '' + nanopore: it['instrument_platform'] == 'OXFORD_NANOPORE' fastq: true } From 41b3d8db822caab916ec82fe4b4f581f17ab1ca5 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 10:47:41 +0100 Subject: [PATCH 013/306] Add nanopore channel --- subworkflows/local/input_check.nf | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 481028f..2e30bcc 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -20,10 +20,15 @@ workflow INPUT_CHECK { } parsed_samplesheet.fastq - .map { create_fastq_channels(it) } + .map { create_fastq_channel(it) } .dump(tag: "fastq_channel_init") .set { fastq } + parsed_samplesheet.nanopore + .map { create_fastq_channel(it) } + .dump(tag: "fastq_nanopore_channel_init") + .set { nanopore } + parsed_samplesheet.fasta .map { create_fasta_channels(it) } .dump(tag: "fasta_channel_init") @@ -31,6 +36,7 @@ workflow INPUT_CHECK { emit: fastq // channel: [ val(meta), [ reads ] ] + nanopore // channel: [ val(meta), [ reads ] ] fasta // channel: [ val(meta), fasta ] versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] } @@ -52,10 +58,17 @@ def create_fastq_channel(LinkedHashMap row) { if (meta.single_end) { fastq_meta = [ meta, [ file(row.fastq_1) ] ] } else { - if (!file(row.fastq_2).exists()) { - exit 1, "ERROR: Please check input samplesheet -> Read 2 FastQ file does not exist!\n${row.fastq_2}" + if (meta.instrument_platform == 'OXFORD_NANOPORE') { + if (row.fastq_2 != '') { + exit 1, "ERROR: Please check input samplesheet -> For Oxford Nanopore reads Read 2 FastQ should be empty!\n${row.fastq_2}" + } + fastq_meta = [ meta, [ file(row.fastq_1) ] ] + } else { + if (!file(row.fastq_2).exists()) { + exit 1, "ERROR: Please check input samplesheet -> Read 2 FastQ file does not exist!\n${row.fastq_2}" + } + fastq_meta = [ meta, [ file(row.fastq_1), file(row.fastq_2) ] ] } - fastq_meta = [ meta, [ file(row.fastq_1), file(row.fastq_2) ] ] } return fastq_meta } From 7f7ddc9f14237f1616918963a002cbc64fea2687 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 10:48:06 +0100 Subject: [PATCH 014/306] Update comment --- bin/check_samplesheet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 16e668b..d10ee90 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -173,7 +173,7 @@ def check_samplesheet(file_in, file_out): ## Auto-detect paired-end/single-end if sample and fastq_1 and fastq_2: ## Paired-end short reads sample_info.extend(["0", fastq_1, fastq_2, fasta]) - elif sample and fastq_1 and not fastq_2: ## Single-end short reads + elif sample and fastq_1 and not fastq_2: ## Single-end short/long fastq reads sample_info.extend(["1", fastq_1, fastq_2, fasta]) elif ( sample and fasta and not fastq_1 and not fastq_2 From 2e1b6c5d0a3b7c455bba5cb4d20dbbceac43dffe Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 11:00:36 +0100 Subject: [PATCH 015/306] Add info on Nanopore reads to fastq_1 column --- docs/usage.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index a8b0448..38c063e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -44,11 +44,11 @@ TREATMENT_REP3,AEG588A6_S6_L003_R1_001.fastq.gz, TREATMENT_REP3,AEG588A6_S6_L004_R1_001.fastq.gz, ``` -| Column | Description | -|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | -| `fastq_1` | Full path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | -| `fastq_2` | Full path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| Column | Description | +| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | +| `fastq_1` | Full path to FastQ file for Illumina short reads 1 or Nanopore reads. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| `fastq_2` | Full path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. From c8e49c56f4f6b26dde209acfd474fc5c4f43caf7 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 13:47:44 +0100 Subject: [PATCH 016/306] Perform fastqc on nanopore reads before trimming --- workflows/taxprofiler.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index f740324..c3d3eb6 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -89,7 +89,7 @@ workflow TAXPROFILER { // MODULE: Run FastQC // FASTQC ( - INPUT_CHECK.out.fastq + INPUT_CHECK.out.fastq.concat( INPUT_CHECK.out.nanopore ) ) ch_versions = ch_versions.mix(FASTQC.out.versions.first()) From 0936b9b28e52986576d9bb62473373f209565d6d Mon Sep 17 00:00:00 2001 From: Lauri Mesilaakso Date: Fri, 18 Mar 2022 14:18:38 +0100 Subject: [PATCH 017/306] Update workflows/taxprofiler.nf Co-authored-by: James A. Fellows Yates --- workflows/taxprofiler.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index c3d3eb6..f48cff6 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -89,7 +89,7 @@ workflow TAXPROFILER { // MODULE: Run FastQC // FASTQC ( - INPUT_CHECK.out.fastq.concat( INPUT_CHECK.out.nanopore ) + INPUT_CHECK.out.fastq.mix( INPUT_CHECK.out.nanopore ) ) ch_versions = ch_versions.mix(FASTQC.out.versions.first()) From 16be676d72f9964038a52fb0ddc58c9bdafd4f9b Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 14:34:10 +0100 Subject: [PATCH 018/306] Add Porechop module --- conf/modules.config | 9 ++++ modules.json | 3 ++ modules/nf-core/modules/porechop/main.nf | 35 ++++++++++++++++ modules/nf-core/modules/porechop/meta.yml | 50 +++++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 modules/nf-core/modules/porechop/main.nf create mode 100644 modules/nf-core/modules/porechop/meta.yml diff --git a/conf/modules.config b/conf/modules.config index 9e334bc..050772e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -50,6 +50,15 @@ process { ] } + withName: PORECHOP { + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/porechop" }, + mode: 'copy', + pattern: '*.fastq.gz' + ] + } + withName: FASTQC_POST { ext.args = '--quiet' ext.prefix = { "${meta.id}_${meta.run_accession}_processed" } diff --git a/modules.json b/modules.json index 6a785b8..284cf13 100644 --- a/modules.json +++ b/modules.json @@ -23,6 +23,9 @@ }, "multiqc": { "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + }, + "porechop": { + "git_sha": "e20e57f90b6787ac9a010a980cf6ea98bd990046" } } } diff --git a/modules/nf-core/modules/porechop/main.nf b/modules/nf-core/modules/porechop/main.nf new file mode 100644 index 0000000..65982b8 --- /dev/null +++ b/modules/nf-core/modules/porechop/main.nf @@ -0,0 +1,35 @@ +process PORECHOP { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? "bioconda::porechop=0.2.4" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/porechop:0.2.4--py39h7cff6ad_2' : + 'quay.io/biocontainers/porechop:0.2.4--py39h7cff6ad_2' }" + + input: + tuple val(meta), path(reads) + + output: + tuple val(meta), path("*.fastq.gz"), emit: reads + 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}" + """ + porechop \\ + -i $reads \\ + -t $task.cpus \\ + $args \\ + -o ${prefix}.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + porechop: \$( porechop --version ) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/porechop/meta.yml b/modules/nf-core/modules/porechop/meta.yml new file mode 100644 index 0000000..81399d2 --- /dev/null +++ b/modules/nf-core/modules/porechop/meta.yml @@ -0,0 +1,50 @@ +name: porechop +description: Adapter removal and demultiplexing of Oxford Nanopore reads +keywords: + - adapter + - nanopore + - demultiplexing +tools: + - porechop: + description: Adapter removal and demultiplexing of Oxford Nanopore reads + homepage: "https://github.com/rrwick/Porechop" + documentation: "https://github.com/rrwick/Porechop" + tool_dev_url: "https://github.com/rrwick/Porechop" + doi: "10.1099/mgen.0.000132" + licence: ["GPL v3"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: fastq/fastq.gz file + pattern: "*.{fastq,fastq.gz,fq,fq.gz}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - reads: + type: file + description: Demultiplexed and/or adapter-trimmed fastq.gz file + pattern: "*.{fastq.gz}" + +authors: + - "@ggabernet" + - "@jasmezz" + - "@d4straub" + - "@LaurenceKuhl" + - "@SusiJo" + - "@jonasscheid" + - "@jonoave" + - "@GokceOGUZ" From 1e42f1d9f295e909cc75d10b6306cce2d1f4bf22 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 15:10:44 +0100 Subject: [PATCH 019/306] Add long read preprocessing subworkflow --- subworkflows/local/longread_preprocessing.nf | 34 ++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 subworkflows/local/longread_preprocessing.nf diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf new file mode 100644 index 0000000..da1049a --- /dev/null +++ b/subworkflows/local/longread_preprocessing.nf @@ -0,0 +1,34 @@ + +include { FASTQC as FASTQC_POST } from '../../modules/nf-core/modules/fastqc/main' +include { PORECHOP } from '../../modules/nf-core/modules/porechop/main' + +workflow LONGREAD_PREPROCESSING { + take: + reads + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + PORECHOP ( reads ) + + ch_processed_reads = PORECHOP.out.reads + .dump(tag: "pre_fastqc_check") + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] + } + + FASTQC_POST ( PORECHOP.out.reads ) + ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_POST.out.zip.collect{it[1]} ) + + + emit: + reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} + From 582aaa105fff0328f8df314d88ab84fe6c8e528b Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 15:12:07 +0100 Subject: [PATCH 020/306] Include long reads preprocessing subworkflow --- workflows/taxprofiler.nf | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index f48cff6..9e52b59 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -40,7 +40,7 @@ include { INPUT_CHECK } from '../subworkflows/local/input_check' include { DB_CHECK } from '../subworkflows/local/db_check' include { FASTQ_PREPROCESSING } from '../subworkflows/local/preprocessing' - +include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -104,6 +104,10 @@ workflow TAXPROFILER { FASTQ_PREPROCESSING ( INPUT_CHECK.out.fastq ) } + LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ) + + ch_versions = ch_versions.mix(LONGREAD_PREPROCESSING.out.versions.first()) + // // PERFORM RUN MERGING // @@ -191,6 +195,7 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(LONGREAD_PREPROCESSING.out.mqc) if (params.fastp_clip_merge) { ch_multiqc_files = ch_multiqc_files.mix(FASTQ_PREPROCESSING.out.mqc) } From d09a3c170edac3afb7e6932cbcca824d6b77e202 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 15:46:03 +0100 Subject: [PATCH 021/306] Add Porechop --- CITATIONS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CITATIONS.md b/CITATIONS.md index 192b2f4..53c53c3 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -15,6 +15,8 @@ * [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. +* [Porechop](https://github.com/rrwick/Porechop) + ## Software packaging/containerisation tools * [Anaconda](https://anaconda.com) From 24a01529f5053c51a0f548ad04790f9bd5e3df9d Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Fri, 18 Mar 2022 15:47:46 +0100 Subject: [PATCH 022/306] Add mentioning about Nanopore reads pre-processing --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5d0c74b..622b8de 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ On release, automated continuous integration tests run the pipeline on a full-si - Low complexity filtering - Host read removal - Run merging + - Adapter and quality trimming of Nanopore reads 3. Performs taxonomic profiling a choice of: - Kraken2 - MetaPhlAn3 From c7f022008c561e70e2ed4a875c17ea1ece109f43 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 21 Mar 2022 15:07:59 +0100 Subject: [PATCH 023/306] Start getting database prep ready --- modules.json | 3 +++ modules/nf-core/modules/untar/main.nf | 36 ++++++++++++++++++++++++++ modules/nf-core/modules/untar/meta.yml | 28 ++++++++++++++++++++ subworkflows/local/db_check.nf | 14 +++++++++- subworkflows/local/input_check.nf | 2 +- 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 modules/nf-core/modules/untar/main.nf create mode 100644 modules/nf-core/modules/untar/meta.yml diff --git a/modules.json b/modules.json index 6a785b8..9a7f1df 100644 --- a/modules.json +++ b/modules.json @@ -23,6 +23,9 @@ }, "multiqc": { "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + }, + "untar": { + "git_sha": "7ec09d0ef4df89617baacc9b2dafcddb7cd4b05a" } } } diff --git a/modules/nf-core/modules/untar/main.nf b/modules/nf-core/modules/untar/main.nf new file mode 100644 index 0000000..bbae948 --- /dev/null +++ b/modules/nf-core/modules/untar/main.nf @@ -0,0 +1,36 @@ +process UNTAR { + tag "$archive" + label 'process_low' + + conda (params.enable_conda ? "conda-forge::tar=1.32" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + 'biocontainers/biocontainers:v1.2.0_cv1' }" + + input: + path archive + + output: + path "$untar" , emit: untar + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + untar = archive.toString() - '.tar.gz' + """ + tar \\ + -xzvf \\ + $args \\ + $archive \\ + $args2 \\ + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + untar: \$(echo \$(tar --version 2>&1) | sed 's/^.*(GNU tar) //; s/ Copyright.*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/untar/meta.yml b/modules/nf-core/modules/untar/meta.yml new file mode 100644 index 0000000..e877a97 --- /dev/null +++ b/modules/nf-core/modules/untar/meta.yml @@ -0,0 +1,28 @@ +name: untar +description: Extract files. +keywords: + - untar + - uncompress +tools: + - untar: + description: | + Extract tar.gz files. + documentation: https://www.gnu.org/software/tar/manual/ + licence: ["GPL-3.0-or-later"] +input: + - archive: + type: file + description: File to be untar + pattern: "*.{tar}.{gz}" +output: + - untar: + type: file + description: + pattern: "*.*" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@joseespinosa" + - "@drpatelh" diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index 909d98f..6f061d7 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -19,8 +19,20 @@ workflow DB_CHECK { .dump(tag: "db_channel_prepped") .set{ dbs } + + parsed_samplesheet + .branch { + untar: it[0]['db_path'].toString().endsWith(".tar.gz") + skip: true + } + .set{ ch_dbs_for_untar } + + UNTAR ( ch_dbs_for_untar.untar ) + + ch_final_dbs = ch_dbs_for_untar.skip.mix( ch_dbs_untarred ) + emit: - dbs // channel: [ val(meta), [ db ] ] + dbs = ch_final_dbs // channel: [ val(meta), [ db ] ] versions = DATABASE_CHECK.out.versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index d66fb3a..938c87f 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -35,7 +35,7 @@ workflow INPUT_CHECK { } // Function to get list of [ meta, [ fastq_1, fastq_2 ] ] -def create_fastq_channel(LinkedHashMap row) { +def create_fastq_channels(LinkedHashMap row) { // create meta map def meta = [:] meta.id = row.sample From 631c115e1003fc7cb9b0c893bf7cde761f4287ef Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 21 Mar 2022 14:58:19 +0000 Subject: [PATCH 024/306] Adds PoC of untarring system --- conf/modules.config | 2 +- modules.json | 2 +- modules/nf-core/modules/untar/main.nf | 10 +++++----- modules/nf-core/modules/untar/meta.yml | 10 ++++++++++ subworkflows/local/db_check.nf | 9 +++++---- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 9e334bc..620ae1d 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -82,7 +82,7 @@ process { publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: 'copy', - pattern: '.{fastq.gz,txt}' + pattern: '*.{fastq.gz,txt}' ] ext.args = { "${meta.db_params}" } ext.when = params.run_kraken2 diff --git a/modules.json b/modules.json index 9a7f1df..96a43d8 100644 --- a/modules.json +++ b/modules.json @@ -25,7 +25,7 @@ "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "untar": { - "git_sha": "7ec09d0ef4df89617baacc9b2dafcddb7cd4b05a" + "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" } } } diff --git a/modules/nf-core/modules/untar/main.nf b/modules/nf-core/modules/untar/main.nf index bbae948..dc43fb7 100644 --- a/modules/nf-core/modules/untar/main.nf +++ b/modules/nf-core/modules/untar/main.nf @@ -8,19 +8,19 @@ process UNTAR { 'biocontainers/biocontainers:v1.2.0_cv1' }" input: - path archive + tuple val(meta), path(archive) output: - path "$untar" , emit: untar - path "versions.yml", emit: versions + tuple val(meta), path("$untar"), emit: untar + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when script: - def args = task.ext.args ?: '' + def args = task.ext.args ?: '' def args2 = task.ext.args2 ?: '' - untar = archive.toString() - '.tar.gz' + untar = archive.toString() - '.tar.gz' """ tar \\ -xzvf \\ diff --git a/modules/nf-core/modules/untar/meta.yml b/modules/nf-core/modules/untar/meta.yml index e877a97..d426919 100644 --- a/modules/nf-core/modules/untar/meta.yml +++ b/modules/nf-core/modules/untar/meta.yml @@ -10,11 +10,21 @@ tools: documentation: https://www.gnu.org/software/tar/manual/ licence: ["GPL-3.0-or-later"] input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] - archive: type: file description: File to be untar pattern: "*.{tar}.{gz}" output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] - untar: type: file description: diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index 6f061d7..641108d 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -3,6 +3,7 @@ // include { DATABASE_CHECK } from '../../modules/local/database_check' +include { UNTAR } from '../../modules/nf-core/modules/untar/main' workflow DB_CHECK { take: @@ -17,19 +18,19 @@ workflow DB_CHECK { .dump(tag: "db_split_csv_out") .map { create_db_channels(it) } .dump(tag: "db_channel_prepped") - .set{ dbs } - parsed_samplesheet .branch { - untar: it[0]['db_path'].toString().endsWith(".tar.gz") + untar: it[1].toString().endsWith(".tar.gz") skip: true } .set{ ch_dbs_for_untar } + // TODO Filter to only run UNTAR on DBs of tools actually using? + // TODO make optional whether to save UNTAR ( ch_dbs_for_untar.untar ) - ch_final_dbs = ch_dbs_for_untar.skip.mix( ch_dbs_untarred ) + ch_final_dbs = ch_dbs_for_untar.skip.mix( UNTAR.out.untar ) emit: dbs = ch_final_dbs // channel: [ val(meta), [ db ] ] From c97de32434d03fdc97c0a5dc9c75cf328b027193 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Mon, 21 Mar 2022 18:17:08 +0100 Subject: [PATCH 025/306] Make adapter and quality trimming optional --- nextflow.config | 1 + workflows/taxprofiler.nf | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/nextflow.config b/nextflow.config index 7b897ab..4a3a56d 100644 --- a/nextflow.config +++ b/nextflow.config @@ -57,6 +57,7 @@ params { // FASTQ preprocessing fastp_clip_merge = false fastp_exclude_unmerged = true + remove_adapters = false // MALT run_malt = false diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 9e52b59..0e144f3 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -104,9 +104,16 @@ workflow TAXPROFILER { FASTQ_PREPROCESSING ( INPUT_CHECK.out.fastq ) } - LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ) + ch_multiqc_files = Channel.empty() + if ( params.remove_adapters ) { + ch_longreads_preprocessed = LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ).reads + .map { it -> [ it[0], [it[1]] ] } ch_versions = ch_versions.mix(LONGREAD_PREPROCESSING.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix(LONGREAD_PREPROCESSING.out.mqc) + } else { + ch_longreads_preprocessed = INPUT_CHECK.out.nanopore + } // // PERFORM RUN MERGING @@ -138,6 +145,7 @@ workflow TAXPROFILER { // output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] ch_input_for_profiling = ch_reads_for_profiling + .mix( ch_longreads_preprocessed ) .combine(DB_CHECK.out.dbs) .dump(tag: "reads_plus_db") .branch { @@ -189,13 +197,11 @@ workflow TAXPROFILER { workflow_summary = WorkflowTaxprofiler.paramsSummaryMultiqc(workflow, summary_params) ch_workflow_summary = Channel.value(workflow_summary) - ch_multiqc_files = Channel.empty() ch_multiqc_files = ch_multiqc_files.mix(Channel.from(ch_multiqc_config)) ch_multiqc_files = ch_multiqc_files.mix(ch_multiqc_custom_config.collect().ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) - ch_multiqc_files = ch_multiqc_files.mix(LONGREAD_PREPROCESSING.out.mqc) if (params.fastp_clip_merge) { ch_multiqc_files = ch_multiqc_files.mix(FASTQ_PREPROCESSING.out.mqc) } From f6fe26de466446379f49ba00dd80eb995bafd0bb Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Mon, 21 Mar 2022 18:25:56 +0100 Subject: [PATCH 026/306] Rename shortread subworkflow to be more consistent --- .../{preprocessing.nf => shortread_preprocessing.nf} | 0 workflows/taxprofiler.nf | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename subworkflows/local/{preprocessing.nf => shortread_preprocessing.nf} (100%) diff --git a/subworkflows/local/preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf similarity index 100% rename from subworkflows/local/preprocessing.nf rename to subworkflows/local/shortread_preprocessing.nf diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 0e144f3..22c7518 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -39,7 +39,7 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi include { INPUT_CHECK } from '../subworkflows/local/input_check' include { DB_CHECK } from '../subworkflows/local/db_check' -include { FASTQ_PREPROCESSING } from '../subworkflows/local/preprocessing' +include { SHORTREAD_PREPROCESSING } from '../subworkflows/local/shortread_preprocessing' include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' /* @@ -101,7 +101,7 @@ workflow TAXPROFILER { // PERFORM PREPROCESSING // if ( params.fastp_clip_merge ) { - FASTQ_PREPROCESSING ( INPUT_CHECK.out.fastq ) + SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ) } ch_multiqc_files = Channel.empty() @@ -118,7 +118,7 @@ workflow TAXPROFILER { // // PERFORM RUN MERGING // - ch_processed_for_combine = FASTQ_PREPROCESSING.out.reads + ch_processed_for_combine = SHORTREAD_PREPROCESSING.out.reads .dump(tag: "prep_for_combine_grouping") .map { meta, reads -> @@ -203,7 +203,7 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) if (params.fastp_clip_merge) { - ch_multiqc_files = ch_multiqc_files.mix(FASTQ_PREPROCESSING.out.mqc) + ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_PREPROCESSING.out.mqc) } if (params.run_kraken2) { ch_multiqc_files = ch_multiqc_files.mix(KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([])) From 4940ec57ffee76378b9bfe8dcff1d73f3097846e Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Mon, 21 Mar 2022 18:26:26 +0100 Subject: [PATCH 027/306] Remove unnecessary extra point --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 622b8de..d454a9b 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,10 @@ On release, automated continuous integration tests run the pipeline on a full-si 1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) 2. Performs optional read pre-processing - - Adapter clipping and merging + - Adapter clipping and merging (short, and nanopore reads) - Low complexity filtering - Host read removal - Run merging - - Adapter and quality trimming of Nanopore reads 3. Performs taxonomic profiling a choice of: - Kraken2 - MetaPhlAn3 From 5b1b48e59e17d271d03450daca083dd8cec502af Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Mon, 21 Mar 2022 18:29:47 +0100 Subject: [PATCH 028/306] Update subworkflow name to be more consistent --- subworkflows/local/shortread_preprocessing.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index 5832824..406c198 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -7,7 +7,7 @@ include { FASTP as FASTP_SINGLE } from '../../modules/nf-core/modules/fast include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fastp/main' include { FASTQC as FASTQC_POST } from '../../modules/nf-core/modules/fastqc/main' -workflow FASTQ_PREPROCESSING { +workflow SHORTREAD_PREPROCESSING { take: reads // file: /path/to/samplesheet.csv From 80129985424b214ee22213a8db7cc139e2793ff5 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 21 Mar 2022 19:52:50 +0100 Subject: [PATCH 029/306] Make parameter naming more consistent for clipmerge --- conf/modules.config | 5 ++--- nextflow.config | 6 ++--- subworkflows/local/shortread_preprocessing.nf | 2 +- workflows/taxprofiler.nf | 22 ++++++++++++------- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 050772e..c09a011 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -41,7 +41,7 @@ process { // TODO also include option to NOT merge ext.args = [ { ${meta.single_end} } == 0 ? "-m" : '', - params.fastp_exclude_unmerged ? '' : "--include_unmerged" + params.shortread_excludeunmerged ? '' : "--include_unmerged" ].join(' ').trim() publishDir = [ path: { "${params.outdir}/fastp" }, @@ -84,7 +84,7 @@ process { pattern: '*.{rma6,tab,text,sam,log}' ] ext.args = { "${meta.db_params}" } - ext.when = params.run_malt + ext.prefix = { "${meta.id}-${meta.db_name}" } } withName: KRAKEN2_KRAKEN2 { @@ -94,7 +94,6 @@ process { pattern: '.{fastq.gz,txt}' ] ext.args = { "${meta.db_params}" } - ext.when = params.run_kraken2 ext.prefix = { "${meta.id}-${meta.db_name}" } } diff --git a/nextflow.config b/nextflow.config index 4a3a56d..5f7aec6 100644 --- a/nextflow.config +++ b/nextflow.config @@ -55,9 +55,9 @@ params { databases = null // FASTQ preprocessing - fastp_clip_merge = false - fastp_exclude_unmerged = true - remove_adapters = false + shortread_clipmerge = false + shortread_excludeunmerged = true + longread_clip = false // MALT run_malt = false diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index 406c198..d996a76 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -23,7 +23,7 @@ workflow SHORTREAD_PREPROCESSING { // TODO move to subworkflow - if ( params.fastp_clip_merge ) { + if ( params.shortread_clipmerge ) { ch_input_for_fastp = reads .dump(tag: "pre-fastp_branch") diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 22c7518..4aa0684 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -100,17 +100,14 @@ workflow TAXPROFILER { // // PERFORM PREPROCESSING // - if ( params.fastp_clip_merge ) { + if ( params.shortread_clipmerge ) { SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ) } - ch_multiqc_files = Channel.empty() - - if ( params.remove_adapters ) { + if ( params.longread_clip ) { ch_longreads_preprocessed = LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ).reads .map { it -> [ it[0], [it[1]] ] } ch_versions = ch_versions.mix(LONGREAD_PREPROCESSING.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix(LONGREAD_PREPROCESSING.out.mqc) } else { ch_longreads_preprocessed = INPUT_CHECK.out.nanopore } @@ -187,9 +184,13 @@ workflow TAXPROFILER { // // RUN PROFILING // - MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) - KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) + if ( params.run_malt ) { + MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) + } + if ( params.run_kraken2 ) { + KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) + } // // MODULE: MultiQC @@ -197,14 +198,19 @@ workflow TAXPROFILER { workflow_summary = WorkflowTaxprofiler.paramsSummaryMultiqc(workflow, summary_params) ch_workflow_summary = Channel.value(workflow_summary) + ch_multiqc_files = Channel.empty() ch_multiqc_files = ch_multiqc_files.mix(Channel.from(ch_multiqc_config)) ch_multiqc_files = ch_multiqc_files.mix(ch_multiqc_custom_config.collect().ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) - if (params.fastp_clip_merge) { + + if (params.shortread_clipmerge) { ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_PREPROCESSING.out.mqc) } + if (params.longread_clip) { + ch_multiqc_files = ch_multiqc_files.mix(LONGREAD_PREPROCESSING.out.mqc) + } if (params.run_kraken2) { ch_multiqc_files = ch_multiqc_files.mix(KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([])) ch_versions = ch_versions.mix(KRAKEN2_KRAKEN2.out.versions.first()) From 07eed435c628ae0518246aa31e87d81fb02c69ad Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 21 Mar 2022 19:54:51 +0100 Subject: [PATCH 030/306] Replace set with explicity assignment --- subworkflows/local/db_check.nf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index 641108d..890e373 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -19,12 +19,11 @@ workflow DB_CHECK { .map { create_db_channels(it) } .dump(tag: "db_channel_prepped") - parsed_samplesheet + ch_dbs_for_untar = parsed_samplesheet .branch { untar: it[1].toString().endsWith(".tar.gz") skip: true } - .set{ ch_dbs_for_untar } // TODO Filter to only run UNTAR on DBs of tools actually using? // TODO make optional whether to save From 81bfb629cadfa76ac6f639f320f3bd0fb0ae4dfd Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 21 Mar 2022 20:28:09 +0100 Subject: [PATCH 031/306] Add working basic test to begin --- .github/workflows/ci.yml | 1 + conf/modules.config | 16 ++++++++++++++++ conf/test.config | 8 +++++++- subworkflows/local/input_check.nf | 6 +++--- workflows/taxprofiler.nf | 13 ++++++++----- 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 033eb63..5fe2777 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,3 +48,4 @@ jobs: # Remember that you can parallelise this by using strategy.matrix run: | nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results + # TODO Add test that runs with pre-downloaded and decompressed databases diff --git a/conf/modules.config b/conf/modules.config index ab8f021..b9c1008 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -26,6 +26,22 @@ process { ] } + withName: DATABASE_CHECK { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: UNTAR { + publishDir = [ + path: { "${params.outdir}/databases" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: FASTQC { ext.args = '--quiet' ext.prefix = { "${meta.id}_${meta.run_accession}_raw" } diff --git a/conf/test.config b/conf/test.config index 51f3bb6..42d8de6 100644 --- a/conf/test.config +++ b/conf/test.config @@ -22,6 +22,12 @@ params { // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + outdir = "./results" + // TODO replace with official once ready + databases = 'https://raw.githubusercontent.com/jfy133/nf-core-test-datasets/taxprofiler/database.csv' + run_kraken2 = true + run_malt = true + shortread_clipmerge = true } diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 67dadc2..4501386 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -30,7 +30,7 @@ workflow INPUT_CHECK { .set { nanopore } parsed_samplesheet.fasta - .map { create_fasta_channels(it) } + .map { create_fasta_channel(it) } .dump(tag: "fasta_channel_init") .set { fasta } @@ -42,7 +42,7 @@ workflow INPUT_CHECK { } // Function to get list of [ meta, [ fastq_1, fastq_2 ] ] -def create_fastq_channels(LinkedHashMap row) { +def create_fastq_channel(LinkedHashMap row) { // create meta map def meta = [:] meta.id = row.sample @@ -74,7 +74,7 @@ def create_fastq_channels(LinkedHashMap row) { } // Function to get list of [ meta, fasta ] -def create_fasta_channels(LinkedHashMap row) { +def create_fasta_channel(LinkedHashMap row) { def meta = [:] meta.id = row.sample meta.run_accession = row.run_accession diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 4aa0684..6fc5450 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -101,7 +101,9 @@ workflow TAXPROFILER { // PERFORM PREPROCESSING // if ( params.shortread_clipmerge ) { - SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ) + ch_shortreads_preprocessed = SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ).reads + } else { + ch_shortreads_preprocessed = INPUT_CHECK.out.fastq } if ( params.longread_clip ) { @@ -113,9 +115,10 @@ workflow TAXPROFILER { } // - // PERFORM RUN MERGING + // PERFORM SHORT READ RUN MERGING + // TODO: Check not necessary for long reads too? // - ch_processed_for_combine = SHORTREAD_PREPROCESSING.out.reads + ch_processed_for_combine = ch_shortreads_preprocessed .dump(tag: "prep_for_combine_grouping") .map { meta, reads -> @@ -140,7 +143,7 @@ workflow TAXPROFILER { // COMBINE READS WITH POSSIBLE DATABASES // - // output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] + // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] ch_input_for_profiling = ch_reads_for_profiling .mix( ch_longreads_preprocessed ) .combine(DB_CHECK.out.dbs) @@ -152,7 +155,7 @@ workflow TAXPROFILER { } // - // PREP PROFILER INPUT CHANNELS ON PER TOOL BASIS + // PREPARE PROFILER INPUT CHANNELS // // We groupTuple to have all samples in one channel for MALT as database From 358b89a4c6d18f195c7aae3115e28080cfa32b3e Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 21 Mar 2022 20:30:29 +0100 Subject: [PATCH 032/306] Linting --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index b9c1008..29a5135 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -35,7 +35,7 @@ process { } withName: UNTAR { - publishDir = [ + publishDir = [ path: { "${params.outdir}/databases" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } From 48b6ef508dbb0d0a35b91aa8e670616f640b36ae Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 23 Mar 2022 10:06:01 +0100 Subject: [PATCH 033/306] Update path to samplesheet --- conf/test.config | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conf/test.config b/conf/test.config index 42d8de6..2e08499 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,8 +24,7 @@ params { // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' outdir = "./results" - // TODO replace with official once ready - databases = 'https://raw.githubusercontent.com/jfy133/nf-core-test-datasets/taxprofiler/database.csv' + databases = 'https://raw.githubusercontent.com/nf-core/nf-core-test-datasets/taxprofiler/database.csv' run_kraken2 = true run_malt = true shortread_clipmerge = true From 038a8d106a3e027d48c781ab15197270ec38e7a5 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 23 Mar 2022 11:25:47 +0100 Subject: [PATCH 034/306] Fix test with correct URL --- conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index 2e08499..5924d7a 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,7 +24,7 @@ params { // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' outdir = "./results" - databases = 'https://raw.githubusercontent.com/nf-core/nf-core-test-datasets/taxprofiler/database.csv' + databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' run_kraken2 = true run_malt = true shortread_clipmerge = true From 3ff54e620e9b9212a3bad5c687769b8c37e5b89d Mon Sep 17 00:00:00 2001 From: sofstam Date: Thu, 24 Mar 2022 12:51:45 +0100 Subject: [PATCH 035/306] Add centrifuge classification --- conf/modules.config | 10 +++ conf/test.config | 1 + modules.json | 5 +- modules/nf-core/modules/centrifuge/main.nf | 63 ++++++++++++++++++ modules/nf-core/modules/centrifuge/meta.yml | 73 +++++++++++++++++++++ nextflow.config | 8 ++- subworkflows/local/db_check.nf | 2 +- subworkflows/local/input_check.nf | 3 +- workflows/taxprofiler.nf | 22 +++++-- 9 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 modules/nf-core/modules/centrifuge/main.nf create mode 100644 modules/nf-core/modules/centrifuge/meta.yml diff --git a/conf/modules.config b/conf/modules.config index 29a5135..20e6bba 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -121,4 +121,14 @@ process { ] } + withName: CENTRIFUGE { + publishDir = [ + path: { "${params.outdir}/centrifuge/${meta.db_name}" }, + mode: 'copy', + pattern: '*.{fastq.gz,txt}' + ] + ext.args = { "${meta.db_params}" } + ext.prefix = { "${meta.id}-${meta.db_name}" } + } + } diff --git a/conf/test.config b/conf/test.config index 42d8de6..6fca9c0 100644 --- a/conf/test.config +++ b/conf/test.config @@ -29,5 +29,6 @@ params { run_kraken2 = true run_malt = true shortread_clipmerge = true + run_centrifuge = true } diff --git a/modules.json b/modules.json index 673a69b..b9dfc87 100644 --- a/modules.json +++ b/modules.json @@ -29,6 +29,9 @@ "porechop": { "git_sha": "e20e57f90b6787ac9a010a980cf6ea98bd990046" } + "centrifuge": { + "git_sha": "ea41a8a6f761b9993d857570e872abaae3fea555" + } } } -} \ No newline at end of file +} diff --git a/modules/nf-core/modules/centrifuge/main.nf b/modules/nf-core/modules/centrifuge/main.nf new file mode 100644 index 0000000..7eb566d --- /dev/null +++ b/modules/nf-core/modules/centrifuge/main.nf @@ -0,0 +1,63 @@ +process CENTRIFUGE { + tag "$meta.id" + label 'process_high' + + conda (params.enable_conda ? "bioconda::centrifuge=1.0.4_beta" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/centrifuge:1.0.4_beta--h9a82719_6' : + 'quay.io/biocontainers/centrifuge:1.0.4_beta--h9a82719_6' }" + + input: + tuple val(meta), path(reads) + path db + val save_unaligned + val save_aligned + val sam_format + + output: + tuple val(meta), path('*report.txt') , emit: report + tuple val(meta), path('*results.txt') , emit: results + tuple val(meta), path('*kreport.txt') , emit: kreport + tuple val(meta), path('*.sam') , optional: true, emit: sam + tuple val(meta), path('*.mapped.fastq{,.1,.2}.gz') , optional: true, emit: fastq_mapped + tuple val(meta), path('*.unmapped.fastq{,.1,.2}.gz') , optional: true, emit: fastq_unmapped + 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}" + def paired = meta.single_end ? "-U ${reads}" : "-1 ${reads[0]} -2 ${reads[1]}" + def db_name = db.toString().replace(".tar.gz","") + def unaligned = '' + def aligned = '' + if (meta.single_end) { + unaligned = save_unaligned ? "--un-gz ${prefix}.unmapped.fastq.gz" : '' + aligned = save_aligned ? "--al-gz ${prefix}.mapped.fastq.gz" : '' + } else { + unaligned = save_unaligned ? "--un-conc-gz ${prefix}.unmapped.fastq.gz" : '' + aligned = save_aligned ? "--al-conc-gz ${prefix}.mapped.fastq.gz" : '' + } + def sam_output = sam_format ? "--out-fmt 'sam'" : '' + """ + tar -xf $db + centrifuge \\ + -x $db_name \\ + -p $task.cpus \\ + $paired \\ + --report-file ${prefix}.report.txt \\ + -S ${prefix}.results.txt \\ + $unaligned \\ + $aligned \\ + $sam_output \\ + $args + centrifuge-kreport -x $db_name ${prefix}.results.txt > ${prefix}.kreport.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + centrifuge: \$( centrifuge --version | sed -n 1p | sed 's/^.*centrifuge-class version //') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/centrifuge/meta.yml b/modules/nf-core/modules/centrifuge/meta.yml new file mode 100644 index 0000000..3adf0e2 --- /dev/null +++ b/modules/nf-core/modules/centrifuge/meta.yml @@ -0,0 +1,73 @@ +name: centrifuge +description: Classifies metagenomic sequence data +keywords: + - classify + - metagenomics + - fastq + - db +tools: + - centrifuge: + description: Centrifuge is a classifier for metagenomic sequences. + homepage: https://ccb.jhu.edu/software/centrifuge/ + documentation: https://ccb.jhu.edu/software/centrifuge/manual.shtml + doi: 10.1101/gr.210641.116 + licence: ["GPL v3"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + - db: + type: directory + description: Centrifuge database in .tar.gz format + pattern: "*.tar.gz" + - save_unaligned: + type: value + description: If true unmapped fastq files are saved + - save_aligned: + type: value + description: If true mapped fastq files are saved +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - report: + type: file + description: | + File containing a classification summary + pattern: "*.{report.txt}" + - results: + type: file + description: | + File containing classification results + pattern: "*.{results.txt}" + - kreport: + type: file + description: | + File containing kraken-style report from centrifuge + out files. + pattern: "*.{kreport.txt}" + - fastq_unmapped: + type: file + description: Unmapped fastq files + pattern: "*.unmapped.fastq.gz" + - fastq_mapped: + type: file + description: Mapped fastq files + pattern: "*.mapped.fastq.gz" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@sofstam" + - "@jfy133" + - "@sateeshperi" diff --git a/nextflow.config b/nextflow.config index 5f7aec6..5bd8f39 100644 --- a/nextflow.config +++ b/nextflow.config @@ -56,7 +56,7 @@ params { // FASTQ preprocessing shortread_clipmerge = false - shortread_excludeunmerged = true + shortread_excludeunmerged = true longread_clip = false // MALT @@ -65,6 +65,12 @@ params { // kraken2 run_kraken2 = false + + // centrifuge + run_centrifuge = false + save_unaligned = false + save_aligned = false + sam_format = false } // Load base.config by default for all pipelines diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index 890e373..28268c3 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -21,7 +21,7 @@ workflow DB_CHECK { ch_dbs_for_untar = parsed_samplesheet .branch { - untar: it[1].toString().endsWith(".tar.gz") + untar: it[1].toString().endsWith(".tar.gz") && it[0]['tool']!="centrifuge" skip: true } diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 4501386..b64e31e 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -67,8 +67,9 @@ def create_fastq_channel(LinkedHashMap row) { if (!file(row.fastq_2).exists()) { exit 1, "ERROR: Please check input samplesheet -> Read 2 FastQ file does not exist!\n${row.fastq_2}" } - fastq_meta = [ meta, [ file(row.fastq_1), file(row.fastq_2) ] ] + fastq_meta = [ meta, [ file(row.fastq_1), file(row.fastq_2) ] ] } + } return fastq_meta } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 6fc5450..ea3ef18 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -58,7 +58,7 @@ include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/ include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fastq/main' include { MALT_RUN } from '../modules/nf-core/modules/malt/run/main' include { KRAKEN2_KRAKEN2 } from '../modules/nf-core/modules/kraken2/kraken2/main' - +include { CENTRIFUGE } from '../modules/nf-core/modules/centrifuge/main' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -149,9 +149,10 @@ workflow TAXPROFILER { .combine(DB_CHECK.out.dbs) .dump(tag: "reads_plus_db") .branch { - malt: it[2]['tool'] == 'malt' - kraken2: it[2]['tool'] == 'kraken2' - unknown: true + malt: it[2]['tool'] == 'malt' + kraken2: it[2]['tool'] == 'kraken2' + centrifuge: it[2]['tool'] == 'centrifuge' + unknown: true } // @@ -184,6 +185,15 @@ workflow TAXPROFILER { db: it[3] } + // We can run centrifuge one-by-one sample-wise + ch_input_for_centrifuge = ch_input_for_profiling.centrifuge + .dump(tag: "input for centrifuge") + .multiMap { + it -> + reads: [ it[0] + it[2], it[1] ] + db: it[3] + } + // // RUN PROFILING // @@ -195,6 +205,10 @@ workflow TAXPROFILER { KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) } + if ( params.run_centrifuge ) { + CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.save_unaligned, params.save_aligned, params.sam_format ) + } + // // MODULE: MultiQC // From e3be393f16025f76cb8bc2389ccb2eb841b569f5 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 24 Mar 2022 14:18:15 +0100 Subject: [PATCH 036/306] Fix broken json --- modules.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules.json b/modules.json index c24ff6a..b78ba31 100644 --- a/modules.json +++ b/modules.json @@ -26,9 +26,10 @@ }, "untar": { "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" + }, "porechop": { "git_sha": "e20e57f90b6787ac9a010a980cf6ea98bd990046" } } } -} +} \ No newline at end of file From 203cd2e7d39936dec20d75901fe6afc94b7f8a6f Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 24 Mar 2022 14:31:53 +0100 Subject: [PATCH 037/306] Prettier --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ea659bb..0c1d3c7 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ ## Introduction + **nf-core/taxprofiler** is a bioinformatics best-practice analysis pipeline for taxonomic profiling of shotgun metagenomic data. It allows for in-parallel profiling against multiple profiling tools and databases and produces standardised output tables. 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! @@ -44,7 +45,7 @@ On release, automated continuous integration tests run the pipeline on a full-si - Kaiju - mOTUs 4. Perform optional post-processing with: - - bracken + - bracken 5. Standardises output tables 6. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) From cc9368ee527243fc1809f891154ae0eb569887f3 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 24 Mar 2022 15:35:09 +0100 Subject: [PATCH 038/306] Prettier linting and update schema --- nextflow.config | 2 +- nextflow_schema.json | 48 ++++++++++++++++++++++++++++++++++++---- workflows/taxprofiler.nf | 3 ++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/nextflow.config b/nextflow.config index 5f7aec6..cc77a99 100644 --- a/nextflow.config +++ b/nextflow.config @@ -56,7 +56,7 @@ params { // FASTQ preprocessing shortread_clipmerge = false - shortread_excludeunmerged = true + shortread_excludeunmerged = true longread_clip = false // MALT diff --git a/nextflow_schema.json b/nextflow_schema.json index de1e515..03910e9 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -256,5 +266,35 @@ { "$ref": "#/definitions/generic_options" } - ] -} + ], + "properties": { + "databases": { + "type": "string", + "default": null + }, + "shortread_clipmerge": { + "type": "string", + "default": "false" + }, + "shortread_excludeunmerged": { + "type": "string", + "default": "true" + }, + "longread_clip": { + "type": "string", + "default": "false" + }, + "run_malt": { + "type": "string", + "default": "false" + }, + "malt_mode": { + "type": "string", + "default": "BlastN" + }, + "run_kraken2": { + "type": "string", + "default": "false" + } + } +} \ No newline at end of file diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 7320922..29058e6 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -88,8 +88,9 @@ workflow TAXPROFILER { // // MODULE: Run FastQC // + ch_input_for_fastqc = INPUT_CHECK.out.fastq.mix( INPUT_CHECK.out.nanopore ).dump(tag: "input_to_fastq") FASTQC ( - INPUT_CHECK.out.fastq.mix( INPUT_CHECK.out.nanopore ) + ch_input_for_fastqc ) ch_versions = ch_versions.mix(FASTQC.out.versions.first()) From 494c641fb8ade3f6f6e73c537f699df182b16b9d Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 24 Mar 2022 15:37:42 +0100 Subject: [PATCH 039/306] Prettier --- .prettierignore | 2 ++ modules.json | 2 +- nextflow_schema.json | 16 +++------------- 3 files changed, 6 insertions(+), 14 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..c037321 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +testing/ +tests/ diff --git a/modules.json b/modules.json index b78ba31..9e6428a 100644 --- a/modules.json +++ b/modules.json @@ -32,4 +32,4 @@ } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index 03910e9..7bb03c3 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -297,4 +287,4 @@ "default": "false" } } -} \ No newline at end of file +} From 92a3c8ec8d6e6698f748f154b0dfeaf92fd9d52b Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 24 Mar 2022 15:40:14 +0100 Subject: [PATCH 040/306] Prettier --- nextflow_schema.json | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 7bb03c3..9527da4 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -260,31 +260,27 @@ "properties": { "databases": { "type": "string", - "default": null + "default": "None" }, "shortread_clipmerge": { - "type": "string", - "default": "false" + "type": "boolean" }, "shortread_excludeunmerged": { - "type": "string", - "default": "true" + "type": "boolean", + "default": true }, "longread_clip": { - "type": "string", - "default": "false" + "type": "boolean" }, "run_malt": { - "type": "string", - "default": "false" + "type": "boolean" }, "malt_mode": { "type": "string", "default": "BlastN" }, "run_kraken2": { - "type": "string", - "default": "false" + "type": "boolean" } } } From fafb7e0f6fce56a57f6d375976b3cffe37ebe14b Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Thu, 24 Mar 2022 16:59:12 +0100 Subject: [PATCH 041/306] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c1d3c7..e976f73 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ![nf-core/taxprofiler](docs/images/nf-core/taxprofiler_logo_light.png#gh-light-mode-only) ![nf-core/taxprofiler](docs/images/nf-core/taxprofiler_logo_dark.png#gh-dark-mode-only) +# ![nf-core/taxprofiler](docs/images/nf-core-taxprofiler_logo_light.png#gh-light-mode-only) ![nf-core/taxprofiler](docs/images/nf-core-taxprofiler_logo_dark.png#gh-dark-mode-only) [![GitHub Actions CI Status](https://github.com/nf-core/taxprofiler/workflows/nf-core%20CI/badge.svg)](https://github.com/nf-core/taxprofiler/actions?query=workflow%3A%22nf-core+CI%22) [![GitHub Actions Linting Status](https://github.com/nf-core/taxprofiler/workflows/nf-core%20linting/badge.svg)](https://github.com/nf-core/taxprofiler/actions?query=workflow%3A%22nf-core+linting%22) From 4e93abc7c06b35eae584af4338c8aeaa60b8fcf8 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 25 Mar 2022 14:58:06 +0100 Subject: [PATCH 042/306] Add read improved read preprocessing --- conf/modules.config | 21 +++++- nextflow.config | 12 +++- nextflow_schema.json | 2 +- subworkflows/local/shortread_fastp.nf | 65 +++++++++++++++++++ subworkflows/local/shortread_preprocessing.nf | 55 ++++------------ workflows/taxprofiler.nf | 2 + 6 files changed, 107 insertions(+), 50 deletions(-) create mode 100644 subworkflows/local/shortread_fastp.nf diff --git a/conf/modules.config b/conf/modules.config index 29a5135..36bc626 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -52,12 +52,27 @@ process { ] } + withName: FASTQC_PROCESSED { + ext.args = '--quiet' + ext.prefix = { "${meta.id}_${meta.run_accession}_processed" } + publishDir = [ + path: { "${params.outdir}/fastqc/processed" }, + mode: 'copy', + pattern: '*.html' + ] + } + withName: FASTP { ext.prefix = { "${meta.id}_${meta.run_accession}" } - // TODO also include option to NOT merge ext.args = [ - { ${meta.single_end} } == 0 ? "-m" : '', - params.shortread_excludeunmerged ? '' : "--include_unmerged" + // collapsing options + params.shortread_clipmerge_excludeunmerged ? '' : "--include_unmerged", + // trimming options + params.shortread_clipmerge_skiptrim ? "--disable_adapter_trimming" : "", + params.shortread_adapter1 ? "--adapter_sequence ${params.shortread_adapter1}" : "", + !{ ${meta.single_end} } && params.shortread_adapter2 ? "--adapter_sequence_r2 ${params.shortread_adapter2}" : !{ ${meta.single_end} } ? "--detect_adapter_for_pe" : "" + // filtering options + "--length_required ${params.shortread_clipmerge_minlength}" ].join(' ').trim() publishDir = [ path: { "${params.outdir}/fastp" }, diff --git a/nextflow.config b/nextflow.config index cc77a99..a312d0c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -55,9 +55,15 @@ params { databases = null // FASTQ preprocessing - shortread_clipmerge = false - shortread_excludeunmerged = true - longread_clip = false + shortread_clipmerge = false + shortread_clipmerge_tool = 'fastp' + shortread_clipmerge_skiptrim = false + shortread_clipmerge_mergepairs = false + shortread_clipmerge_excludeunmerged = true + shortread_clipmerge_adapter1 = null + shortread_clipmerge_adapter2 = null + shortread_clipmerge_minlength = 15 + longread_clip = false // MALT run_malt = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 9527da4..0fa217f 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -265,7 +265,7 @@ "shortread_clipmerge": { "type": "boolean" }, - "shortread_excludeunmerged": { + "shortread_clipmerge_excludeunmerged": { "type": "boolean", "default": true }, diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf new file mode 100644 index 0000000..87aba25 --- /dev/null +++ b/subworkflows/local/shortread_fastp.nf @@ -0,0 +1,65 @@ +// +// Check input samplesheet and get read channels +// + + +include { FASTP as FASTP_SINGLE } from '../../modules/nf-core/modules/fastp/main' +include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fastp/main' + +workflow SHORTREAD_FASTP { + take: + reads // file: /path/to/samplesheet.csv + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + // + // STEP: Read clipping and merging + // + // TODO give option to retain singletons (probably fastp option likely) + // TODO move to subworkflow + + ch_input_for_fastp = reads + .dump(tag: "pre-fastp_branch") + .branch{ + single: it[0]['single_end'] == true + paired: it[0]['single_end'] == false + } + + ch_input_for_fastp.single.dump(tag: "input_fastp_single") + ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") + + FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) + FASTP_PAIRED ( ch_input_for_fastp.paired, false, params.shortread_clipmerge_mergepairs ) + + if ( params.shortread_clipmerge_mergepairs ) { + ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged + .mix( FASTP_SINGLE.out.reads ) + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] + } + } else { + ch_fastp_reads_prepped = FASTP_PAIRED.out.reads + .mix( FASTP_SINGLE.out.reads ) + } + + ch_versions = ch_versions.mix(FASTP_SINGLE.out.versions.first()) + ch_versions = ch_versions.mix(FASTP_PAIRED.out.versions.first()) + + ch_processed_reads = ch_fastp_reads_prepped.dump(tag: "ch_fastp_reads_prepped") + + ch_multiqc_files = ch_multiqc_files.mix( FASTP_SINGLE.out.json.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( FASTP_PAIRED.out.json.collect{it[1]} ) + + ch_multiqc_files.dump(tag: "preprocessing_fastp_mqc_final") + + emit: + reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} + diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index d996a76..c31289d 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -3,17 +3,16 @@ // -include { FASTP as FASTP_SINGLE } from '../../modules/nf-core/modules/fastp/main' -include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fastp/main' -include { FASTQC as FASTQC_POST } from '../../modules/nf-core/modules/fastqc/main' +include { SHORTREAD_FASTP } from './shortread_fastp' +include { FASTQC as FASTQC_PROCESSED } from '../../modules/nf-core/modules/fastqc/main' workflow SHORTREAD_PREPROCESSING { take: reads // file: /path/to/samplesheet.csv main: - ch_versions = Channel.empty() - ch_multiqc_files = Channel.empty() + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() // // STEP: Read clipping and merging @@ -22,50 +21,20 @@ workflow SHORTREAD_PREPROCESSING { // TODO give option to retain singletons (probably fastp option likely) // TODO move to subworkflow - - if ( params.shortread_clipmerge ) { - - ch_input_for_fastp = reads - .dump(tag: "pre-fastp_branch") - .branch{ - single: it[0]['single_end'] == true - paired: it[0]['single_end'] == false - } - - ch_input_for_fastp.single.dump(tag: "input_fastp_single") - ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") - - FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) - FASTP_PAIRED ( ch_input_for_fastp.paired, false, true ) - - ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged - .mix( FASTP_SINGLE.out.reads ) - .map { - meta, reads -> - def meta_new = meta.clone() - meta_new['single_end'] = 1 - [ meta_new, reads ] - } - - FASTQC_POST ( ch_fastp_reads_prepped ) - - ch_versions = ch_versions.mix(FASTP_SINGLE.out.versions.first()) - ch_versions = ch_versions.mix(FASTP_PAIRED.out.versions.first()) - - ch_processed_reads = ch_fastp_reads_prepped - - ch_multiqc_files = ch_multiqc_files.mix( FASTQC_POST.out.zip.collect{it[1]} ) - ch_multiqc_files = ch_multiqc_files.mix( FASTP_SINGLE.out.json.collect{it[1]} ) - ch_multiqc_files = ch_multiqc_files.mix( FASTP_PAIRED.out.json.collect{it[1]} ) - - ch_multiqc_files.dump(tag: "preprocessing_mqc_final") - + if ( params.shortread_clipmerge_tool == "fastp" ) { + ch_processed_reads = SHORTREAD_FASTP ( reads ).reads + ch_versions = ch_versions.mix( SHORTREAD_FASTP.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_FASTP.out.mqc ) } else { ch_processed_reads = reads } + //FASTQC_PROCESSED ( ch_processed_reads ) + //ch_versions = ch_versions.mix( FASTQC_PROCESSED.out.versions ) + //ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip.collect{it[1]} ) emit: + // TODO: problem, this is being exported as a multi-channel output? This is why FASTQC is broken reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] versions = ch_versions // channel: [ versions.yml ] mqc = ch_multiqc_files diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 29058e6..0a907bf 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -17,6 +17,7 @@ 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 samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } +if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] warning: MALT does not except uncollapsed paired-reads. Pairs will be profiled as separate files." /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -135,6 +136,7 @@ workflow TAXPROFILER { CAT_FASTQ ( ch_processed_for_combine.combine ) + // TODO May need to flatten reads? ch_reads_for_profiling = ch_processed_for_combine.skip .dump(tag: "skip_combine") .mix( CAT_FASTQ.out.reads ) From ff1f28f4f0b8e88898e0f568f83e2da90ff81bc6 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 25 Mar 2022 15:01:25 +0100 Subject: [PATCH 043/306] Update schema --- conf/modules.config | 4 ++-- nextflow_schema.json | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 36bc626..1052bed 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -69,8 +69,8 @@ process { params.shortread_clipmerge_excludeunmerged ? '' : "--include_unmerged", // trimming options params.shortread_clipmerge_skiptrim ? "--disable_adapter_trimming" : "", - params.shortread_adapter1 ? "--adapter_sequence ${params.shortread_adapter1}" : "", - !{ ${meta.single_end} } && params.shortread_adapter2 ? "--adapter_sequence_r2 ${params.shortread_adapter2}" : !{ ${meta.single_end} } ? "--detect_adapter_for_pe" : "" + params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", + !{ ${meta.single_end} } && params.shortread_clipmerge_adapter2 ? "--adapter_sequence_r2 ${params.shortread_clipmerge_adapter2}" : !{ ${meta.single_end} } ? "--detect_adapter_for_pe" : "", // filtering options "--length_required ${params.shortread_clipmerge_minlength}" ].join(' ').trim() diff --git a/nextflow_schema.json b/nextflow_schema.json index 0fa217f..42307e9 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -281,6 +291,28 @@ }, "run_kraken2": { "type": "boolean" + }, + "shortread_clipmerge_tool": { + "type": "string", + "default": "fastp" + }, + "shortread_clipmerge_skiptrim": { + "type": "boolean" + }, + "shortread_clipmerge_mergepairs": { + "type": "boolean" + }, + "shortread_clipmerge_adapter1": { + "type": "string", + "default": null + }, + "shortread_clipmerge_adapter2": { + "type": "string", + "default": null + }, + "shortread_clipmerge_minlength": { + "type": "integer", + "default": 15 } } -} +} \ No newline at end of file From dfcf8f7b1ae183a233d8a1c94f39a1f27084a966 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 25 Mar 2022 15:10:52 +0100 Subject: [PATCH 044/306] Prettier --- conf/test.config | 1 - nextflow_schema.json | 16 +++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/conf/test.config b/conf/test.config index 5924d7a..92a10e4 100644 --- a/conf/test.config +++ b/conf/test.config @@ -23,7 +23,6 @@ params { // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' - outdir = "./results" databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' run_kraken2 = true run_malt = true diff --git a/nextflow_schema.json b/nextflow_schema.json index 42307e9..545f990 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -315,4 +305,4 @@ "default": 15 } } -} \ No newline at end of file +} From b5f5c755046b3ae1af9648e879dfa48519385891 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 25 Mar 2022 15:21:52 +0100 Subject: [PATCH 045/306] Fix file header descriptions --- subworkflows/local/longread_preprocessing.nf | 9 ++++++--- subworkflows/local/shortread_fastp.nf | 10 +++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index da1049a..58968e8 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -1,6 +1,9 @@ +/* +Process long raw reads with porechop +*/ -include { FASTQC as FASTQC_POST } from '../../modules/nf-core/modules/fastqc/main' -include { PORECHOP } from '../../modules/nf-core/modules/porechop/main' +include { FASTQC as FASTQC_PROCESSED } from '../../modules/nf-core/modules/fastqc/main' +include { PORECHOP } from '../../modules/nf-core/modules/porechop/main' workflow LONGREAD_PREPROCESSING { take: @@ -23,7 +26,7 @@ workflow LONGREAD_PREPROCESSING { FASTQC_POST ( PORECHOP.out.reads ) ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix( FASTQC_POST.out.zip.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip.collect{it[1]} ) emit: diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index 87aba25..f457cf3 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -1,7 +1,6 @@ -// -// Check input samplesheet and get read channels -// - +/* +Process short raw reads with FastP +*/ include { FASTP as FASTP_SINGLE } from '../../modules/nf-core/modules/fastp/main' include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fastp/main' @@ -17,9 +16,6 @@ workflow SHORTREAD_FASTP { // // STEP: Read clipping and merging // - // TODO give option to retain singletons (probably fastp option likely) - // TODO move to subworkflow - ch_input_for_fastp = reads .dump(tag: "pre-fastp_branch") .branch{ From 7ddcb09a85ed8c50f5d661b398f0e0a5e113add8 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 25 Mar 2022 15:22:41 +0100 Subject: [PATCH 046/306] Some more cleanup --- .../local/shortread_adapterremoval.nf | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 subworkflows/local/shortread_adapterremoval.nf diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf new file mode 100644 index 0000000..e15d2ef --- /dev/null +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -0,0 +1,64 @@ +/* +Process raw reads with AdapterRemoval +*/ + +include { FASTP as FASTP_SINGLE } from '../../modules/nf-core/modules/fastp/main' +include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fastp/main' + +workflow SHORTREAD_FASTP { + take: + reads // file: /path/to/samplesheet.csv + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + // + // STEP: Read clipping and merging + // + // TODO give option to retain singletons (probably fastp option likely) + // TODO move to subworkflow + + ch_input_for_fastp = reads + .dump(tag: "pre-fastp_branch") + .branch{ + single: it[0]['single_end'] == true + paired: it[0]['single_end'] == false + } + + ch_input_for_fastp.single.dump(tag: "input_fastp_single") + ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") + + FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) + FASTP_PAIRED ( ch_input_for_fastp.paired, false, params.shortread_clipmerge_mergepairs ) + + if ( params.shortread_clipmerge_mergepairs ) { + ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged + .mix( FASTP_SINGLE.out.reads ) + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] + } + } else { + ch_fastp_reads_prepped = FASTP_PAIRED.out.reads + .mix( FASTP_SINGLE.out.reads ) + } + + ch_versions = ch_versions.mix(FASTP_SINGLE.out.versions.first()) + ch_versions = ch_versions.mix(FASTP_PAIRED.out.versions.first()) + + ch_processed_reads = ch_fastp_reads_prepped.dump(tag: "ch_fastp_reads_prepped") + + ch_multiqc_files = ch_multiqc_files.mix( FASTP_SINGLE.out.json.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( FASTP_PAIRED.out.json.collect{it[1]} ) + + ch_multiqc_files.dump(tag: "preprocessing_fastp_mqc_final") + + emit: + reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} + From 0e6f7e2ca137fbb8306b733ab3abc3b6d8ad83f7 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 25 Mar 2022 15:24:10 +0100 Subject: [PATCH 047/306] Revert "Some more cleanup" This reverts commit 7ddcb09a85ed8c50f5d661b398f0e0a5e113add8. --- .../local/shortread_adapterremoval.nf | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 subworkflows/local/shortread_adapterremoval.nf diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf deleted file mode 100644 index e15d2ef..0000000 --- a/subworkflows/local/shortread_adapterremoval.nf +++ /dev/null @@ -1,64 +0,0 @@ -/* -Process raw reads with AdapterRemoval -*/ - -include { FASTP as FASTP_SINGLE } from '../../modules/nf-core/modules/fastp/main' -include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fastp/main' - -workflow SHORTREAD_FASTP { - take: - reads // file: /path/to/samplesheet.csv - - main: - ch_versions = Channel.empty() - ch_multiqc_files = Channel.empty() - - // - // STEP: Read clipping and merging - // - // TODO give option to retain singletons (probably fastp option likely) - // TODO move to subworkflow - - ch_input_for_fastp = reads - .dump(tag: "pre-fastp_branch") - .branch{ - single: it[0]['single_end'] == true - paired: it[0]['single_end'] == false - } - - ch_input_for_fastp.single.dump(tag: "input_fastp_single") - ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") - - FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) - FASTP_PAIRED ( ch_input_for_fastp.paired, false, params.shortread_clipmerge_mergepairs ) - - if ( params.shortread_clipmerge_mergepairs ) { - ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged - .mix( FASTP_SINGLE.out.reads ) - .map { - meta, reads -> - def meta_new = meta.clone() - meta_new['single_end'] = 1 - [ meta_new, reads ] - } - } else { - ch_fastp_reads_prepped = FASTP_PAIRED.out.reads - .mix( FASTP_SINGLE.out.reads ) - } - - ch_versions = ch_versions.mix(FASTP_SINGLE.out.versions.first()) - ch_versions = ch_versions.mix(FASTP_PAIRED.out.versions.first()) - - ch_processed_reads = ch_fastp_reads_prepped.dump(tag: "ch_fastp_reads_prepped") - - ch_multiqc_files = ch_multiqc_files.mix( FASTP_SINGLE.out.json.collect{it[1]} ) - ch_multiqc_files = ch_multiqc_files.mix( FASTP_PAIRED.out.json.collect{it[1]} ) - - ch_multiqc_files.dump(tag: "preprocessing_fastp_mqc_final") - - emit: - reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] - versions = ch_versions // channel: [ versions.yml ] - mqc = ch_multiqc_files -} - From b763bfa2c0a7dabe4ad8c4216cf852a3938a49b6 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 25 Mar 2022 15:26:57 +0100 Subject: [PATCH 048/306] More cleanup --- subworkflows/local/shortread_fastp.nf | 3 --- 1 file changed, 3 deletions(-) diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index f457cf3..57ce2a6 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -13,9 +13,6 @@ workflow SHORTREAD_FASTP { ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() - // - // STEP: Read clipping and merging - // ch_input_for_fastp = reads .dump(tag: "pre-fastp_branch") .branch{ From ede362dbf91df9f5118f0122f8bfc09d888cb141 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 25 Mar 2022 15:28:30 +0100 Subject: [PATCH 049/306] Remove duplicate config and sync longread fastqc --- conf/modules.config | 10 ---------- subworkflows/local/longread_preprocessing.nf | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 1052bed..7d50174 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -90,16 +90,6 @@ process { ] } - withName: FASTQC_POST { - ext.args = '--quiet' - ext.prefix = { "${meta.id}_${meta.run_accession}_processed" } - publishDir = [ - path: { "${params.outdir}/fastqc/processed" }, - mode: 'copy', - pattern: '*.html' - ] - } - withName: CAT_FASTQ { publishDir = [ path: { "${params.outdir}/prepared_sequences" }, diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 58968e8..7c7c24b 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -24,7 +24,7 @@ workflow LONGREAD_PREPROCESSING { [ meta_new, reads ] } - FASTQC_POST ( PORECHOP.out.reads ) + FASTQC_PROCESSED ( PORECHOP.out.reads ) ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip.collect{it[1]} ) From 000f129dab1b139cec6e9194670addf8d9e6173a Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Fri, 25 Mar 2022 15:32:56 +0100 Subject: [PATCH 050/306] Apply suggestions from code review --- subworkflows/local/shortread_preprocessing.nf | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index c31289d..c82536f 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -29,12 +29,11 @@ workflow SHORTREAD_PREPROCESSING { ch_processed_reads = reads } - //FASTQC_PROCESSED ( ch_processed_reads ) - //ch_versions = ch_versions.mix( FASTQC_PROCESSED.out.versions ) - //ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip.collect{it[1]} ) + FASTQC_PROCESSED ( ch_processed_reads ) + ch_versions = ch_versions.mix( FASTQC_PROCESSED.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip.collect{it[1]} ) emit: - // TODO: problem, this is being exported as a multi-channel output? This is why FASTQC is broken reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] versions = ch_versions // channel: [ versions.yml ] mqc = ch_multiqc_files From 16bdc79cc08750e8df36acc8c14aa90e8c522f76 Mon Sep 17 00:00:00 2001 From: sofstam Date: Fri, 25 Mar 2022 16:30:26 +0100 Subject: [PATCH 051/306] Apply prettier --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 2dbde56..ffaff90 100644 --- a/modules.json +++ b/modules.json @@ -29,7 +29,7 @@ }, "porechop": { "git_sha": "e20e57f90b6787ac9a010a980cf6ea98bd990046" - } + }, "centrifuge": { "git_sha": "ea41a8a6f761b9993d857570e872abaae3fea555" } From 59d3f18a753bf5b06ab495c98f96df77a84513d5 Mon Sep 17 00:00:00 2001 From: sofstam Date: Fri, 25 Mar 2022 17:18:04 +0100 Subject: [PATCH 052/306] Update nextflow_schema.json --- nextflow_schema.json | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index b8d5a1d..b4b3e07 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -293,20 +293,16 @@ "type": "boolean" }, "run_centrifuge": { - "type": "string", - "default": "false" + "type": "boolean" }, "centrifuge_save_unaligned": { - "type": "string", - "default": "false" + "type": "boolean" }, "centrifuge_save_aligned": { - "type": "string", - "default": "false" + "type": "boolean" }, "centrifuge_sam_format": { - "type": "string", - "default": "false" + "type": "boolean" } } -} \ No newline at end of file +} From 120382f51d184bed0375ab8c642f6bdab77a617f Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 26 Mar 2022 06:45:16 +0100 Subject: [PATCH 053/306] Fix input channels to kraken2 --- workflows/taxprofiler.nf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 0a907bf..c1d7f34 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -37,11 +37,11 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { INPUT_CHECK } from '../subworkflows/local/input_check' -include { DB_CHECK } from '../subworkflows/local/db_check' +include { DB_CHECK } from '../subworkflows/local/db_check' include { SHORTREAD_PREPROCESSING } from '../subworkflows/local/shortread_preprocessing' -include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' +include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -171,7 +171,7 @@ workflow TAXPROFILER { [ temp_meta, it[1], db ] } .groupTuple(by: [0,2]) - .dump(tag: "input for malt") + .dump(tag: "input_for_malt") .multiMap { it -> reads: [ it[0], it[1].flatten() ] @@ -180,10 +180,10 @@ workflow TAXPROFILER { // We can run Kraken2 one-by-one sample-wise ch_input_for_kraken2 = ch_input_for_profiling.kraken2 - .dump(tag: "input for kraken") + .dump(tag: "input_for_kraken") .multiMap { it -> - reads: [ it[0] + it[2], it[1] ] + reads: [ it[0] + it[2], it[1].flatten() ] db: it[3] } From 622fafedc88489026d4c8cd5eb62319e736d1375 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Sat, 26 Mar 2022 20:22:35 +0000 Subject: [PATCH 054/306] Prempt centrifuge database untar crash --- subworkflows/local/db_check.nf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index 890e373..96f815a 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -12,6 +12,9 @@ workflow DB_CHECK { main: // TODO: make database sheet check + // Checks: + // 1) no duplicates, + // 2) dbs with no special arguments does not have quotes, e.g. just `,,` and NOT `,"",` parsed_samplesheet = DATABASE_CHECK ( dbsheet ) .csv .splitCsv ( header:true, sep:',' ) @@ -21,7 +24,7 @@ workflow DB_CHECK { ch_dbs_for_untar = parsed_samplesheet .branch { - untar: it[1].toString().endsWith(".tar.gz") + untar: it[1].toString().endsWith(".tar.gz") && it[0]['tool'] != 'centrifuge' skip: true } From e6e8ed7cc9904973ce6236c98eb74f8246c3ec09 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Sat, 26 Mar 2022 20:54:50 +0000 Subject: [PATCH 055/306] Add some debugging notes --- conf/modules.config | 4 +-- workflows/taxprofiler.nf | 60 +++++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 7d50174..5823d3f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -105,7 +105,7 @@ process { pattern: '*.{rma6,tab,text,sam,log}' ] ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.db_name}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: KRAKEN2_KRAKEN2 { @@ -115,7 +115,7 @@ process { pattern: '*.{fastq.gz,txt}' ] ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.db_name}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: CUSTOM_DUMPSOFTWAREVERSIONS { diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index c1d7f34..ee921ea 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -74,9 +74,9 @@ workflow TAXPROFILER { ch_versions = Channel.empty() - // - // SUBWORKFLOW: Read in samplesheet, validate and stage input files - // + /* + SUBWORKFLOW: Read in samplesheet, validate and stage input files + */ INPUT_CHECK ( ch_input ) @@ -86,9 +86,9 @@ workflow TAXPROFILER { ch_databases ) - // - // MODULE: Run FastQC - // + /* + MODULE: Run FastQC + */ ch_input_for_fastqc = INPUT_CHECK.out.fastq.mix( INPUT_CHECK.out.nanopore ).dump(tag: "input_to_fastq") FASTQC ( ch_input_for_fastqc @@ -99,9 +99,9 @@ workflow TAXPROFILER { ch_versions.unique().collectFile(name: 'collated_versions.yml') ) - // - // PERFORM PREPROCESSING - // + /* + SUBWORKFLOW: PERFORM PREPROCESSING + */ if ( params.shortread_clipmerge ) { ch_shortreads_preprocessed = SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ).reads } else { @@ -116,16 +116,19 @@ workflow TAXPROFILER { ch_longreads_preprocessed = INPUT_CHECK.out.nanopore } - // - // PERFORM SHORT READ RUN MERGING + /* + MODULE: PERFORM SHORT READ RUN MERGING + */ + // TODO: Check not necessary for long reads too? - // + // TODO: source of clash - combined should only occur when + // files ARE to be combined. SE/unmerged (see not below) ch_processed_for_combine = ch_shortreads_preprocessed .dump(tag: "prep_for_combine_grouping") .map { meta, reads -> def meta_new = meta.clone() - meta_new['run_accession'] = 'combined' + //meta_new['run_accession'] = 'combined' [ meta_new, reads ] } .groupTuple ( by: 0 ) @@ -134,17 +137,18 @@ workflow TAXPROFILER { skip: it[1].size() < 2 } + // NOTE: this does not allow CATing of SE & PE runs of same sample + // when --shortread_clipmerge_mergepairs is false CAT_FASTQ ( ch_processed_for_combine.combine ) - // TODO May need to flatten reads? ch_reads_for_profiling = ch_processed_for_combine.skip .dump(tag: "skip_combine") .mix( CAT_FASTQ.out.reads ) .dump(tag: "files_for_profiling") - // - // COMBINE READS WITH POSSIBLE DATABASES - // + /* + COMBINE READS WITH POSSIBLE DATABASES + */ // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] ch_input_for_profiling = ch_reads_for_profiling @@ -157,9 +161,9 @@ workflow TAXPROFILER { unknown: true } - // - // PREPARE PROFILER INPUT CHANNELS - // + /* + PREPARE PROFILER INPUT CHANNELS + */ // We groupTuple to have all samples in one channel for MALT as database // loading takes a long time, so we only want to run it once per database @@ -171,7 +175,7 @@ workflow TAXPROFILER { [ temp_meta, it[1], db ] } .groupTuple(by: [0,2]) - .dump(tag: "input_for_malt") + .dump(tag: "input_to_malt") .multiMap { it -> reads: [ it[0], it[1].flatten() ] @@ -180,16 +184,16 @@ workflow TAXPROFILER { // We can run Kraken2 one-by-one sample-wise ch_input_for_kraken2 = ch_input_for_profiling.kraken2 - .dump(tag: "input_for_kraken") + .dump(tag: "input_to_kraken") .multiMap { it -> reads: [ it[0] + it[2], it[1].flatten() ] db: it[3] } - // - // RUN PROFILING - // + /* + MODULE: RUN PROFILING + */ if ( params.run_malt ) { MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) } @@ -198,9 +202,9 @@ workflow TAXPROFILER { KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) } - // - // MODULE: MultiQC - // + /* + MODULE: MultiQC + */ workflow_summary = WorkflowTaxprofiler.paramsSummaryMultiqc(workflow, summary_params) ch_workflow_summary = Channel.value(workflow_summary) From 8dc9e583add80f51111dc6c796a2ea1413e8731b Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 27 Mar 2022 09:30:23 +0200 Subject: [PATCH 056/306] Debugging run merging --- conf/modules.config | 6 ++--- .../nf-core/modules/kraken2/kraken2/main.nf | 3 ++- subworkflows/local/shortread_fastp.nf | 10 ++++--- subworkflows/local/shortread_preprocessing.nf | 7 ----- workflows/taxprofiler.nf | 27 ++++++++++++++----- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 5823d3f..41f471f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -65,7 +65,7 @@ process { withName: FASTP { ext.prefix = { "${meta.id}_${meta.run_accession}" } ext.args = [ - // collapsing options + // collapsing options - option to retain singletons params.shortread_clipmerge_excludeunmerged ? '' : "--include_unmerged", // trimming options params.shortread_clipmerge_skiptrim ? "--disable_adapter_trimming" : "", @@ -105,7 +105,7 @@ process { pattern: '*.{rma6,tab,text,sam,log}' ] ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = { "${meta.id}-${meta.db_name}" } } withName: KRAKEN2_KRAKEN2 { @@ -115,7 +115,7 @@ process { pattern: '*.{fastq.gz,txt}' ] ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = { "${meta.id}-${meta.db_name}" } } withName: CUSTOM_DUMPSOFTWAREVERSIONS { diff --git a/modules/nf-core/modules/kraken2/kraken2/main.nf b/modules/nf-core/modules/kraken2/kraken2/main.nf index 3ec5df5..52351f5 100644 --- a/modules/nf-core/modules/kraken2/kraken2/main.nf +++ b/modules/nf-core/modules/kraken2/kraken2/main.nf @@ -32,11 +32,12 @@ process KRAKEN2_KRAKEN2 { --threads $task.cpus \\ --unclassified-out $unclassified \\ --classified-out $classified \\ + $args \\ --report ${prefix}.kraken2.report.txt \\ --gzip-compressed \\ $paired \\ - $args \\ $reads + pigz -p $task.cpus *.fastq diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index 57ce2a6..d4d706e 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -7,7 +7,7 @@ include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fast workflow SHORTREAD_FASTP { take: - reads // file: /path/to/samplesheet.csv + reads // [[meta], [reads]] main: ch_versions = Channel.empty() @@ -24,16 +24,18 @@ workflow SHORTREAD_FASTP { ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) + // Last parameter here turns on merging of PE data FASTP_PAIRED ( ch_input_for_fastp.paired, false, params.shortread_clipmerge_mergepairs ) if ( params.shortread_clipmerge_mergepairs ) { + // TODO update to replace meta suffix ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged .mix( FASTP_SINGLE.out.reads ) .map { meta, reads -> - def meta_new = meta.clone() - meta_new['single_end'] = 1 - [ meta_new, reads ] + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] } } else { ch_fastp_reads_prepped = FASTP_PAIRED.out.reads diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index c82536f..7fba0c0 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -14,13 +14,6 @@ workflow SHORTREAD_PREPROCESSING { ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() - // - // STEP: Read clipping and merging - // - // TODO give option to clip only and retain pairs - // TODO give option to retain singletons (probably fastp option likely) - // TODO move to subworkflow - if ( params.shortread_clipmerge_tool == "fastp" ) { ch_processed_reads = SHORTREAD_FASTP ( reads ).reads ch_versions = ch_versions.mix( SHORTREAD_FASTP.out.versions ) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index ee921ea..87f7a30 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -120,25 +120,40 @@ workflow TAXPROFILER { MODULE: PERFORM SHORT READ RUN MERGING */ - // TODO: Check not necessary for long reads too? - // TODO: source of clash - combined should only occur when - // files ARE to be combined. SE/unmerged (see not below) + // Remove run accession to allow grouping by sample. Will only merge + // if pairment type is the same. + + // TODO Current Branch system currently problematic - when single file not in a list, splits at + // `/` so makes list >= 2, so tries to merge, but then breaks kraken downstream + // e.g. `home jfellows Documents git nf-core taxprofiler testing work 68 9a2c8362add37832a776058d280bb7 2612_se.merged.fastq.gz` + // So theoretically need to force this into a list, (but results the can't access meta.id error as incorrect input format) + // But second issue >= 2 is MAYBE sufficient because what if merging two paired-end files? Need to chcek if the input channel formatted correctly for this? Need to check... ch_processed_for_combine = ch_shortreads_preprocessed .dump(tag: "prep_for_combine_grouping") .map { meta, reads -> def meta_new = meta.clone() - //meta_new['run_accession'] = 'combined' + + // remove run accession to allow group by sample + meta_new.remove('run_accession') + + // update id to prevent file name clashes when unable to group + // unmerged PE and SE runs of same sample + def type = meta_new['single_end'] ? "_se" : "_pe" + meta_new['id'] = meta['id'] + type + [ meta_new, reads ] } .groupTuple ( by: 0 ) + .dump(tag: "files_for_cat_fastq_branch") .branch{ - combine: it[1].size() >= 2 - skip: it[1].size() < 2 + combine: it[1] && it[1].size() > 1 + skip: true } // NOTE: this does not allow CATing of SE & PE runs of same sample // when --shortread_clipmerge_mergepairs is false + ch_processed_for_combine.combine.dump(tag: "input_into_cat_fastq") CAT_FASTQ ( ch_processed_for_combine.combine ) ch_reads_for_profiling = ch_processed_for_combine.skip From ee53283696261fa2687fa55402e3cfa3da164509 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 28 Mar 2022 09:13:05 +0200 Subject: [PATCH 057/306] Debugging comment --- workflows/taxprofiler.nf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 87f7a30..8a05720 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -118,7 +118,6 @@ workflow TAXPROFILER { /* MODULE: PERFORM SHORT READ RUN MERGING - */ // Remove run accession to allow grouping by sample. Will only merge // if pairment type is the same. @@ -128,6 +127,7 @@ workflow TAXPROFILER { // e.g. `home jfellows Documents git nf-core taxprofiler testing work 68 9a2c8362add37832a776058d280bb7 2612_se.merged.fastq.gz` // So theoretically need to force this into a list, (but results the can't access meta.id error as incorrect input format) // But second issue >= 2 is MAYBE sufficient because what if merging two paired-end files? Need to chcek if the input channel formatted correctly for this? Need to check... + ch_processed_for_combine = ch_shortreads_preprocessed .dump(tag: "prep_for_combine_grouping") .map { @@ -160,6 +160,9 @@ workflow TAXPROFILER { .dump(tag: "skip_combine") .mix( CAT_FASTQ.out.reads ) .dump(tag: "files_for_profiling") + */ + + ch_reads_for_profiling = ch_shortreads_preprocessed /* COMBINE READS WITH POSSIBLE DATABASES @@ -198,6 +201,7 @@ workflow TAXPROFILER { } // We can run Kraken2 one-by-one sample-wise + // TODO Only flatten when paired-end! Causing issue commented out above! ch_input_for_kraken2 = ch_input_for_profiling.kraken2 .dump(tag: "input_to_kraken") .multiMap { From 0e0e8128e868df6ea8a4f4c22d7841f518d84c9f Mon Sep 17 00:00:00 2001 From: sofstam Date: Mon, 28 Mar 2022 13:43:32 +0200 Subject: [PATCH 058/306] Prettier format --- nextflow_schema.json | 579 +++++++++++++++++++++---------------------- 1 file changed, 288 insertions(+), 291 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index b4b3e07..b61a50e 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -1,308 +1,305 @@ { - "$schema": "http://json-schema.org/draft-07/schema", - "$id": "https://raw.githubusercontent.com/nf-core/taxprofiler/master/nextflow_schema.json", - "title": "nf-core/taxprofiler pipeline parameters", - "description": "Taxonomic profiling of shotgun metagenomic data", - "type": "object", - "definitions": { - "input_output_options": { - "title": "Input/output options", - "type": "object", - "fa_icon": "fas fa-terminal", - "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], - "properties": { - "input": { - "type": "string", - "format": "file-path", - "mimetype": "text/csv", - "pattern": "^\\S+\\.csv$", - "schema": "assets/schema_input.json", - "description": "Path to comma-separated file containing information about the samples in the experiment.", - "help_text": "You will need to create a design file with information about the samples in your experiment before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row. See [usage docs](https://nf-co.re/taxprofiler/usage#samplesheet-input).", - "fa_icon": "fas fa-file-csv" - }, - "outdir": { - "type": "string", - "format": "directory-path", - "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" - }, - "email": { - "type": "string", - "description": "Email address for completion summary.", - "fa_icon": "fas fa-envelope", - "help_text": "Set this parameter to your e-mail address to get a summary e-mail with details of the run sent to you when the workflow exits. If set in your user config file (`~/.nextflow/config`) then you don't need to specify this on the command line for every run.", - "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$" - }, - "multiqc_title": { - "type": "string", - "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified.", - "fa_icon": "fas fa-file-signature" - } - } + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://raw.githubusercontent.com/nf-core/taxprofiler/master/nextflow_schema.json", + "title": "nf-core/taxprofiler pipeline parameters", + "description": "Taxonomic profiling of shotgun metagenomic data", + "type": "object", + "definitions": { + "input_output_options": { + "title": "Input/output options", + "type": "object", + "fa_icon": "fas fa-terminal", + "description": "Define where the pipeline should find input data and save output data.", + "required": ["input", "outdir"], + "properties": { + "input": { + "type": "string", + "format": "file-path", + "mimetype": "text/csv", + "pattern": "^\\S+\\.csv$", + "schema": "assets/schema_input.json", + "description": "Path to comma-separated file containing information about the samples in the experiment.", + "help_text": "You will need to create a design file with information about the samples in your experiment before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row. See [usage docs](https://nf-co.re/taxprofiler/usage#samplesheet-input).", + "fa_icon": "fas fa-file-csv" }, - "reference_genome_options": { - "title": "Reference genome options", - "type": "object", - "fa_icon": "fas fa-dna", - "description": "Reference genome related files and options required for the workflow.", - "properties": { - "genome": { - "type": "string", - "description": "Name of iGenomes reference.", - "fa_icon": "fas fa-book", - "help_text": "If using a reference genome configured in the pipeline using iGenomes, use this parameter to give the ID for the reference. This is then used to build the full paths for all required reference genome files e.g. `--genome GRCh38`. \n\nSee the [nf-core website docs](https://nf-co.re/usage/reference_genomes) for more details." - }, - "igenomes_base": { - "type": "string", - "format": "directory-path", - "description": "Directory / URL base for iGenomes references.", - "default": "s3://ngi-igenomes/igenomes", - "fa_icon": "fas fa-cloud-download-alt", - "hidden": true - }, - "igenomes_ignore": { - "type": "boolean", - "description": "Do not load the iGenomes reference config.", - "fa_icon": "fas fa-ban", - "hidden": true, - "help_text": "Do not load `igenomes.config` when running the pipeline. You may choose this option if you observe clashes between custom parameters and those supplied in `igenomes.config`." - } - } + "outdir": { + "type": "string", + "format": "directory-path", + "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" }, - "institutional_config_options": { - "title": "Institutional config options", - "type": "object", - "fa_icon": "fas fa-university", - "description": "Parameters used to describe centralised config profiles. These should not be edited.", - "help_text": "The centralised nf-core configuration profiles use a handful of pipeline parameters to describe themselves. This information is then printed to the Nextflow log when you run a pipeline. You should not need to change these values when you run a pipeline.", - "properties": { - "custom_config_version": { - "type": "string", - "description": "Git commit id for Institutional configs.", - "default": "master", - "hidden": true, - "fa_icon": "fas fa-users-cog" - }, - "custom_config_base": { - "type": "string", - "description": "Base directory for Institutional configs.", - "default": "https://raw.githubusercontent.com/nf-core/configs/master", - "hidden": true, - "help_text": "If you're running offline, Nextflow will not be able to fetch the institutional config files from the internet. If you don't need them, then this is not a problem. If you do need them, you should download the files from the repo and tell Nextflow where to find them with this parameter.", - "fa_icon": "fas fa-users-cog" - }, - "config_profile_name": { - "type": "string", - "description": "Institutional config name.", - "hidden": true, - "fa_icon": "fas fa-users-cog" - }, - "config_profile_description": { - "type": "string", - "description": "Institutional config description.", - "hidden": true, - "fa_icon": "fas fa-users-cog" - }, - "config_profile_contact": { - "type": "string", - "description": "Institutional config contact information.", - "hidden": true, - "fa_icon": "fas fa-users-cog" - }, - "config_profile_url": { - "type": "string", - "description": "Institutional config URL link.", - "hidden": true, - "fa_icon": "fas fa-users-cog" - } - } + "email": { + "type": "string", + "description": "Email address for completion summary.", + "fa_icon": "fas fa-envelope", + "help_text": "Set this parameter to your e-mail address to get a summary e-mail with details of the run sent to you when the workflow exits. If set in your user config file (`~/.nextflow/config`) then you don't need to specify this on the command line for every run.", + "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$" }, - "max_job_request_options": { - "title": "Max job request options", - "type": "object", - "fa_icon": "fab fa-acquisitions-incorporated", - "description": "Set the top limit for requested resources for any single job.", - "help_text": "If you are running on a smaller system, a pipeline step requesting more resources than are available may cause the Nextflow to stop the run with an error. These options allow you to cap the maximum resources requested by any single job so that the pipeline will run on your system.\n\nNote that you can not _increase_ the resources requested by any job using these options. For that you will need your own configuration file. See [the nf-core website](https://nf-co.re/usage/configuration) for details.", - "properties": { - "max_cpus": { - "type": "integer", - "description": "Maximum number of CPUs that can be requested for any single job.", - "default": 16, - "fa_icon": "fas fa-microchip", - "hidden": true, - "help_text": "Use to set an upper-limit for the CPU requirement for each process. Should be an integer e.g. `--max_cpus 1`" - }, - "max_memory": { - "type": "string", - "description": "Maximum amount of memory that can be requested for any single job.", - "default": "128.GB", - "fa_icon": "fas fa-memory", - "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", - "hidden": true, - "help_text": "Use to set an upper-limit for the memory requirement for each process. Should be a string in the format integer-unit e.g. `--max_memory '8.GB'`" - }, - "max_time": { - "type": "string", - "description": "Maximum amount of time that can be requested for any single job.", - "default": "240.h", - "fa_icon": "far fa-clock", - "pattern": "^(\\d+\\.?\\s*(s|m|h|day)\\s*)+$", - "hidden": true, - "help_text": "Use to set an upper-limit for the time requirement for each process. Should be a string in the format integer-unit e.g. `--max_time '2.h'`" - } - } - }, - "generic_options": { - "title": "Generic options", - "type": "object", - "fa_icon": "fas fa-file-import", - "description": "Less common options for the pipeline, typically set in a config file.", - "help_text": "These options are common to all nf-core pipelines and allow you to customise some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.", - "properties": { - "help": { - "type": "boolean", - "description": "Display help text.", - "fa_icon": "fas fa-question-circle", - "hidden": true - }, - "publish_dir_mode": { - "type": "string", - "default": "copy", - "description": "Method used to save pipeline results to output directory.", - "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", - "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], - "hidden": true - }, - "email_on_fail": { - "type": "string", - "description": "Email address for completion summary, only when pipeline fails.", - "fa_icon": "fas fa-exclamation-triangle", - "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$", - "help_text": "An email address to send a summary email to when the pipeline is completed - ONLY sent if the pipeline does not exit successfully.", - "hidden": true - }, - "plaintext_email": { - "type": "boolean", - "description": "Send plain-text email instead of HTML.", - "fa_icon": "fas fa-remove-format", - "hidden": true - }, - "max_multiqc_email_size": { - "type": "string", - "description": "File size limit when attaching MultiQC reports to summary emails.", - "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", - "default": "25.MB", - "fa_icon": "fas fa-file-upload", - "hidden": true - }, - "monochrome_logs": { - "type": "boolean", - "description": "Do not use coloured log outputs.", - "fa_icon": "fas fa-palette", - "hidden": true - }, - "multiqc_config": { - "type": "string", - "description": "Custom config file to supply to MultiQC.", - "fa_icon": "fas fa-cog", - "hidden": true - }, - "tracedir": { - "type": "string", - "description": "Directory to keep pipeline Nextflow logs and reports.", - "default": "${params.outdir}/pipeline_info", - "fa_icon": "fas fa-cogs", - "hidden": true - }, - "validate_params": { - "type": "boolean", - "description": "Boolean whether to validate parameters against the schema at runtime", - "default": true, - "fa_icon": "fas fa-check-square", - "hidden": true - }, - "show_hidden_params": { - "type": "boolean", - "fa_icon": "far fa-eye-slash", - "description": "Show all params when using `--help`", - "hidden": true, - "help_text": "By default, parameters set as _hidden_ in the schema are not shown on the command line when a user runs with `--help`. Specifying this option will tell the pipeline to show all parameters." - }, - "enable_conda": { - "type": "boolean", - "description": "Run this workflow with Conda. You can also use '-profile conda' instead of providing this parameter.", - "hidden": true, - "fa_icon": "fas fa-bacon" - } - } + "multiqc_title": { + "type": "string", + "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified.", + "fa_icon": "fas fa-file-signature" } + } }, - "allOf": [ - { - "$ref": "#/definitions/input_output_options" + "reference_genome_options": { + "title": "Reference genome options", + "type": "object", + "fa_icon": "fas fa-dna", + "description": "Reference genome related files and options required for the workflow.", + "properties": { + "genome": { + "type": "string", + "description": "Name of iGenomes reference.", + "fa_icon": "fas fa-book", + "help_text": "If using a reference genome configured in the pipeline using iGenomes, use this parameter to give the ID for the reference. This is then used to build the full paths for all required reference genome files e.g. `--genome GRCh38`. \n\nSee the [nf-core website docs](https://nf-co.re/usage/reference_genomes) for more details." }, - { - "$ref": "#/definitions/reference_genome_options" + "igenomes_base": { + "type": "string", + "format": "directory-path", + "description": "Directory / URL base for iGenomes references.", + "default": "s3://ngi-igenomes/igenomes", + "fa_icon": "fas fa-cloud-download-alt", + "hidden": true }, - { - "$ref": "#/definitions/institutional_config_options" - }, - { - "$ref": "#/definitions/max_job_request_options" - }, - { - "$ref": "#/definitions/generic_options" + "igenomes_ignore": { + "type": "boolean", + "description": "Do not load the iGenomes reference config.", + "fa_icon": "fas fa-ban", + "hidden": true, + "help_text": "Do not load `igenomes.config` when running the pipeline. You may choose this option if you observe clashes between custom parameters and those supplied in `igenomes.config`." } - ], - "properties": { - "databases": { - "type": "string", - "default": "None" + } + }, + "institutional_config_options": { + "title": "Institutional config options", + "type": "object", + "fa_icon": "fas fa-university", + "description": "Parameters used to describe centralised config profiles. These should not be edited.", + "help_text": "The centralised nf-core configuration profiles use a handful of pipeline parameters to describe themselves. This information is then printed to the Nextflow log when you run a pipeline. You should not need to change these values when you run a pipeline.", + "properties": { + "custom_config_version": { + "type": "string", + "description": "Git commit id for Institutional configs.", + "default": "master", + "hidden": true, + "fa_icon": "fas fa-users-cog" }, - "shortread_clipmerge": { - "type": "boolean" + "custom_config_base": { + "type": "string", + "description": "Base directory for Institutional configs.", + "default": "https://raw.githubusercontent.com/nf-core/configs/master", + "hidden": true, + "help_text": "If you're running offline, Nextflow will not be able to fetch the institutional config files from the internet. If you don't need them, then this is not a problem. If you do need them, you should download the files from the repo and tell Nextflow where to find them with this parameter.", + "fa_icon": "fas fa-users-cog" }, - "shortread_excludeunmerged": { - "type": "boolean", - "default": true + "config_profile_name": { + "type": "string", + "description": "Institutional config name.", + "hidden": true, + "fa_icon": "fas fa-users-cog" }, - "longread_clip": { - "type": "boolean" + "config_profile_description": { + "type": "string", + "description": "Institutional config description.", + "hidden": true, + "fa_icon": "fas fa-users-cog" }, - "run_malt": { - "type": "boolean" + "config_profile_contact": { + "type": "string", + "description": "Institutional config contact information.", + "hidden": true, + "fa_icon": "fas fa-users-cog" }, - "malt_mode": { - "type": "string", - "default": "BlastN" - }, - "run_kraken2": { - "type": "boolean" - }, - "run_centrifuge": { - "type": "boolean" - }, - "centrifuge_save_unaligned": { - "type": "boolean" - }, - "centrifuge_save_aligned": { - "type": "boolean" - }, - "centrifuge_sam_format": { - "type": "boolean" + "config_profile_url": { + "type": "string", + "description": "Institutional config URL link.", + "hidden": true, + "fa_icon": "fas fa-users-cog" } + } + }, + "max_job_request_options": { + "title": "Max job request options", + "type": "object", + "fa_icon": "fab fa-acquisitions-incorporated", + "description": "Set the top limit for requested resources for any single job.", + "help_text": "If you are running on a smaller system, a pipeline step requesting more resources than are available may cause the Nextflow to stop the run with an error. These options allow you to cap the maximum resources requested by any single job so that the pipeline will run on your system.\n\nNote that you can not _increase_ the resources requested by any job using these options. For that you will need your own configuration file. See [the nf-core website](https://nf-co.re/usage/configuration) for details.", + "properties": { + "max_cpus": { + "type": "integer", + "description": "Maximum number of CPUs that can be requested for any single job.", + "default": 16, + "fa_icon": "fas fa-microchip", + "hidden": true, + "help_text": "Use to set an upper-limit for the CPU requirement for each process. Should be an integer e.g. `--max_cpus 1`" + }, + "max_memory": { + "type": "string", + "description": "Maximum amount of memory that can be requested for any single job.", + "default": "128.GB", + "fa_icon": "fas fa-memory", + "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", + "hidden": true, + "help_text": "Use to set an upper-limit for the memory requirement for each process. Should be a string in the format integer-unit e.g. `--max_memory '8.GB'`" + }, + "max_time": { + "type": "string", + "description": "Maximum amount of time that can be requested for any single job.", + "default": "240.h", + "fa_icon": "far fa-clock", + "pattern": "^(\\d+\\.?\\s*(s|m|h|day)\\s*)+$", + "hidden": true, + "help_text": "Use to set an upper-limit for the time requirement for each process. Should be a string in the format integer-unit e.g. `--max_time '2.h'`" + } + } + }, + "generic_options": { + "title": "Generic options", + "type": "object", + "fa_icon": "fas fa-file-import", + "description": "Less common options for the pipeline, typically set in a config file.", + "help_text": "These options are common to all nf-core pipelines and allow you to customise some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.", + "properties": { + "help": { + "type": "boolean", + "description": "Display help text.", + "fa_icon": "fas fa-question-circle", + "hidden": true + }, + "publish_dir_mode": { + "type": "string", + "default": "copy", + "description": "Method used to save pipeline results to output directory.", + "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", + "fa_icon": "fas fa-copy", + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], + "hidden": true + }, + "email_on_fail": { + "type": "string", + "description": "Email address for completion summary, only when pipeline fails.", + "fa_icon": "fas fa-exclamation-triangle", + "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$", + "help_text": "An email address to send a summary email to when the pipeline is completed - ONLY sent if the pipeline does not exit successfully.", + "hidden": true + }, + "plaintext_email": { + "type": "boolean", + "description": "Send plain-text email instead of HTML.", + "fa_icon": "fas fa-remove-format", + "hidden": true + }, + "max_multiqc_email_size": { + "type": "string", + "description": "File size limit when attaching MultiQC reports to summary emails.", + "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", + "default": "25.MB", + "fa_icon": "fas fa-file-upload", + "hidden": true + }, + "monochrome_logs": { + "type": "boolean", + "description": "Do not use coloured log outputs.", + "fa_icon": "fas fa-palette", + "hidden": true + }, + "multiqc_config": { + "type": "string", + "description": "Custom config file to supply to MultiQC.", + "fa_icon": "fas fa-cog", + "hidden": true + }, + "tracedir": { + "type": "string", + "description": "Directory to keep pipeline Nextflow logs and reports.", + "default": "${params.outdir}/pipeline_info", + "fa_icon": "fas fa-cogs", + "hidden": true + }, + "validate_params": { + "type": "boolean", + "description": "Boolean whether to validate parameters against the schema at runtime", + "default": true, + "fa_icon": "fas fa-check-square", + "hidden": true + }, + "show_hidden_params": { + "type": "boolean", + "fa_icon": "far fa-eye-slash", + "description": "Show all params when using `--help`", + "hidden": true, + "help_text": "By default, parameters set as _hidden_ in the schema are not shown on the command line when a user runs with `--help`. Specifying this option will tell the pipeline to show all parameters." + }, + "enable_conda": { + "type": "boolean", + "description": "Run this workflow with Conda. You can also use '-profile conda' instead of providing this parameter.", + "hidden": true, + "fa_icon": "fas fa-bacon" + } + } } + }, + "allOf": [ + { + "$ref": "#/definitions/input_output_options" + }, + { + "$ref": "#/definitions/reference_genome_options" + }, + { + "$ref": "#/definitions/institutional_config_options" + }, + { + "$ref": "#/definitions/max_job_request_options" + }, + { + "$ref": "#/definitions/generic_options" + } + ], + "properties": { + "databases": { + "type": "string", + "default": "None" + }, + "shortread_clipmerge": { + "type": "boolean" + }, + "shortread_excludeunmerged": { + "type": "boolean", + "default": true + }, + "longread_clip": { + "type": "boolean" + }, + "run_malt": { + "type": "boolean" + }, + "malt_mode": { + "type": "string", + "default": "BlastN" + }, + "run_kraken2": { + "type": "boolean" + }, + "run_centrifuge": { + "type": "boolean" + }, + "centrifuge_save_unaligned": { + "type": "boolean" + }, + "centrifuge_save_aligned": { + "type": "boolean" + }, + "centrifuge_sam_format": { + "type": "boolean" + } + } } From 231253227c7e59c1c108cf4cf4953cb89c4f6f5f Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 28 Mar 2022 16:38:10 +0200 Subject: [PATCH 059/306] Remove run merging for now due to complexity --- conf/modules.config | 4 +- subworkflows/local/shortread_fastp.nf | 17 +++++---- workflows/taxprofiler.nf | 55 ++------------------------- 3 files changed, 14 insertions(+), 62 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 41f471f..a9bc3a1 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -105,7 +105,7 @@ process { pattern: '*.{rma6,tab,text,sam,log}' ] ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.db_name}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: KRAKEN2_KRAKEN2 { @@ -115,7 +115,7 @@ process { pattern: '*.{fastq.gz,txt}' ] ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.db_name}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: CUSTOM_DUMPSOFTWAREVERSIONS { diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index d4d706e..c2e435c 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -28,15 +28,16 @@ workflow SHORTREAD_FASTP { FASTP_PAIRED ( ch_input_for_fastp.paired, false, params.shortread_clipmerge_mergepairs ) if ( params.shortread_clipmerge_mergepairs ) { - // TODO update to replace meta suffix - ch_fastp_reads_prepped = FASTP_PAIRED.out.reads_merged - .mix( FASTP_SINGLE.out.reads ) - .map { - meta, reads -> - def meta_new = meta.clone() - meta_new['single_end'] = 1 - [ meta_new, reads ] + ch_fastp_reads_prepped_pe = FASTP_PAIRED.out.reads_merged + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] } + + ch_fastp_reads_prepped = ch_fastp_reads_prepped_pe.mix( FASTP_SINGLE.out.reads ) + } else { ch_fastp_reads_prepped = FASTP_PAIRED.out.reads .mix( FASTP_SINGLE.out.reads ) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 8a05720..e73d94d 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -116,63 +116,15 @@ workflow TAXPROFILER { ch_longreads_preprocessed = INPUT_CHECK.out.nanopore } - /* - MODULE: PERFORM SHORT READ RUN MERGING - - // Remove run accession to allow grouping by sample. Will only merge - // if pairment type is the same. - - // TODO Current Branch system currently problematic - when single file not in a list, splits at - // `/` so makes list >= 2, so tries to merge, but then breaks kraken downstream - // e.g. `home jfellows Documents git nf-core taxprofiler testing work 68 9a2c8362add37832a776058d280bb7 2612_se.merged.fastq.gz` - // So theoretically need to force this into a list, (but results the can't access meta.id error as incorrect input format) - // But second issue >= 2 is MAYBE sufficient because what if merging two paired-end files? Need to chcek if the input channel formatted correctly for this? Need to check... - - ch_processed_for_combine = ch_shortreads_preprocessed - .dump(tag: "prep_for_combine_grouping") - .map { - meta, reads -> - def meta_new = meta.clone() - - // remove run accession to allow group by sample - meta_new.remove('run_accession') - - // update id to prevent file name clashes when unable to group - // unmerged PE and SE runs of same sample - def type = meta_new['single_end'] ? "_se" : "_pe" - meta_new['id'] = meta['id'] + type - - [ meta_new, reads ] - } - .groupTuple ( by: 0 ) - .dump(tag: "files_for_cat_fastq_branch") - .branch{ - combine: it[1] && it[1].size() > 1 - skip: true - } - - // NOTE: this does not allow CATing of SE & PE runs of same sample - // when --shortread_clipmerge_mergepairs is false - ch_processed_for_combine.combine.dump(tag: "input_into_cat_fastq") - CAT_FASTQ ( ch_processed_for_combine.combine ) - - ch_reads_for_profiling = ch_processed_for_combine.skip - .dump(tag: "skip_combine") - .mix( CAT_FASTQ.out.reads ) - .dump(tag: "files_for_profiling") - */ - - ch_reads_for_profiling = ch_shortreads_preprocessed - /* COMBINE READS WITH POSSIBLE DATABASES */ // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] - ch_input_for_profiling = ch_reads_for_profiling + ch_input_for_profiling = ch_shortreads_preprocessed .mix( ch_longreads_preprocessed ) .combine(DB_CHECK.out.dbs) - .dump(tag: "reads_plus_db") + .dump(tag: "reads_plus_db_clean") .branch { malt: it[2]['tool'] == 'malt' kraken2: it[2]['tool'] == 'kraken2' @@ -201,12 +153,11 @@ workflow TAXPROFILER { } // We can run Kraken2 one-by-one sample-wise - // TODO Only flatten when paired-end! Causing issue commented out above! ch_input_for_kraken2 = ch_input_for_profiling.kraken2 .dump(tag: "input_to_kraken") .multiMap { it -> - reads: [ it[0] + it[2], it[1].flatten() ] + reads: [ it[0] + it[2], it[1] ] db: it[3] } From 98dc8014a526209b24bdd55e321c10b2dc007a88 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 28 Mar 2022 16:46:45 +0200 Subject: [PATCH 060/306] Fix kraken2 module --- modules.json | 8 ++++---- modules/nf-core/modules/kraken2/kraken2/main.nf | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/modules.json b/modules.json index 9e6428a..2494fc1 100644 --- a/modules.json +++ b/modules.json @@ -24,12 +24,12 @@ "multiqc": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, - "untar": { - "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" - }, "porechop": { "git_sha": "e20e57f90b6787ac9a010a980cf6ea98bd990046" + }, + "untar": { + "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/kraken2/kraken2/main.nf b/modules/nf-core/modules/kraken2/kraken2/main.nf index 52351f5..3ec5df5 100644 --- a/modules/nf-core/modules/kraken2/kraken2/main.nf +++ b/modules/nf-core/modules/kraken2/kraken2/main.nf @@ -32,12 +32,11 @@ process KRAKEN2_KRAKEN2 { --threads $task.cpus \\ --unclassified-out $unclassified \\ --classified-out $classified \\ - $args \\ --report ${prefix}.kraken2.report.txt \\ --gzip-compressed \\ $paired \\ + $args \\ $reads - pigz -p $task.cpus *.fastq From eada201eb28f3572af5481a6d69e4f87407a352d Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 28 Mar 2022 16:47:37 +0200 Subject: [PATCH 061/306] Prettier --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 2494fc1..34ef9f1 100644 --- a/modules.json +++ b/modules.json @@ -32,4 +32,4 @@ } } } -} \ No newline at end of file +} From 94e5cfef4aa7c2dd69e72e7b1b12439110ed1a09 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 28 Mar 2022 18:20:10 +0200 Subject: [PATCH 062/306] Filter long reads for MALT, bump cpus for FastQC for minigut to pass --- conf/base.config | 2 +- conf/test.config | 15 ++++++++++++++- subworkflows/local/db_check.nf | 2 +- workflows/taxprofiler.nf | 6 ++++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/conf/base.config b/conf/base.config index 0c65574..2f0f97f 100644 --- a/conf/base.config +++ b/conf/base.config @@ -27,7 +27,7 @@ process { // TODO nf-core: Customise requirements for specific processes. // See https://www.nextflow.io/docs/latest/config.html#config-process-selectors withLabel:process_low { - cpus = { check_max( 2 * task.attempt, 'cpus' ) } + cpus = { check_max( 4 * task.attempt, 'cpus' ) } memory = { check_max( 12.GB * task.attempt, 'memory' ) } time = { check_max( 4.h * task.attempt, 'time' ) } } diff --git a/conf/test.config b/conf/test.config index 92a10e4..26972df 100644 --- a/conf/test.config +++ b/conf/test.config @@ -15,7 +15,7 @@ params { config_profile_description = 'Minimal test dataset to check pipeline function' // Limit resources so that this can run on GitHub Actions - max_cpus = 2 + max_cpus = 8 max_memory = '6.GB' max_time = '6.h' @@ -29,3 +29,16 @@ params { shortread_clipmerge = true } + +process { + withName: FASTQC { + cpus = { check_max( 8 * task.attempt, 'cpus' ) } + memory = { check_max( 6.GB * task.attempt, 'memory' ) } + time = { check_max( 6.h * task.attempt, 'time' ) } + } + withName: FASTQC_PROCESSED { + cpus = { check_max( 8 * task.attempt, 'cpus' ) } + memory = { check_max( 6.GB * task.attempt, 'memory' ) } + time = { check_max( 6.h * task.attempt, 'time' ) } + } +} diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index 96f815a..bda5cfe 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -14,7 +14,7 @@ workflow DB_CHECK { // TODO: make database sheet check // Checks: // 1) no duplicates, - // 2) dbs with no special arguments does not have quotes, e.g. just `,,` and NOT `,"",` + // 2) args do not have quotes, e.g. just `,,` and NOT `,"",` parsed_samplesheet = DATABASE_CHECK ( dbsheet ) .csv .splitCsv ( header:true, sep:',' ) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index e73d94d..af5c54d 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -90,9 +90,11 @@ workflow TAXPROFILER { MODULE: Run FastQC */ ch_input_for_fastqc = INPUT_CHECK.out.fastq.mix( INPUT_CHECK.out.nanopore ).dump(tag: "input_to_fastq") + FASTQC ( ch_input_for_fastqc ) + ch_versions = ch_versions.mix(FASTQC.out.versions.first()) CUSTOM_DUMPSOFTWAREVERSIONS ( @@ -137,7 +139,11 @@ workflow TAXPROFILER { // We groupTuple to have all samples in one channel for MALT as database // loading takes a long time, so we only want to run it once per database + // TODO document somewhere we only accept illumina short reads for MALT? ch_input_for_malt = ch_input_for_profiling.malt + .dump(tag: "input_to_malt_prefilter") + .filter { it[0]['instrument_platform'] == 'ILLUMINA' } + .dump(tag: "input_to_malt_postfilter") .map { it -> def temp_meta = [ id: it[2]['db_name']] + it[2] From 04fb6f412752811ac8fc621c8730d418986a1d08 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 28 Mar 2022 20:38:16 +0200 Subject: [PATCH 063/306] Use new resources for test data --- conf/test.config | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/conf/test.config b/conf/test.config index 26972df..92a10e4 100644 --- a/conf/test.config +++ b/conf/test.config @@ -15,7 +15,7 @@ params { config_profile_description = 'Minimal test dataset to check pipeline function' // Limit resources so that this can run on GitHub Actions - max_cpus = 8 + max_cpus = 2 max_memory = '6.GB' max_time = '6.h' @@ -29,16 +29,3 @@ params { shortread_clipmerge = true } - -process { - withName: FASTQC { - cpus = { check_max( 8 * task.attempt, 'cpus' ) } - memory = { check_max( 6.GB * task.attempt, 'memory' ) } - time = { check_max( 6.h * task.attempt, 'time' ) } - } - withName: FASTQC_PROCESSED { - cpus = { check_max( 8 * task.attempt, 'cpus' ) } - memory = { check_max( 6.GB * task.attempt, 'memory' ) } - time = { check_max( 6.h * task.attempt, 'time' ) } - } -} From d25f97c5061ef7759196f04017ec2b7710075e48 Mon Sep 17 00:00:00 2001 From: sofstam Date: Mon, 28 Mar 2022 22:08:05 +0200 Subject: [PATCH 064/306] Prettier format --- nextflow_schema.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index b61a50e..f1b1de0 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", From c552819c725a51fe2f04af981ebd90c311b33c30 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Mon, 28 Mar 2022 22:15:31 +0200 Subject: [PATCH 065/306] Apply prettier again --- nextflow_schema.json | 572 +++++++++++++++++++++---------------------- 1 file changed, 281 insertions(+), 291 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index f1b1de0..0e52ee5 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -1,308 +1,298 @@ { - "$schema": "http://json-schema.org/draft-07/schema", - "$id": "https://raw.githubusercontent.com/nf-core/taxprofiler/master/nextflow_schema.json", - "title": "nf-core/taxprofiler pipeline parameters", - "description": "Taxonomic profiling of shotgun metagenomic data", - "type": "object", - "definitions": { - "input_output_options": { - "title": "Input/output options", - "type": "object", - "fa_icon": "fas fa-terminal", - "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], - "properties": { - "input": { - "type": "string", - "format": "file-path", - "mimetype": "text/csv", - "pattern": "^\\S+\\.csv$", - "schema": "assets/schema_input.json", - "description": "Path to comma-separated file containing information about the samples in the experiment.", - "help_text": "You will need to create a design file with information about the samples in your experiment before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row. See [usage docs](https://nf-co.re/taxprofiler/usage#samplesheet-input).", - "fa_icon": "fas fa-file-csv" + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://raw.githubusercontent.com/nf-core/taxprofiler/master/nextflow_schema.json", + "title": "nf-core/taxprofiler pipeline parameters", + "description": "Taxonomic profiling of shotgun metagenomic data", + "type": "object", + "definitions": { + "input_output_options": { + "title": "Input/output options", + "type": "object", + "fa_icon": "fas fa-terminal", + "description": "Define where the pipeline should find input data and save output data.", + "required": ["input", "outdir"], + "properties": { + "input": { + "type": "string", + "format": "file-path", + "mimetype": "text/csv", + "pattern": "^\\S+\\.csv$", + "schema": "assets/schema_input.json", + "description": "Path to comma-separated file containing information about the samples in the experiment.", + "help_text": "You will need to create a design file with information about the samples in your experiment before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row. See [usage docs](https://nf-co.re/taxprofiler/usage#samplesheet-input).", + "fa_icon": "fas fa-file-csv" + }, + "outdir": { + "type": "string", + "format": "directory-path", + "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" + }, + "email": { + "type": "string", + "description": "Email address for completion summary.", + "fa_icon": "fas fa-envelope", + "help_text": "Set this parameter to your e-mail address to get a summary e-mail with details of the run sent to you when the workflow exits. If set in your user config file (`~/.nextflow/config`) then you don't need to specify this on the command line for every run.", + "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$" + }, + "multiqc_title": { + "type": "string", + "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified.", + "fa_icon": "fas fa-file-signature" + } + } }, - "outdir": { - "type": "string", - "format": "directory-path", - "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" + "reference_genome_options": { + "title": "Reference genome options", + "type": "object", + "fa_icon": "fas fa-dna", + "description": "Reference genome related files and options required for the workflow.", + "properties": { + "genome": { + "type": "string", + "description": "Name of iGenomes reference.", + "fa_icon": "fas fa-book", + "help_text": "If using a reference genome configured in the pipeline using iGenomes, use this parameter to give the ID for the reference. This is then used to build the full paths for all required reference genome files e.g. `--genome GRCh38`. \n\nSee the [nf-core website docs](https://nf-co.re/usage/reference_genomes) for more details." + }, + "igenomes_base": { + "type": "string", + "format": "directory-path", + "description": "Directory / URL base for iGenomes references.", + "default": "s3://ngi-igenomes/igenomes", + "fa_icon": "fas fa-cloud-download-alt", + "hidden": true + }, + "igenomes_ignore": { + "type": "boolean", + "description": "Do not load the iGenomes reference config.", + "fa_icon": "fas fa-ban", + "hidden": true, + "help_text": "Do not load `igenomes.config` when running the pipeline. You may choose this option if you observe clashes between custom parameters and those supplied in `igenomes.config`." + } + } }, - "email": { - "type": "string", - "description": "Email address for completion summary.", - "fa_icon": "fas fa-envelope", - "help_text": "Set this parameter to your e-mail address to get a summary e-mail with details of the run sent to you when the workflow exits. If set in your user config file (`~/.nextflow/config`) then you don't need to specify this on the command line for every run.", - "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$" + "institutional_config_options": { + "title": "Institutional config options", + "type": "object", + "fa_icon": "fas fa-university", + "description": "Parameters used to describe centralised config profiles. These should not be edited.", + "help_text": "The centralised nf-core configuration profiles use a handful of pipeline parameters to describe themselves. This information is then printed to the Nextflow log when you run a pipeline. You should not need to change these values when you run a pipeline.", + "properties": { + "custom_config_version": { + "type": "string", + "description": "Git commit id for Institutional configs.", + "default": "master", + "hidden": true, + "fa_icon": "fas fa-users-cog" + }, + "custom_config_base": { + "type": "string", + "description": "Base directory for Institutional configs.", + "default": "https://raw.githubusercontent.com/nf-core/configs/master", + "hidden": true, + "help_text": "If you're running offline, Nextflow will not be able to fetch the institutional config files from the internet. If you don't need them, then this is not a problem. If you do need them, you should download the files from the repo and tell Nextflow where to find them with this parameter.", + "fa_icon": "fas fa-users-cog" + }, + "config_profile_name": { + "type": "string", + "description": "Institutional config name.", + "hidden": true, + "fa_icon": "fas fa-users-cog" + }, + "config_profile_description": { + "type": "string", + "description": "Institutional config description.", + "hidden": true, + "fa_icon": "fas fa-users-cog" + }, + "config_profile_contact": { + "type": "string", + "description": "Institutional config contact information.", + "hidden": true, + "fa_icon": "fas fa-users-cog" + }, + "config_profile_url": { + "type": "string", + "description": "Institutional config URL link.", + "hidden": true, + "fa_icon": "fas fa-users-cog" + } + } }, - "multiqc_title": { - "type": "string", - "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified.", - "fa_icon": "fas fa-file-signature" + "max_job_request_options": { + "title": "Max job request options", + "type": "object", + "fa_icon": "fab fa-acquisitions-incorporated", + "description": "Set the top limit for requested resources for any single job.", + "help_text": "If you are running on a smaller system, a pipeline step requesting more resources than are available may cause the Nextflow to stop the run with an error. These options allow you to cap the maximum resources requested by any single job so that the pipeline will run on your system.\n\nNote that you can not _increase_ the resources requested by any job using these options. For that you will need your own configuration file. See [the nf-core website](https://nf-co.re/usage/configuration) for details.", + "properties": { + "max_cpus": { + "type": "integer", + "description": "Maximum number of CPUs that can be requested for any single job.", + "default": 16, + "fa_icon": "fas fa-microchip", + "hidden": true, + "help_text": "Use to set an upper-limit for the CPU requirement for each process. Should be an integer e.g. `--max_cpus 1`" + }, + "max_memory": { + "type": "string", + "description": "Maximum amount of memory that can be requested for any single job.", + "default": "128.GB", + "fa_icon": "fas fa-memory", + "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", + "hidden": true, + "help_text": "Use to set an upper-limit for the memory requirement for each process. Should be a string in the format integer-unit e.g. `--max_memory '8.GB'`" + }, + "max_time": { + "type": "string", + "description": "Maximum amount of time that can be requested for any single job.", + "default": "240.h", + "fa_icon": "far fa-clock", + "pattern": "^(\\d+\\.?\\s*(s|m|h|day)\\s*)+$", + "hidden": true, + "help_text": "Use to set an upper-limit for the time requirement for each process. Should be a string in the format integer-unit e.g. `--max_time '2.h'`" + } + } + }, + "generic_options": { + "title": "Generic options", + "type": "object", + "fa_icon": "fas fa-file-import", + "description": "Less common options for the pipeline, typically set in a config file.", + "help_text": "These options are common to all nf-core pipelines and allow you to customise some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.", + "properties": { + "help": { + "type": "boolean", + "description": "Display help text.", + "fa_icon": "fas fa-question-circle", + "hidden": true + }, + "publish_dir_mode": { + "type": "string", + "default": "copy", + "description": "Method used to save pipeline results to output directory.", + "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", + "fa_icon": "fas fa-copy", + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "hidden": true + }, + "email_on_fail": { + "type": "string", + "description": "Email address for completion summary, only when pipeline fails.", + "fa_icon": "fas fa-exclamation-triangle", + "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$", + "help_text": "An email address to send a summary email to when the pipeline is completed - ONLY sent if the pipeline does not exit successfully.", + "hidden": true + }, + "plaintext_email": { + "type": "boolean", + "description": "Send plain-text email instead of HTML.", + "fa_icon": "fas fa-remove-format", + "hidden": true + }, + "max_multiqc_email_size": { + "type": "string", + "description": "File size limit when attaching MultiQC reports to summary emails.", + "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", + "default": "25.MB", + "fa_icon": "fas fa-file-upload", + "hidden": true + }, + "monochrome_logs": { + "type": "boolean", + "description": "Do not use coloured log outputs.", + "fa_icon": "fas fa-palette", + "hidden": true + }, + "multiqc_config": { + "type": "string", + "description": "Custom config file to supply to MultiQC.", + "fa_icon": "fas fa-cog", + "hidden": true + }, + "tracedir": { + "type": "string", + "description": "Directory to keep pipeline Nextflow logs and reports.", + "default": "${params.outdir}/pipeline_info", + "fa_icon": "fas fa-cogs", + "hidden": true + }, + "validate_params": { + "type": "boolean", + "description": "Boolean whether to validate parameters against the schema at runtime", + "default": true, + "fa_icon": "fas fa-check-square", + "hidden": true + }, + "show_hidden_params": { + "type": "boolean", + "fa_icon": "far fa-eye-slash", + "description": "Show all params when using `--help`", + "hidden": true, + "help_text": "By default, parameters set as _hidden_ in the schema are not shown on the command line when a user runs with `--help`. Specifying this option will tell the pipeline to show all parameters." + }, + "enable_conda": { + "type": "boolean", + "description": "Run this workflow with Conda. You can also use '-profile conda' instead of providing this parameter.", + "hidden": true, + "fa_icon": "fas fa-bacon" + } + } } - } }, - "reference_genome_options": { - "title": "Reference genome options", - "type": "object", - "fa_icon": "fas fa-dna", - "description": "Reference genome related files and options required for the workflow.", - "properties": { - "genome": { - "type": "string", - "description": "Name of iGenomes reference.", - "fa_icon": "fas fa-book", - "help_text": "If using a reference genome configured in the pipeline using iGenomes, use this parameter to give the ID for the reference. This is then used to build the full paths for all required reference genome files e.g. `--genome GRCh38`. \n\nSee the [nf-core website docs](https://nf-co.re/usage/reference_genomes) for more details." + "allOf": [ + { + "$ref": "#/definitions/input_output_options" }, - "igenomes_base": { - "type": "string", - "format": "directory-path", - "description": "Directory / URL base for iGenomes references.", - "default": "s3://ngi-igenomes/igenomes", - "fa_icon": "fas fa-cloud-download-alt", - "hidden": true + { + "$ref": "#/definitions/reference_genome_options" }, - "igenomes_ignore": { - "type": "boolean", - "description": "Do not load the iGenomes reference config.", - "fa_icon": "fas fa-ban", - "hidden": true, - "help_text": "Do not load `igenomes.config` when running the pipeline. You may choose this option if you observe clashes between custom parameters and those supplied in `igenomes.config`." + { + "$ref": "#/definitions/institutional_config_options" + }, + { + "$ref": "#/definitions/max_job_request_options" + }, + { + "$ref": "#/definitions/generic_options" } - } - }, - "institutional_config_options": { - "title": "Institutional config options", - "type": "object", - "fa_icon": "fas fa-university", - "description": "Parameters used to describe centralised config profiles. These should not be edited.", - "help_text": "The centralised nf-core configuration profiles use a handful of pipeline parameters to describe themselves. This information is then printed to the Nextflow log when you run a pipeline. You should not need to change these values when you run a pipeline.", - "properties": { - "custom_config_version": { - "type": "string", - "description": "Git commit id for Institutional configs.", - "default": "master", - "hidden": true, - "fa_icon": "fas fa-users-cog" + ], + "properties": { + "databases": { + "type": "string", + "default": "None" }, - "custom_config_base": { - "type": "string", - "description": "Base directory for Institutional configs.", - "default": "https://raw.githubusercontent.com/nf-core/configs/master", - "hidden": true, - "help_text": "If you're running offline, Nextflow will not be able to fetch the institutional config files from the internet. If you don't need them, then this is not a problem. If you do need them, you should download the files from the repo and tell Nextflow where to find them with this parameter.", - "fa_icon": "fas fa-users-cog" + "shortread_clipmerge": { + "type": "boolean" }, - "config_profile_name": { - "type": "string", - "description": "Institutional config name.", - "hidden": true, - "fa_icon": "fas fa-users-cog" + "shortread_excludeunmerged": { + "type": "boolean", + "default": true }, - "config_profile_description": { - "type": "string", - "description": "Institutional config description.", - "hidden": true, - "fa_icon": "fas fa-users-cog" + "longread_clip": { + "type": "boolean" }, - "config_profile_contact": { - "type": "string", - "description": "Institutional config contact information.", - "hidden": true, - "fa_icon": "fas fa-users-cog" + "run_malt": { + "type": "boolean" }, - "config_profile_url": { - "type": "string", - "description": "Institutional config URL link.", - "hidden": true, - "fa_icon": "fas fa-users-cog" + "malt_mode": { + "type": "string", + "default": "BlastN" + }, + "run_kraken2": { + "type": "boolean" + }, + "run_centrifuge": { + "type": "boolean" + }, + "centrifuge_save_unaligned": { + "type": "boolean" + }, + "centrifuge_save_aligned": { + "type": "boolean" + }, + "centrifuge_sam_format": { + "type": "boolean" } - } - }, - "max_job_request_options": { - "title": "Max job request options", - "type": "object", - "fa_icon": "fab fa-acquisitions-incorporated", - "description": "Set the top limit for requested resources for any single job.", - "help_text": "If you are running on a smaller system, a pipeline step requesting more resources than are available may cause the Nextflow to stop the run with an error. These options allow you to cap the maximum resources requested by any single job so that the pipeline will run on your system.\n\nNote that you can not _increase_ the resources requested by any job using these options. For that you will need your own configuration file. See [the nf-core website](https://nf-co.re/usage/configuration) for details.", - "properties": { - "max_cpus": { - "type": "integer", - "description": "Maximum number of CPUs that can be requested for any single job.", - "default": 16, - "fa_icon": "fas fa-microchip", - "hidden": true, - "help_text": "Use to set an upper-limit for the CPU requirement for each process. Should be an integer e.g. `--max_cpus 1`" - }, - "max_memory": { - "type": "string", - "description": "Maximum amount of memory that can be requested for any single job.", - "default": "128.GB", - "fa_icon": "fas fa-memory", - "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", - "hidden": true, - "help_text": "Use to set an upper-limit for the memory requirement for each process. Should be a string in the format integer-unit e.g. `--max_memory '8.GB'`" - }, - "max_time": { - "type": "string", - "description": "Maximum amount of time that can be requested for any single job.", - "default": "240.h", - "fa_icon": "far fa-clock", - "pattern": "^(\\d+\\.?\\s*(s|m|h|day)\\s*)+$", - "hidden": true, - "help_text": "Use to set an upper-limit for the time requirement for each process. Should be a string in the format integer-unit e.g. `--max_time '2.h'`" - } - } - }, - "generic_options": { - "title": "Generic options", - "type": "object", - "fa_icon": "fas fa-file-import", - "description": "Less common options for the pipeline, typically set in a config file.", - "help_text": "These options are common to all nf-core pipelines and allow you to customise some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.", - "properties": { - "help": { - "type": "boolean", - "description": "Display help text.", - "fa_icon": "fas fa-question-circle", - "hidden": true - }, - "publish_dir_mode": { - "type": "string", - "default": "copy", - "description": "Method used to save pipeline results to output directory.", - "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", - "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], - "hidden": true - }, - "email_on_fail": { - "type": "string", - "description": "Email address for completion summary, only when pipeline fails.", - "fa_icon": "fas fa-exclamation-triangle", - "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$", - "help_text": "An email address to send a summary email to when the pipeline is completed - ONLY sent if the pipeline does not exit successfully.", - "hidden": true - }, - "plaintext_email": { - "type": "boolean", - "description": "Send plain-text email instead of HTML.", - "fa_icon": "fas fa-remove-format", - "hidden": true - }, - "max_multiqc_email_size": { - "type": "string", - "description": "File size limit when attaching MultiQC reports to summary emails.", - "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", - "default": "25.MB", - "fa_icon": "fas fa-file-upload", - "hidden": true - }, - "monochrome_logs": { - "type": "boolean", - "description": "Do not use coloured log outputs.", - "fa_icon": "fas fa-palette", - "hidden": true - }, - "multiqc_config": { - "type": "string", - "description": "Custom config file to supply to MultiQC.", - "fa_icon": "fas fa-cog", - "hidden": true - }, - "tracedir": { - "type": "string", - "description": "Directory to keep pipeline Nextflow logs and reports.", - "default": "${params.outdir}/pipeline_info", - "fa_icon": "fas fa-cogs", - "hidden": true - }, - "validate_params": { - "type": "boolean", - "description": "Boolean whether to validate parameters against the schema at runtime", - "default": true, - "fa_icon": "fas fa-check-square", - "hidden": true - }, - "show_hidden_params": { - "type": "boolean", - "fa_icon": "far fa-eye-slash", - "description": "Show all params when using `--help`", - "hidden": true, - "help_text": "By default, parameters set as _hidden_ in the schema are not shown on the command line when a user runs with `--help`. Specifying this option will tell the pipeline to show all parameters." - }, - "enable_conda": { - "type": "boolean", - "description": "Run this workflow with Conda. You can also use '-profile conda' instead of providing this parameter.", - "hidden": true, - "fa_icon": "fas fa-bacon" - } - } } - }, - "allOf": [ - { - "$ref": "#/definitions/input_output_options" - }, - { - "$ref": "#/definitions/reference_genome_options" - }, - { - "$ref": "#/definitions/institutional_config_options" - }, - { - "$ref": "#/definitions/max_job_request_options" - }, - { - "$ref": "#/definitions/generic_options" - } - ], - "properties": { - "databases": { - "type": "string", - "default": "None" - }, - "shortread_clipmerge": { - "type": "boolean" - }, - "shortread_excludeunmerged": { - "type": "boolean", - "default": true - }, - "longread_clip": { - "type": "boolean" - }, - "run_malt": { - "type": "boolean" - }, - "malt_mode": { - "type": "string", - "default": "BlastN" - }, - "run_kraken2": { - "type": "boolean" - }, - "run_centrifuge": { - "type": "boolean" - }, - "centrifuge_save_unaligned": { - "type": "boolean" - }, - "centrifuge_save_aligned": { - "type": "boolean" - }, - "centrifuge_sam_format": { - "type": "boolean" - } - } } From 98a9688329e66814764746a9219580e841e84477 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 29 Mar 2022 15:18:06 +0200 Subject: [PATCH 066/306] Change CPUs back to template, update skiptrim fiag --- conf/base.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/base.config b/conf/base.config index 2f0f97f..0c65574 100644 --- a/conf/base.config +++ b/conf/base.config @@ -27,7 +27,7 @@ process { // TODO nf-core: Customise requirements for specific processes. // See https://www.nextflow.io/docs/latest/config.html#config-process-selectors withLabel:process_low { - cpus = { check_max( 4 * task.attempt, 'cpus' ) } + cpus = { check_max( 2 * task.attempt, 'cpus' ) } memory = { check_max( 12.GB * task.attempt, 'memory' ) } time = { check_max( 4.h * task.attempt, 'time' ) } } From 76e9624e3f04e573a56c1443884b2ca385488319 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 30 Mar 2022 11:31:37 +0200 Subject: [PATCH 067/306] NOW commit the adapte rtrim flag --- conf/modules.config | 2 +- nextflow.config | 2 +- nextflow_schema.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index a9bc3a1..71eaa75 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -68,7 +68,7 @@ process { // collapsing options - option to retain singletons params.shortread_clipmerge_excludeunmerged ? '' : "--include_unmerged", // trimming options - params.shortread_clipmerge_skiptrim ? "--disable_adapter_trimming" : "", + params.shortread_clipmerge_skipadaptertrim ? "--disable_adapter_trimming" : "", params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", !{ ${meta.single_end} } && params.shortread_clipmerge_adapter2 ? "--adapter_sequence_r2 ${params.shortread_clipmerge_adapter2}" : !{ ${meta.single_end} } ? "--detect_adapter_for_pe" : "", // filtering options diff --git a/nextflow.config b/nextflow.config index a312d0c..6fde513 100644 --- a/nextflow.config +++ b/nextflow.config @@ -57,7 +57,7 @@ params { // FASTQ preprocessing shortread_clipmerge = false shortread_clipmerge_tool = 'fastp' - shortread_clipmerge_skiptrim = false + shortread_clipmerge_skipadaptertrim = false shortread_clipmerge_mergepairs = false shortread_clipmerge_excludeunmerged = true shortread_clipmerge_adapter1 = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 545f990..adc5a73 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -286,7 +286,7 @@ "type": "string", "default": "fastp" }, - "shortread_clipmerge_skiptrim": { + "shortread_clipmerge_skipadaptertrim": { "type": "boolean" }, "shortread_clipmerge_mergepairs": { From c4c93bd59d5390c7970dea72575ff9bf8fe1707b Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 31 Mar 2022 15:31:45 +0200 Subject: [PATCH 068/306] Start working on adding adapterremoval --- modules.json | 5 +- .../nf-core/modules/adapterremoval/main.nf | 70 ++++++++++++++ .../nf-core/modules/adapterremoval/meta.yml | 90 ++++++++++++++++++ nextflow_schema.json | 26 ++++-- .../local/shortread_adapterremoval.nf | 91 +++++++++++++++++++ subworkflows/local/shortread_fastp.nf | 8 +- subworkflows/local/shortread_preprocessing.nf | 5 + 7 files changed, 281 insertions(+), 14 deletions(-) create mode 100644 modules/nf-core/modules/adapterremoval/main.nf create mode 100644 modules/nf-core/modules/adapterremoval/meta.yml create mode 100644 subworkflows/local/shortread_adapterremoval.nf diff --git a/modules.json b/modules.json index 34ef9f1..c4b2f12 100644 --- a/modules.json +++ b/modules.json @@ -3,6 +3,9 @@ "homePage": "https://github.com/nf-core/taxprofiler", "repos": { "nf-core/modules": { + "adapterremoval": { + "git_sha": "f0800157544a82ae222931764483331a81812012" + }, "cat/fastq": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, @@ -32,4 +35,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/adapterremoval/main.nf b/modules/nf-core/modules/adapterremoval/main.nf new file mode 100644 index 0000000..9d16b9c --- /dev/null +++ b/modules/nf-core/modules/adapterremoval/main.nf @@ -0,0 +1,70 @@ +process ADAPTERREMOVAL { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? "bioconda::adapterremoval=2.3.2" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/adapterremoval:2.3.2--hb7ba0dd_0' : + 'quay.io/biocontainers/adapterremoval:2.3.2--hb7ba0dd_0' }" + + input: + tuple val(meta), path(reads) + path(adapterlist) + + output: + tuple val(meta), path("${prefix}.truncated.gz") , optional: true, emit: singles_truncated + tuple val(meta), path("${prefix}.discarded.gz") , optional: true, emit: discarded + tuple val(meta), path("${prefix}.pair1.truncated.gz") , optional: true, emit: pair1_truncated + tuple val(meta), path("${prefix}.pair2.truncated.gz") , optional: true, emit: pair2_truncated + tuple val(meta), path("${prefix}.collapsed.gz") , optional: true, emit: collapsed + tuple val(meta), path("${prefix}.collapsed.truncated.gz") , optional: true, emit: collapsed_truncated + tuple val(meta), path("${prefix}.paired.gz") , optional: true, emit: paired_interleaved + tuple val(meta), path('*.log') , emit: log + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def list = adapterlist ? "--adapter-list ${adapterlist}" : "" + prefix = task.ext.prefix ?: "${meta.id}" + + if (meta.single_end) { + """ + AdapterRemoval \\ + --file1 $reads \\ + $args \\ + $adapterlist \\ + --basename ${prefix} \\ + --threads ${task.cpus} \\ + --settings ${prefix}.log \\ + --seed 42 \\ + --gzip + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + adapterremoval: \$(AdapterRemoval --version 2>&1 | sed -e "s/AdapterRemoval ver. //g") + END_VERSIONS + """ + } else { + """ + AdapterRemoval \\ + --file1 ${reads[0]} \\ + --file2 ${reads[1]} \\ + $args \\ + $adapterlist \\ + --basename ${prefix} \\ + --threads $task.cpus \\ + --settings ${prefix}.log \\ + --seed 42 \\ + --gzip + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + adapterremoval: \$(AdapterRemoval --version 2>&1 | sed -e "s/AdapterRemoval ver. //g") + END_VERSIONS + """ + } + +} diff --git a/modules/nf-core/modules/adapterremoval/meta.yml b/modules/nf-core/modules/adapterremoval/meta.yml new file mode 100644 index 0000000..5faad04 --- /dev/null +++ b/modules/nf-core/modules/adapterremoval/meta.yml @@ -0,0 +1,90 @@ +name: adapterremoval +description: Trim sequencing adapters and collapse overlapping reads +keywords: + - trimming + - adapters + - merging + - fastq +tools: + - adapterremoval: + description: The AdapterRemoval v2 tool for merging and clipping reads. + homepage: https://github.com/MikkelSchubert/adapterremoval + documentation: https://adapterremoval.readthedocs.io + licence: ["GPL v3"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + pattern: "*.{fq,fastq,fq.gz,fastq.gz}" + - adapterlist: + type: file + description: Optional text file containing list of adapters to look for for removal + with one adapter per line. Otherwise will look for default adapters (see + AdapterRemoval man page), or can be modified to remove user-specified + adapters via ext.args. + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - singles_truncated: + type: file + description: | + Adapter trimmed FastQ files of either single-end reads, or singleton + 'orphaned' reads from merging of paired-end data (i.e., one of the pair + was lost due to filtering thresholds). + pattern: "*.truncated.gz" + - discarded: + type: file + description: | + Adapter trimmed FastQ files of reads that did not pass filtering + thresholds. + pattern: "*.discarded.gz" + - pair1_truncated: + type: file + description: | + Adapter trimmed R1 FastQ files of paired-end reads that did not merge + with their respective R2 pair due to long templates. The respective pair + is stored in 'pair2_truncated'. + pattern: "*.pair1.truncated.gz" + - pair2_truncated: + type: file + description: | + Adapter trimmed R2 FastQ files of paired-end reads that did not merge + with their respective R1 pair due to long templates. The respective pair + is stored in 'pair1_truncated'. + pattern: "*.pair2.truncated.gz" + - collapsed: + type: file + description: | + Collapsed FastQ of paired-end reads that successfully merged with their + respective R1 pair but were not trimmed. + pattern: "*.collapsed.gz" + - collapsed_truncated: + type: file + description: | + Collapsed FastQ of paired-end reads that successfully merged with their + respective R1 pair and were trimmed of adapter due to sufficient overlap. + pattern: "*.collapsed.truncated.gz" + - log: + type: file + description: AdapterRemoval log file + pattern: "*.log" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@maxibor" + - "@jfy133" diff --git a/nextflow_schema.json b/nextflow_schema.json index adc5a73..ebb748a 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -284,7 +294,11 @@ }, "shortread_clipmerge_tool": { "type": "string", - "default": "fastp" + "default": "fastp", + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -294,15 +308,15 @@ }, "shortread_clipmerge_adapter1": { "type": "string", - "default": null + "default": "None" }, "shortread_clipmerge_adapter2": { "type": "string", - "default": null + "default": "None" }, "shortread_clipmerge_minlength": { "type": "integer", "default": 15 } } -} +} \ No newline at end of file diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf new file mode 100644 index 0000000..b09356a --- /dev/null +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -0,0 +1,91 @@ +/* +Process short raw reads with AdapterRemoval +*/ + +include { ADAPTERREMOVAL as ADAPTERREMOVAL_SINGLE } from '../../modules/nf-core/modules/adapterremoval/main' +include { ADAPTERREMOVAL as ADAPTERREMOVAL_PAIRED } from '../../modules/nf-core/modules/adapterremoval/main' +include { CAT_FASTQ } from '../../modules/nf-core/modules/cat/fastq/main' + +workflow SHORTREAD_ADAPTERREMOVAL { + + take: + reads // [[meta], [reads]] + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + ch_input_for_adapterremoval = reads + .dump(tag: "pre_adapterremoval_branch") + .branch{ + single: it[0]['single_end'] == true + paired: it[0]['single_end'] == false + } + + ADAPTERREMOVAL_SINGLE ( ch_input_for_adapterremoval.single, [] ) + ADAPTERREMOVAL_PAIRED ( ch_input_for_adapterremoval.paired, [] ) + + if ( params.shortread_clipmerge_mergepairs && !params.shortread_clipmerge_excludeunmerged ) { + ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed + .mix( + ADAPTERREMOVAL_PAIRED.out.collapsed_truncated, + ADAPTERREMOVAL_PAIRED.out.singles_truncated, + ADAPTERREMOVAL_PAIRED.out.pair1_truncated, + ADAPTERREMOVAL_PAIRED.out.pair2_truncated + ) + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + + [ meta_new, reads ] + } + .groupTuple(by: 0) + ch_adapterremoval_reads_prepped_pe = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads + + ch_adapterremoval_reads_prepped = ch_adapterremoval_reads_prepped_pe.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + + } else if ( params.shortread_clipmerge_mergepairs && params.shortread_clipmerge_excludeunmerged ) { + ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed + .mix( ADAPTERREMOVAL_PAIRED.out.collapsed_truncated ) + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + + [ meta_new, reads ] + } + .groupTuple(by: 0) + + ch_adapterremoval_reads_prepped_pe = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads + + ch_adapterremoval_reads_prepped = ch_adapterremoval_reads_prepped_pe.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + + } else { + + ch_adapterremoval_reads_prepped_pe = ADAPTERREMOVAL_PAIRED.out.pair1_truncated + .join( ADAPTERREMOVAL_PAIRED.out.pair2_truncated ) + .dump(tag: "pre-group") + .groupTuple(by: 0) + .dump(tag: "post-group") + .map { meta, pair1, pair2 -> + [ meta, [ pair1, pair2 ].flatten() ] + } + .dump(tag: "post-map") + + + ch_adapterremoval_reads_prepped = ch_adapterremoval_reads_prepped_pe + .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + } + + ch_processed_reads = ch_adapterremoval_reads_prepped + + ch_versions = ch_versions.mix( ADAPTERREMOVAL_SINGLE.out.versions.first() ) + ch_versions = ch_versions.mix( ADAPTERREMOVAL_PAIRED.out.versions.first() ) + ch_multiqc_files = ch_multiqc_files.mix( ADAPTERREMOVAL_PAIRED.out.log.collect{it[1]}, ADAPTERREMOVAL_SINGLE.out.log.collect{it[1]} ) + + emit: + reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index c2e435c..48817db 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -14,15 +14,11 @@ workflow SHORTREAD_FASTP { ch_multiqc_files = Channel.empty() ch_input_for_fastp = reads - .dump(tag: "pre-fastp_branch") .branch{ single: it[0]['single_end'] == true paired: it[0]['single_end'] == false } - ch_input_for_fastp.single.dump(tag: "input_fastp_single") - ch_input_for_fastp.paired.dump(tag: "input_fastp_paired") - FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) // Last parameter here turns on merging of PE data FASTP_PAIRED ( ch_input_for_fastp.paired, false, params.shortread_clipmerge_mergepairs ) @@ -46,13 +42,11 @@ workflow SHORTREAD_FASTP { ch_versions = ch_versions.mix(FASTP_SINGLE.out.versions.first()) ch_versions = ch_versions.mix(FASTP_PAIRED.out.versions.first()) - ch_processed_reads = ch_fastp_reads_prepped.dump(tag: "ch_fastp_reads_prepped") + ch_processed_reads = ch_fastp_reads_prepped ch_multiqc_files = ch_multiqc_files.mix( FASTP_SINGLE.out.json.collect{it[1]} ) ch_multiqc_files = ch_multiqc_files.mix( FASTP_PAIRED.out.json.collect{it[1]} ) - ch_multiqc_files.dump(tag: "preprocessing_fastp_mqc_final") - emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] versions = ch_versions // channel: [ versions.yml ] diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index 7fba0c0..f0a1ed4 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -4,6 +4,7 @@ include { SHORTREAD_FASTP } from './shortread_fastp' +include { SHORTREAD_ADAPTERREMOVAL } from './shortread_adapterremoval' include { FASTQC as FASTQC_PROCESSED } from '../../modules/nf-core/modules/fastqc/main' workflow SHORTREAD_PREPROCESSING { @@ -18,6 +19,10 @@ workflow SHORTREAD_PREPROCESSING { ch_processed_reads = SHORTREAD_FASTP ( reads ).reads ch_versions = ch_versions.mix( SHORTREAD_FASTP.out.versions ) ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_FASTP.out.mqc ) + } else if ( params.shortread_clipmerge_tool == "adapterremoval" ) { + ch_processed_reads = SHORTREAD_ADAPTERREMOVAL ( reads ).reads + ch_versions = ch_versions.mix( SHORTREAD_ADAPTERREMOVAL.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_ADAPTERREMOVAL.out.mqc ) } else { ch_processed_reads = reads } From dcda815bfb03697b69f219f26af69708bd2d28b5 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 1 Apr 2022 09:50:08 +0200 Subject: [PATCH 069/306] Add final adapterremoval options to match fastp functionality --- conf/modules.config | 70 ++++++++++++++++--- nextflow.config | 2 +- nextflow_schema.json | 23 ++---- .../local/shortread_adapterremoval.nf | 10 ++- 4 files changed, 71 insertions(+), 34 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 71eaa75..dc8b138 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -62,18 +62,15 @@ process { ] } - withName: FASTP { - ext.prefix = { "${meta.id}_${meta.run_accession}" } + withName: FASTP_SINGLE { ext.args = [ - // collapsing options - option to retain singletons - params.shortread_clipmerge_excludeunmerged ? '' : "--include_unmerged", // trimming options params.shortread_clipmerge_skipadaptertrim ? "--disable_adapter_trimming" : "", params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", - !{ ${meta.single_end} } && params.shortread_clipmerge_adapter2 ? "--adapter_sequence_r2 ${params.shortread_clipmerge_adapter2}" : !{ ${meta.single_end} } ? "--detect_adapter_for_pe" : "", // filtering options "--length_required ${params.shortread_clipmerge_minlength}" ].join(' ').trim() + ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ path: { "${params.outdir}/fastp" }, mode: 'copy', @@ -81,6 +78,61 @@ process { ] } + withName: FASTP_PAIRED { + ext.args = [ + // collapsing options - option to retain singletons + params.shortread_clipmerge_excludeunmerged ? '' : "--include_unmerged", + // trimming options + params.shortread_clipmerge_skipadaptertrim ? "--disable_adapter_trimming" : "", + params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", + params.shortread_clipmerge_adapter2 ? "--adapter_sequence_r2 ${params.shortread_clipmerge_adapter2}" : "--detect_adapter_for_pe", + // filtering options + "--length_required ${params.shortread_clipmerge_minlength}" + ].join(' ').trim() + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/fastp" }, + mode: 'copy', + pattern: '*.fastq.gz' + ] + } + + withName: ADAPTERREMOVAL_SINGLE { + ext.args = [ + // trimming options + params.shortread_clipmerge_skipadaptertrim ? "--adapter1 '' --adapter2 ''" : "", + params.shortread_clipmerge_adapter1 ? "--adapter1 ${params.shortread_clipmerge_adapter1}" : "", + // filtering options + "--minlength ${params.shortread_clipmerge_minlength}" + ].join(' ').trim() + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/adapterremoval" }, + mode: 'copy', + pattern: '*.fastq.gz' + ] + } + + withName: ADAPTERREMOVAL_PAIRED { + ext.args = [ + // collapsing options + params.shortread_clipmerge_mergepairs ? "--collapse" : "", + // trimming options + params.shortread_clipmerge_skipadaptertrim ? "--adapter1 '' --adapter2 ''" : "", + params.shortread_clipmerge_adapter1 ? "--adapter1 ${params.shortread_clipmerge_adapter1}" : "", + params.shortread_clipmerge_adapter2 ? "--adapter2 ${params.shortread_clipmerge_adapter2}" : "", + // filtering options + "--minlength ${params.shortread_clipmerge_minlength}" + ].join(' ').trim() + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/adapterremoval" }, + mode: 'copy', + pattern: '*.fastq.gz' + ] + } + + withName: PORECHOP { ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -99,23 +151,23 @@ process { } withName: MALT_RUN { + ext.args = { "${meta.db_params}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/malt/${meta.db_name}" }, mode: 'copy', pattern: '*.{rma6,tab,text,sam,log}' ] - ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: KRAKEN2_KRAKEN2 { + ext.args = { "${meta.db_params}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: 'copy', pattern: '*.{fastq.gz,txt}' ] - ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: CUSTOM_DUMPSOFTWAREVERSIONS { diff --git a/nextflow.config b/nextflow.config index 6fde513..7be36a6 100644 --- a/nextflow.config +++ b/nextflow.config @@ -59,7 +59,7 @@ params { shortread_clipmerge_tool = 'fastp' shortread_clipmerge_skipadaptertrim = false shortread_clipmerge_mergepairs = false - shortread_clipmerge_excludeunmerged = true + shortread_clipmerge_excludeunmerged = false shortread_clipmerge_adapter1 = null shortread_clipmerge_adapter2 = null shortread_clipmerge_minlength = 15 diff --git a/nextflow_schema.json b/nextflow_schema.json index ebb748a..fb2ca31 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -277,7 +267,7 @@ }, "shortread_clipmerge_excludeunmerged": { "type": "boolean", - "default": true + "default": false }, "longread_clip": { "type": "boolean" @@ -295,10 +285,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -319,4 +306,4 @@ "default": 15 } } -} \ No newline at end of file +} diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index b09356a..b467a64 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -16,7 +16,6 @@ workflow SHORTREAD_ADAPTERREMOVAL { ch_multiqc_files = Channel.empty() ch_input_for_adapterremoval = reads - .dump(tag: "pre_adapterremoval_branch") .branch{ single: it[0]['single_end'] == true paired: it[0]['single_end'] == false @@ -36,11 +35,13 @@ workflow SHORTREAD_ADAPTERREMOVAL { .map { meta, reads -> def meta_new = meta.clone() - meta_new['single_end'] = 1 + meta_new['single_end'] = true [ meta_new, reads ] } .groupTuple(by: 0) + + ch_adapterremoval_reads_prepped_pe = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads ch_adapterremoval_reads_prepped = ch_adapterremoval_reads_prepped_pe.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) @@ -51,7 +52,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { .map { meta, reads -> def meta_new = meta.clone() - meta_new['single_end'] = 1 + meta_new['single_end'] = true [ meta_new, reads ] } @@ -65,13 +66,10 @@ workflow SHORTREAD_ADAPTERREMOVAL { ch_adapterremoval_reads_prepped_pe = ADAPTERREMOVAL_PAIRED.out.pair1_truncated .join( ADAPTERREMOVAL_PAIRED.out.pair2_truncated ) - .dump(tag: "pre-group") .groupTuple(by: 0) - .dump(tag: "post-group") .map { meta, pair1, pair2 -> [ meta, [ pair1, pair2 ].flatten() ] } - .dump(tag: "post-map") ch_adapterremoval_reads_prepped = ch_adapterremoval_reads_prepped_pe From b04cfec2c2ad651e498bf8b0a21108ccb51a1a0a Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 1 Apr 2022 11:13:36 +0200 Subject: [PATCH 070/306] Prettier linting --- CITATIONS.md | 18 +++++++++++++++++- modules.json | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CITATIONS.md b/CITATIONS.md index 1018ccd..6e43999 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -13,9 +13,25 @@ - [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) - [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) + > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. -* [Porechop](https://github.com/rrwick/Porechop) +- [fastp](https://doi.org/10.1093/bioinformatics/bty560) + + > Chen, Shifu, Yanqing Zhou, Yaru Chen, and Jia Gu. 2018. “Fastp: An Ultra-Fast All-in-One FASTQ Preprocessor.” Bioinformatics 34 (17): i884-90. 10.1093/bioinformatics/bty560. + +- [AdapterRemoval2](https://doi.org/10.1186/s13104-016-1900-2) + + > Schubert, Mikkel, Stinus Lindgreen, and Ludovic Orlando. 2016. “AdapterRemoval v2: Rapid Adapter Trimming, Identification, and Read Merging.” BMC Research Notes 9 (February): 88. doi:10.1186/s13104-016-1900-2. + +- [Porechop](https://github.com/rrwick/Porechop) + +- [Kraken2](https://doi.org/10.1186/s13059-019-1891-0) + + > Wood, Derrick E., Jennifer Lu, and Ben Langmead. 2019. “Improved Metagenomic Analysis with Kraken 2.” Genome Biology 20 (1): 257. doi: 10.1186/s13059-019-1891-0. + +- [MALT](https://doi.org/10.1038/s41559-017-0446-6) + > Vågene, Åshild J., Alexander Herbig, Michael G. Campana, Nelly M. Robles García, Christina Warinner, Susanna Sabin, Maria A. Spyrou, et al. 2018. “Salmonella Enterica Genomes from Victims of a Major Sixteenth-Century Epidemic in Mexico.” Nature Ecology & Evolution 2 (3): 520-28. doi: 10.1038/s41559-017-0446-6. ## Software packaging/containerisation tools diff --git a/modules.json b/modules.json index c4b2f12..dcfbd3f 100644 --- a/modules.json +++ b/modules.json @@ -35,4 +35,4 @@ } } } -} \ No newline at end of file +} From df4f13319fde9e29d1ed5f0780d60aedccde04e5 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 1 Apr 2022 11:13:57 +0200 Subject: [PATCH 071/306] More linting --- CITATIONS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CITATIONS.md b/CITATIONS.md index 6e43999..8f286b0 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -31,6 +31,7 @@ > Wood, Derrick E., Jennifer Lu, and Ben Langmead. 2019. “Improved Metagenomic Analysis with Kraken 2.” Genome Biology 20 (1): 257. doi: 10.1186/s13059-019-1891-0. - [MALT](https://doi.org/10.1038/s41559-017-0446-6) + > Vågene, Åshild J., Alexander Herbig, Michael G. Campana, Nelly M. Robles García, Christina Warinner, Susanna Sabin, Maria A. Spyrou, et al. 2018. “Salmonella Enterica Genomes from Victims of a Major Sixteenth-Century Epidemic in Mexico.” Nature Ecology & Evolution 2 (3): 520-28. doi: 10.1038/s41559-017-0446-6. ## Software packaging/containerisation tools From 0d0b377dfb00b8ccd48b47be55b9bcba3dcbd582 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 1 Apr 2022 11:19:00 +0200 Subject: [PATCH 072/306] Add additional test --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb6de77..a8078e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,10 @@ jobs: # Test latest edge release of Nextflow - NXF_VER: "" NXF_EDGE: "1" + parameters: + - "--shortread_clipmerge_tool fastp" + - "--shortread_clipmerge_tool adapterremoval" + steps: - name: Check out pipeline code uses: actions/checkout@v2 @@ -47,6 +51,6 @@ jobs: # For example: adding multiple test runs with different parameters # Remember that you can parallelise this by using strategy.matrix run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results + nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results ${{ matrix.parameters }} # From 14f2b7bd84956a2dadb6c2f69d39baf1c6f3e027 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Fri, 1 Apr 2022 15:34:05 +0200 Subject: [PATCH 073/306] Apply suggestions from code review Co-authored-by: Moritz E. Beber --- subworkflows/local/shortread_adapterremoval.nf | 18 ++++++++---------- subworkflows/local/shortread_preprocessing.nf | 4 ++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index b467a64..3701f7e 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -17,14 +17,16 @@ workflow SHORTREAD_ADAPTERREMOVAL { ch_input_for_adapterremoval = reads .branch{ - single: it[0]['single_end'] == true - paired: it[0]['single_end'] == false + single: it[0].single_end + paired: !it[0].single_end } ADAPTERREMOVAL_SINGLE ( ch_input_for_adapterremoval.single, [] ) ADAPTERREMOVAL_PAIRED ( ch_input_for_adapterremoval.paired, [] ) if ( params.shortread_clipmerge_mergepairs && !params.shortread_clipmerge_excludeunmerged ) { + // due to the slightly ugly output implementation of the current AdapterRemoval2 module, each file + // has to be exported in a separate channel, and we must manually recombine when necessary ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed .mix( ADAPTERREMOVAL_PAIRED.out.collapsed_truncated, @@ -35,16 +37,14 @@ workflow SHORTREAD_ADAPTERREMOVAL { .map { meta, reads -> def meta_new = meta.clone() - meta_new['single_end'] = true + meta_new.single_end = true [ meta_new, reads ] } - .groupTuple(by: 0) + .groupTuple() - ch_adapterremoval_reads_prepped_pe = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads - - ch_adapterremoval_reads_prepped = ch_adapterremoval_reads_prepped_pe.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + ch_adapterremoval_reads_prepped = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) } else if ( params.shortread_clipmerge_mergepairs && params.shortread_clipmerge_excludeunmerged ) { ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed @@ -58,9 +58,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { } .groupTuple(by: 0) - ch_adapterremoval_reads_prepped_pe = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads - - ch_adapterremoval_reads_prepped = ch_adapterremoval_reads_prepped_pe.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + ch_adapterremoval_reads_prepped = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) } else { diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index f0a1ed4..1d0caac 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -21,8 +21,8 @@ workflow SHORTREAD_PREPROCESSING { ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_FASTP.out.mqc ) } else if ( params.shortread_clipmerge_tool == "adapterremoval" ) { ch_processed_reads = SHORTREAD_ADAPTERREMOVAL ( reads ).reads - ch_versions = ch_versions.mix( SHORTREAD_ADAPTERREMOVAL.out.versions ) - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_ADAPTERREMOVAL.out.mqc ) + ch_versions = ch_versions.mix( SHORTREAD_ADAPTERREMOVAL.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_ADAPTERREMOVAL.out.mqc ) } else { ch_processed_reads = reads } From 03e832954f89e43a641033dcae059c1870c08b2e Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Fri, 1 Apr 2022 20:55:33 +0200 Subject: [PATCH 074/306] Update subworkflows/local/shortread_adapterremoval.nf Co-authored-by: Moritz E. Beber --- subworkflows/local/shortread_adapterremoval.nf | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 3701f7e..ac8ff0f 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -62,15 +62,12 @@ workflow SHORTREAD_ADAPTERREMOVAL { } else { - ch_adapterremoval_reads_prepped_pe = ADAPTERREMOVAL_PAIRED.out.pair1_truncated + ch_adapterremoval_reads_prepped = ADAPTERREMOVAL_PAIRED.out.pair1_truncated .join( ADAPTERREMOVAL_PAIRED.out.pair2_truncated ) - .groupTuple(by: 0) + .groupTuple() .map { meta, pair1, pair2 -> [ meta, [ pair1, pair2 ].flatten() ] } - - - ch_adapterremoval_reads_prepped = ch_adapterremoval_reads_prepped_pe .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) } From 93aaa9eeacbf6211646853be0f4e18a94574e2f1 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 1 Apr 2022 21:36:22 +0200 Subject: [PATCH 075/306] Clean up AdapterRemoval and remove all debugging dumps --- subworkflows/local/db_check.nf | 2 -- subworkflows/local/input_check.nf | 4 --- subworkflows/local/longread_preprocessing.nf | 1 - .../local/shortread_adapterremoval.nf | 32 ++++++++++--------- workflows/taxprofiler.nf | 6 +--- 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index bda5cfe..356915a 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -18,9 +18,7 @@ workflow DB_CHECK { parsed_samplesheet = DATABASE_CHECK ( dbsheet ) .csv .splitCsv ( header:true, sep:',' ) - .dump(tag: "db_split_csv_out") .map { create_db_channels(it) } - .dump(tag: "db_channel_prepped") ch_dbs_for_untar = parsed_samplesheet .branch { diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 4501386..ec404c2 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -12,7 +12,6 @@ workflow INPUT_CHECK { parsed_samplesheet = SAMPLESHEET_CHECK ( samplesheet ) .csv .splitCsv ( header:true, sep:',' ) - .dump(tag: "input_split_csv_out") .branch { fasta: it['fasta'] != '' nanopore: it['instrument_platform'] == 'OXFORD_NANOPORE' @@ -21,17 +20,14 @@ workflow INPUT_CHECK { parsed_samplesheet.fastq .map { create_fastq_channel(it) } - .dump(tag: "fastq_channel_init") .set { fastq } parsed_samplesheet.nanopore .map { create_fastq_channel(it) } - .dump(tag: "fastq_nanopore_channel_init") .set { nanopore } parsed_samplesheet.fasta .map { create_fasta_channel(it) } - .dump(tag: "fasta_channel_init") .set { fasta } emit: diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 7c7c24b..a1515c7 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -16,7 +16,6 @@ workflow LONGREAD_PREPROCESSING { PORECHOP ( reads ) ch_processed_reads = PORECHOP.out.reads - .dump(tag: "pre_fastqc_check") .map { meta, reads -> def meta_new = meta.clone() diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index ac8ff0f..5e005db 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -24,9 +24,10 @@ workflow SHORTREAD_ADAPTERREMOVAL { ADAPTERREMOVAL_SINGLE ( ch_input_for_adapterremoval.single, [] ) ADAPTERREMOVAL_PAIRED ( ch_input_for_adapterremoval.paired, [] ) + // due to the slightly ugly output implementation of the current AdapterRemoval2 version, each file + // has to be exported in a separate channel, and we must manually recombine when necessary + if ( params.shortread_clipmerge_mergepairs && !params.shortread_clipmerge_excludeunmerged ) { - // due to the slightly ugly output implementation of the current AdapterRemoval2 module, each file - // has to be exported in a separate channel, and we must manually recombine when necessary ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed .mix( ADAPTERREMOVAL_PAIRED.out.collapsed_truncated, @@ -43,22 +44,23 @@ workflow SHORTREAD_ADAPTERREMOVAL { } .groupTuple() - - ch_adapterremoval_reads_prepped = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + ch_adapterremoval_reads_prepped = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads + .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) } else if ( params.shortread_clipmerge_mergepairs && params.shortread_clipmerge_excludeunmerged ) { - ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed - .mix( ADAPTERREMOVAL_PAIRED.out.collapsed_truncated ) - .map { - meta, reads -> - def meta_new = meta.clone() - meta_new['single_end'] = true + ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed + .mix( ADAPTERREMOVAL_PAIRED.out.collapsed_truncated ) + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = true - [ meta_new, reads ] - } - .groupTuple(by: 0) + [ meta_new, reads ] + } + .groupTuple(by: 0) - ch_adapterremoval_reads_prepped = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads.mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + ch_adapterremoval_reads_prepped = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads + .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) } else { @@ -68,7 +70,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { .map { meta, pair1, pair2 -> [ meta, [ pair1, pair2 ].flatten() ] } - .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) } ch_processed_reads = ch_adapterremoval_reads_prepped diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index af5c54d..ce89a91 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -18,6 +18,7 @@ for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true if (params.input ) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] warning: MALT does not except uncollapsed paired-reads. Pairs will be profiled as separate files." +if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "[nf-core/taxprofiler] error: cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -126,7 +127,6 @@ workflow TAXPROFILER { ch_input_for_profiling = ch_shortreads_preprocessed .mix( ch_longreads_preprocessed ) .combine(DB_CHECK.out.dbs) - .dump(tag: "reads_plus_db_clean") .branch { malt: it[2]['tool'] == 'malt' kraken2: it[2]['tool'] == 'kraken2' @@ -141,9 +141,7 @@ workflow TAXPROFILER { // loading takes a long time, so we only want to run it once per database // TODO document somewhere we only accept illumina short reads for MALT? ch_input_for_malt = ch_input_for_profiling.malt - .dump(tag: "input_to_malt_prefilter") .filter { it[0]['instrument_platform'] == 'ILLUMINA' } - .dump(tag: "input_to_malt_postfilter") .map { it -> def temp_meta = [ id: it[2]['db_name']] + it[2] @@ -151,7 +149,6 @@ workflow TAXPROFILER { [ temp_meta, it[1], db ] } .groupTuple(by: [0,2]) - .dump(tag: "input_to_malt") .multiMap { it -> reads: [ it[0], it[1].flatten() ] @@ -160,7 +157,6 @@ workflow TAXPROFILER { // We can run Kraken2 one-by-one sample-wise ch_input_for_kraken2 = ch_input_for_profiling.kraken2 - .dump(tag: "input_to_kraken") .multiMap { it -> reads: [ it[0] + it[2], it[1] ] From 4d2960c06fedfb052ce38b70c90197ea8ae8f9fc Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Fri, 18 Mar 2022 15:38:19 +0100 Subject: [PATCH 076/306] chore: add metaphlan3 module --- modules.json | 3 ++ modules/nf-core/modules/metaphlan3/main.nf | 45 ++++++++++++++++++ modules/nf-core/modules/metaphlan3/meta.yml | 52 +++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 modules/nf-core/modules/metaphlan3/main.nf create mode 100644 modules/nf-core/modules/metaphlan3/meta.yml diff --git a/modules.json b/modules.json index dcfbd3f..9953edb 100644 --- a/modules.json +++ b/modules.json @@ -24,6 +24,9 @@ "malt/run": { "git_sha": "72b96f4e504eef673f2b5c13560a9d90b669129b" }, + "metaphlan3": { + "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + }, "multiqc": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, diff --git a/modules/nf-core/modules/metaphlan3/main.nf b/modules/nf-core/modules/metaphlan3/main.nf new file mode 100644 index 0000000..3fc6b27 --- /dev/null +++ b/modules/nf-core/modules/metaphlan3/main.nf @@ -0,0 +1,45 @@ +process METAPHLAN3 { + tag "$meta.id" + label 'process_high' + + conda (params.enable_conda ? 'bioconda::metaphlan=3.0.12' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/metaphlan:3.0.12--pyhb7b1952_0' : + 'quay.io/biocontainers/metaphlan:3.0.12--pyhb7b1952_0' }" + + input: + tuple val(meta), path(input) + path metaphlan_db + + output: + tuple val(meta), path("*_profile.txt") , emit: profile + tuple val(meta), path("*.biom") , emit: biom + tuple val(meta), path('*.bowtie2out.txt'), optional:true, emit: bt2out + 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}" + def input_type = ("$input".endsWith(".fastq.gz")) ? "--input_type fastq" : ("$input".contains(".fasta")) ? "--input_type fasta" : ("$input".endsWith(".bowtie2out.txt")) ? "--input_type bowtie2out" : "--input_type sam" + def input_data = ("$input_type".contains("fastq")) && !meta.single_end ? "${input[0]},${input[1]}" : "$input" + def bowtie2_out = "$input_type" == "--input_type bowtie2out" || "$input_type" == "--input_type sam" ? '' : "--bowtie2out ${prefix}.bowtie2out.txt" + + """ + metaphlan \\ + --nproc $task.cpus \\ + $input_type \\ + $input_data \\ + $args \\ + $bowtie2_out \\ + --bowtie2db ${metaphlan_db} \\ + --biom ${prefix}.biom \\ + --output_file ${prefix}_profile.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + metaphlan3: \$(metaphlan --version 2>&1 | awk '{print \$3}') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/metaphlan3/meta.yml b/modules/nf-core/modules/metaphlan3/meta.yml new file mode 100644 index 0000000..d10a27d --- /dev/null +++ b/modules/nf-core/modules/metaphlan3/meta.yml @@ -0,0 +1,52 @@ +name: metaphlan3 +description: MetaPhlAn is a tool for profiling the composition of microbial communities from metagenomic shotgun sequencing data. +keywords: + - metagenomics + - classification + - fastq + - bam + - fasta +tools: + - metaphlan3: + description: Identify clades (phyla to species) present in the metagenome obtained from a microbiome sample and their relative abundance + homepage: https://huttenhower.sph.harvard.edu/metaphlan/ + documentation: https://github.com/biobakery/MetaPhlAn + doi: "10.7554/eLife.65088" + licence: ["MIT License"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - input: + type: file + description: Metaphlan 3.0 can classify the metagenome from a variety of input data types, including FASTQ files (single-end and paired-end), FASTA, bowtie2-produced SAM files (produced from alignments to the MetaPHlAn marker database) and intermediate bowtie2 alignment files (bowtie2out) + pattern: "*.{fastq.gz, fasta, fasta.gz, sam, bowtie2out.txt}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - profile: + type: file + description: Tab-separated output file of the predicted taxon relative abundances + pattern: "*.{txt}" + - biom: + type: file + description: General-use format for representing biological sample by observation contingency tables + pattern: "*.{biom}" + - bowtie2out: + type: file + description: Intermediate Bowtie2 output produced from mapping the metagenome against the MetaPHlAn marker database ( not compatible with `bowtie2out` files generated with MetaPhlAn versions below 3 ) + pattern: "*.{bowtie2out.txt}" + +authors: + - "@MGordon09" From 69db9206108c7926a25b45aba7401e406e36b4c3 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Fri, 18 Mar 2022 16:01:33 +0100 Subject: [PATCH 077/306] feat: include metaphlan3 in workflow --- workflows/taxprofiler.nf | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index ce89a91..a61580c 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -60,7 +60,7 @@ include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/ include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fastq/main' include { MALT_RUN } from '../modules/nf-core/modules/malt/run/main' include { KRAKEN2_KRAKEN2 } from '../modules/nf-core/modules/kraken2/kraken2/main' - +include { METAPHLAN3 } from '../modules/nf-core/modules/metaphlan3/main' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -130,6 +130,7 @@ workflow TAXPROFILER { .branch { malt: it[2]['tool'] == 'malt' kraken2: it[2]['tool'] == 'kraken2' + metaphlan3: it[2]['tool'] == 'metaphlan3' unknown: true } @@ -163,6 +164,13 @@ workflow TAXPROFILER { db: it[3] } + ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 + .multiMap { + it -> + reads: [ it[0] + it[2], it[1] ] + db: it[3] + } + /* MODULE: RUN PROFILING */ @@ -174,6 +182,10 @@ workflow TAXPROFILER { KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) } + if ( params.run_metaphlan3 ) { + METAPHLAN3 ( ch_input_for_metaphlan3.reads, ch_input_for_metaphlan3.db ) + } + /* MODULE: MultiQC */ From a425a322f294f49f0588123a555533e292bacfc7 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Fri, 18 Mar 2022 20:50:18 +0100 Subject: [PATCH 078/306] refactor: make metaphlan3 run in workflow --- nextflow.config | 3 +++ subworkflows/local/input_check.nf | 6 +++--- workflows/taxprofiler.nf | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/nextflow.config b/nextflow.config index 7be36a6..9f3b103 100644 --- a/nextflow.config +++ b/nextflow.config @@ -71,6 +71,9 @@ params { // kraken2 run_kraken2 = false + + // metaphlan3 + run_metaphlan3 = false } // Load base.config by default for all pipelines diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index ec404c2..e8669b3 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -31,9 +31,9 @@ workflow INPUT_CHECK { .set { fasta } emit: - fastq // channel: [ val(meta), [ reads ] ] - nanopore // channel: [ val(meta), [ reads ] ] - fasta // channel: [ val(meta), fasta ] + fastq = fastq ?: [] // channel: [ val(meta), [ reads ] ] + nanopore = nanopore ?: [] // channel: [ val(meta), [ reads ] ] + fasta = fasta ?: [] // channel: [ val(meta), fasta ] versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index a61580c..99a1685 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -165,9 +165,10 @@ workflow TAXPROFILER { } ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 + .dump(tag: "input_metaphlan3") .multiMap { it -> - reads: [ it[0] + it[2], it[1] ] + reads: [it[0] + it[2], it[1][0]] db: it[3] } From fb9afa013afa33c08554a740b73bd18ee6a1d57e Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Fri, 1 Apr 2022 16:33:22 +0200 Subject: [PATCH 079/306] fix: add metaphlan parameter to schema --- nextflow_schema.json | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index fb2ca31..358ba64 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -282,6 +292,10 @@ "run_kraken2": { "type": "boolean" }, + "run_metaphlan3": { + "type": "boolean", + "description": "Enable MetaPhlAn for taxonomic profiling" + }, "shortread_clipmerge_tool": { "type": "string", "default": "fastp", @@ -306,4 +320,4 @@ "default": 15 } } -} +} \ No newline at end of file From e579f9f5f33899336ea449eea41302ec0e8657f0 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 00:28:54 +0200 Subject: [PATCH 080/306] style: apply prettier --- nextflow_schema.json | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 358ba64..1efdea8 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -320,4 +310,4 @@ "default": 15 } } -} \ No newline at end of file +} From 95ed86516b445b42e1703cd2c7c020c45fabbfb3 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 13:59:10 +0200 Subject: [PATCH 081/306] chore: add citation --- CITATIONS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CITATIONS.md b/CITATIONS.md index 8f286b0..0b35cce 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -34,6 +34,12 @@ > Vågene, Åshild J., Alexander Herbig, Michael G. Campana, Nelly M. Robles García, Christina Warinner, Susanna Sabin, Maria A. Spyrou, et al. 2018. “Salmonella Enterica Genomes from Victims of a Major Sixteenth-Century Epidemic in Mexico.” Nature Ecology & Evolution 2 (3): 520-28. doi: 10.1038/s41559-017-0446-6. +- [Porechop](https://github.com/rrwick/Porechop) + +- [MetaPhlAn3](https://doi.org/10.7554/eLife.65088) + + > Beghini, Francesco, Lauren J McIver, Aitor Blanco-Míguez, Leonard Dubois, Francesco Asnicar, Sagun Maharjan, Ana Mailyan, et al. 2021. “Integrating Taxonomic, Functional, and Strain-Level Profiling of Diverse Microbial Communities with BioBakery 3.” Edited by Peter Turnbaugh, Eduardo Franco, and C Titus Brown. ELife 10 (May): e65088. + ## Software packaging/containerisation tools - [Anaconda](https://anaconda.com) From b0b5936fe847fd416d4f223f9f1051425fddac73 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 14:44:27 +0200 Subject: [PATCH 082/306] chore: configure output for metaphlan3 --- conf/modules.config | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/conf/modules.config b/conf/modules.config index dc8b138..5dfaa82 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -170,6 +170,15 @@ process { ] } + withName: METAPHLAN3 { + publishDir = [ + path: { "${params.outdir}/metaphlan3/${metaphlan_db.simpleName}" }, + mode: params.publish_dir_mode, + pattern: '*.{biom,txt}' + ] + ext.prefix = { "${meta.id}-${meta.run_accession}-${metaphlan_db.simpleName}" } + } + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ path: { "${params.outdir}/pipeline_info" }, From 996b3f49126b68e273dbc94dd46302f0490a8168 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 14:44:48 +0200 Subject: [PATCH 083/306] fix: correct the input shape to metaphlan3 --- workflows/taxprofiler.nf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 99a1685..54ede13 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -168,7 +168,7 @@ workflow TAXPROFILER { .dump(tag: "input_metaphlan3") .multiMap { it -> - reads: [it[0] + it[2], it[1][0]] + reads: [it[0] + it[2], it[1]] db: it[3] } @@ -217,6 +217,7 @@ workflow TAXPROFILER { // TODO MALT results overwriting per database? // TODO Versions for Karken/MALT not report? + // TODO create multiQC module for metaphlan MULTIQC ( ch_multiqc_files.collect() ) From 817678d870389a5b88b99bcf4dc7290c71dd416b Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 14:45:07 +0200 Subject: [PATCH 084/306] chore: add metaphlan3 run to test profile --- conf/test.config | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/test.config b/conf/test.config index 92a10e4..b8cd47c 100644 --- a/conf/test.config +++ b/conf/test.config @@ -26,6 +26,7 @@ params { databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' run_kraken2 = true run_malt = true + run_metaphlan3 = true shortread_clipmerge = true } From 70862570e0334e0af3a039fe790158f57c88b86d Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 14:58:07 +0200 Subject: [PATCH 085/306] fix: use same naming scheme --- conf/modules.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 5dfaa82..37b1f2f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -172,11 +172,11 @@ process { withName: METAPHLAN3 { publishDir = [ - path: { "${params.outdir}/metaphlan3/${metaphlan_db.simpleName}" }, + path: { "${params.outdir}/metaphlan3/${meta.db_name}" }, mode: params.publish_dir_mode, pattern: '*.{biom,txt}' ] - ext.prefix = { "${meta.id}-${meta.run_accession}-${metaphlan_db.simpleName}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: CUSTOM_DUMPSOFTWAREVERSIONS { From 562fa426962da46946bf9411ac3b6f68b4ae8d96 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 15:00:55 +0200 Subject: [PATCH 086/306] fix: remove duplicate porechop entry --- CITATIONS.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CITATIONS.md b/CITATIONS.md index 0b35cce..b18f841 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -34,8 +34,6 @@ > Vågene, Åshild J., Alexander Herbig, Michael G. Campana, Nelly M. Robles García, Christina Warinner, Susanna Sabin, Maria A. Spyrou, et al. 2018. “Salmonella Enterica Genomes from Victims of a Major Sixteenth-Century Epidemic in Mexico.” Nature Ecology & Evolution 2 (3): 520-28. doi: 10.1038/s41559-017-0446-6. -- [Porechop](https://github.com/rrwick/Porechop) - - [MetaPhlAn3](https://doi.org/10.7554/eLife.65088) > Beghini, Francesco, Lauren J McIver, Aitor Blanco-Míguez, Leonard Dubois, Francesco Asnicar, Sagun Maharjan, Ana Mailyan, et al. 2021. “Integrating Taxonomic, Functional, and Strain-Level Profiling of Diverse Microbial Communities with BioBakery 3.” Edited by Peter Turnbaugh, Eduardo Franco, and C Titus Brown. ELife 10 (May): e65088. From 4e22bc961084688ce9a0c6f8d1d564b70b7af912 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 15:20:36 +0200 Subject: [PATCH 087/306] fix: force UTF-8 in different locale? --- nextflow.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 9f3b103..588c0cf 100644 --- a/nextflow.config +++ b/nextflow.config @@ -158,7 +158,8 @@ if (!params.igenomes_ignore) { // See https://apeltzer.github.io/post/03-julia-lang-nextflow/ for details on that. Once we have a common agreement on where to keep Julia packages, this is adjustable. env { - PYTHONNOUSERSITE = 1 + PYTHONNOUSERSITE = '1' + PYTHONUTF8 = '1' // MetaPhlAn3 may fail with a UnicodeDecodeError if the locale is not set appropriately. R_PROFILE_USER = "/.Rprofile" R_ENVIRON_USER = "/.Renviron" JULIA_DEPOT_PATH = "/usr/local/share/julia" From 41526e8a64bdbf40cc28a941e1aaa2430aa66a5d Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 15:44:39 +0200 Subject: [PATCH 088/306] try to fix locale --- .github/workflows/ci.yml | 8 ++++++++ nextflow.config | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8078e5..b8975b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,14 @@ jobs: wget -qO- get.nextflow.io | bash sudo mv nextflow /usr/local/bin/ + - name: Show current locale + run: locale + + - name: Set UTF-8 enabled locale + run: | + sudo locale-gen en_US.UTF-8 + sudo update-locale LANG=en_US.UTF-8 + - name: Run pipeline with test data # TODO nf-core: You can customise CI pipeline run tests as required # For example: adding multiple test runs with different parameters diff --git a/nextflow.config b/nextflow.config index 588c0cf..cd6de9c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -159,7 +159,7 @@ if (!params.igenomes_ignore) { env { PYTHONNOUSERSITE = '1' - PYTHONUTF8 = '1' // MetaPhlAn3 may fail with a UnicodeDecodeError if the locale is not set appropriately. + // PYTHONUTF8 = '1' // MetaPhlAn3 may fail with a UnicodeDecodeError if the locale is not set appropriately. R_PROFILE_USER = "/.Rprofile" R_ENVIRON_USER = "/.Renviron" JULIA_DEPOT_PATH = "/usr/local/share/julia" From b055df5ea076e2d82071eba9e3e692ce9d43e976 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 2 Apr 2022 17:02:05 +0200 Subject: [PATCH 089/306] Add bbduk complexity (entropy-based) filtering --- CITATIONS.md | 2 + conf/modules.config | 15 +++-- modules.json | 3 + modules/nf-core/modules/bbmap/bbduk/main.nf | 43 ++++++++++++ modules/nf-core/modules/bbmap/bbduk/meta.yml | 52 +++++++++++++++ nextflow.config | 7 ++ nextflow_schema.json | 21 +++++- subworkflows/local/longread_preprocessing.nf | 8 +-- .../local/shortread_adapterremoval.nf | 6 +- .../local/shortread_complexityfiltering.nf | 28 ++++++++ subworkflows/local/shortread_fastp.nf | 10 +-- subworkflows/local/shortread_preprocessing.nf | 6 +- workflows/taxprofiler.nf | 66 ++++++++++++------- 13 files changed, 222 insertions(+), 45 deletions(-) create mode 100644 modules/nf-core/modules/bbmap/bbduk/main.nf create mode 100644 modules/nf-core/modules/bbmap/bbduk/meta.yml create mode 100644 subworkflows/local/shortread_complexityfiltering.nf diff --git a/CITATIONS.md b/CITATIONS.md index 8f286b0..0eb8141 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -26,6 +26,8 @@ - [Porechop](https://github.com/rrwick/Porechop) +- [BBTools](http://sourceforge.net/projects/bbmap/) + - [Kraken2](https://doi.org/10.1186/s13059-019-1891-0) > Wood, Derrick E., Jennifer Lu, and Ben Langmead. 2019. “Improved Metagenomic Analysis with Kraken 2.” Genome Biology 20 (1): 257. doi: 10.1186/s13059-019-1891-0. diff --git a/conf/modules.config b/conf/modules.config index dc8b138..601f915 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -132,7 +132,6 @@ process { ] } - withName: PORECHOP { ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -142,11 +141,17 @@ process { ] } - withName: CAT_FASTQ { + withName: BBMAP_BBDUK { + ext.args = [ + "entropy=${params.shortread_complexityfilter_bbduk_entropy}", + "entropywindow=${params.shortread_complexityfilter_bbduk_windowsize}", + params.shortread_complexityfilter_bbduk_mask ? "entropymask=t" : "entropymask=f" + ].join(' ').trim() + ext.prefix = { "${meta.id}-${meta.run_accession}" } publishDir = [ - path: { "${params.outdir}/prepared_sequences" }, - mode: 'copy', - pattern: '*.fastq.gz' + path: { "${params.outdir}/bbduk/" }, + mode: params.publish_dir_mode, + pattern: '*.{fastq.gz,log}' ] } diff --git a/modules.json b/modules.json index dcfbd3f..64e6c3c 100644 --- a/modules.json +++ b/modules.json @@ -6,6 +6,9 @@ "adapterremoval": { "git_sha": "f0800157544a82ae222931764483331a81812012" }, + "bbmap/bbduk": { + "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + }, "cat/fastq": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, diff --git a/modules/nf-core/modules/bbmap/bbduk/main.nf b/modules/nf-core/modules/bbmap/bbduk/main.nf new file mode 100644 index 0000000..0ae005e --- /dev/null +++ b/modules/nf-core/modules/bbmap/bbduk/main.nf @@ -0,0 +1,43 @@ +process BBMAP_BBDUK { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? "bioconda::bbmap=38.90" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bbmap:38.90--he522d1c_1' : + 'quay.io/biocontainers/bbmap:38.90--he522d1c_1' }" + + input: + tuple val(meta), path(reads) + path contaminants + + output: + tuple val(meta), path('*.fastq.gz'), emit: reads + tuple val(meta), path('*.log') , emit: log + 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}" + def raw = meta.single_end ? "in=${reads[0]}" : "in1=${reads[0]} in2=${reads[1]}" + def trimmed = meta.single_end ? "out=${prefix}.fastq.gz" : "out1=${prefix}_1.fastq.gz out2=${prefix}_2.fastq.gz" + def contaminants_fa = contaminants ? "ref=$contaminants" : '' + """ + maxmem=\$(echo \"$task.memory\"| sed 's/ GB/g/g') + bbduk.sh \\ + -Xmx\$maxmem \\ + $raw \\ + $trimmed \\ + threads=$task.cpus \\ + $args \\ + $contaminants_fa \\ + &> ${prefix}.bbduk.log + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bbmap: \$(bbversion.sh) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/bbmap/bbduk/meta.yml b/modules/nf-core/modules/bbmap/bbduk/meta.yml new file mode 100644 index 0000000..6abd3d9 --- /dev/null +++ b/modules/nf-core/modules/bbmap/bbduk/meta.yml @@ -0,0 +1,52 @@ +name: bbmap_bbduk +description: Adapter and quality trimming of sequencing reads +keywords: + - trimming + - adapter trimming + - quality trimming +tools: + - bbmap: + description: BBMap is a short read aligner, as well as various other bioinformatic tools. + homepage: https://jgi.doe.gov/data-and-tools/bbtools/bb-tools-user-guide/ + documentation: https://jgi.doe.gov/data-and-tools/bbtools/bb-tools-user-guide/ + tool_dev_url: None + doi: "" + licence: ["UC-LBL license (see package)"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + - contaminants: + type: file + description: | + Reference files containing adapter and/or contaminant sequences for sequence kmer matching + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: The trimmed/modified fastq reads + pattern: "*fastq.gz" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - log: + type: file + description: Bbduk log file + pattern: "*bbduk.log" + +authors: + - "@MGordon09" diff --git a/nextflow.config b/nextflow.config index 7be36a6..d57743a 100644 --- a/nextflow.config +++ b/nextflow.config @@ -65,6 +65,13 @@ params { shortread_clipmerge_minlength = 15 longread_clip = false + // Complexity filtering + shortread_complexityfilter = false + shortread_complexityfilter_tool = 'bbduk' + shortread_complexityfilter_bbduk_entropy = 0.3 + shortread_complexityfilter_bbduk_windowsize = 50 + shortread_complexityfilter_bbduk_mask = false + // MALT run_malt = false malt_mode = 'BlastN' diff --git a/nextflow_schema.json b/nextflow_schema.json index fb2ca31..bd8b438 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -266,8 +266,7 @@ "type": "boolean" }, "shortread_clipmerge_excludeunmerged": { - "type": "boolean", - "default": false + "type": "boolean" }, "longread_clip": { "type": "boolean" @@ -304,6 +303,24 @@ "shortread_clipmerge_minlength": { "type": "integer", "default": 15 + }, + "shortread_complexityfilter_tool": { + "type": "string", + "default": "bbduk" + }, + "shortread_complexityfilter_bbduk_entropy": { + "type": "number", + "default": 0.3 + }, + "shortread_complexityfilter_bbduk_windowsize": { + "type": "integer", + "default": 50 + }, + "shortread_complexityfilter_bbduk_mask": { + "type": "boolean" + }, + "shortread_complexityfilter": { + "type": "boolean" } } } diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index a1515c7..2fa5f3b 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -1,6 +1,6 @@ -/* -Process long raw reads with porechop -*/ +// +// Process long raw reads with porechop +// include { FASTQC as FASTQC_PROCESSED } from '../../modules/nf-core/modules/fastqc/main' include { PORECHOP } from '../../modules/nf-core/modules/porechop/main' @@ -25,7 +25,7 @@ workflow LONGREAD_PREPROCESSING { FASTQC_PROCESSED ( PORECHOP.out.reads ) ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) emit: diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 5e005db..b173e4c 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -1,6 +1,6 @@ -/* -Process short raw reads with AdapterRemoval -*/ +// +// Process short raw reads with AdapterRemoval +// include { ADAPTERREMOVAL as ADAPTERREMOVAL_SINGLE } from '../../modules/nf-core/modules/adapterremoval/main' include { ADAPTERREMOVAL as ADAPTERREMOVAL_PAIRED } from '../../modules/nf-core/modules/adapterremoval/main' diff --git a/subworkflows/local/shortread_complexityfiltering.nf b/subworkflows/local/shortread_complexityfiltering.nf new file mode 100644 index 0000000..3d69ed6 --- /dev/null +++ b/subworkflows/local/shortread_complexityfiltering.nf @@ -0,0 +1,28 @@ +// +// Check input samplesheet and get read channels +// + +include { BBMAP_BBDUK } from '../../modules/nf-core/modules/bbmap/bbduk/main' + +workflow SHORTREAD_COMPLEXITYFILTERING { + take: + reads // [ [ meta ], [ reads ] ] + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + if ( params.shortread_complexityfilter_tool == 'bbduk' ) { + ch_filtered_reads = BBMAP_BBDUK ( reads, [] ).reads + ch_versions = ch_versions.mix( BBMAP_BBDUK.out.versions.first() ) + ch_multiqc_files = ch_multiqc_files.mix( BBMAP_BBDUK.out.log ) + } else { + ch_filtered_reads = reads + } + + emit: + reads = ch_filtered_reads // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} + diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index 48817db..18baf17 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -1,6 +1,6 @@ -/* -Process short raw reads with FastP -*/ +// +// Process short raw reads with FastP +// include { FASTP as FASTP_SINGLE } from '../../modules/nf-core/modules/fastp/main' include { FASTP as FASTP_PAIRED } from '../../modules/nf-core/modules/fastp/main' @@ -44,8 +44,8 @@ workflow SHORTREAD_FASTP { ch_processed_reads = ch_fastp_reads_prepped - ch_multiqc_files = ch_multiqc_files.mix( FASTP_SINGLE.out.json.collect{it[1]} ) - ch_multiqc_files = ch_multiqc_files.mix( FASTP_PAIRED.out.json.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( FASTP_SINGLE.out.json ) + ch_multiqc_files = ch_multiqc_files.mix( FASTP_PAIRED.out.json ) emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index 1d0caac..b0ac25e 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -1,5 +1,5 @@ // -// Check input samplesheet and get read channels +// Perform read trimming and merging // @@ -9,7 +9,7 @@ include { FASTQC as FASTQC_PROCESSED } from '../../modules/nf-core/modules workflow SHORTREAD_PREPROCESSING { take: - reads // file: /path/to/samplesheet.csv + reads // [ [ meta ], [ reads ] ] main: ch_versions = Channel.empty() @@ -29,7 +29,7 @@ workflow SHORTREAD_PREPROCESSING { FASTQC_PROCESSED ( ch_processed_reads ) ch_versions = ch_versions.mix( FASTQC_PROCESSED.out.versions ) - ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index ce89a91..a403589 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -40,9 +40,10 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi // include { INPUT_CHECK } from '../subworkflows/local/input_check' -include { DB_CHECK } from '../subworkflows/local/db_check' -include { SHORTREAD_PREPROCESSING } from '../subworkflows/local/shortread_preprocessing' -include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' +include { DB_CHECK } from '../subworkflows/local/db_check' +include { SHORTREAD_PREPROCESSING } from '../subworkflows/local/shortread_preprocessing' +include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' +include { SHORTREAD_COMPLEXITYFILTERING } from '../subworkflows/local/shortread_complexityfiltering' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -61,7 +62,6 @@ include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fas include { MALT_RUN } from '../modules/nf-core/modules/malt/run/main' include { KRAKEN2_KRAKEN2 } from '../modules/nf-core/modules/kraken2/kraken2/main' - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RUN MAIN WORKFLOW @@ -98,10 +98,6 @@ workflow TAXPROFILER { ch_versions = ch_versions.mix(FASTQC.out.versions.first()) - CUSTOM_DUMPSOFTWAREVERSIONS ( - ch_versions.unique().collectFile(name: 'collated_versions.yml') - ) - /* SUBWORKFLOW: PERFORM PREPROCESSING */ @@ -114,17 +110,26 @@ workflow TAXPROFILER { if ( params.longread_clip ) { ch_longreads_preprocessed = LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ).reads .map { it -> [ it[0], [it[1]] ] } - ch_versions = ch_versions.mix(LONGREAD_PREPROCESSING.out.versions.first()) } else { ch_longreads_preprocessed = INPUT_CHECK.out.nanopore } + /* + SUBWORKFLOW: COMPLEXITY FILTERING + */ + + if ( params.shortread_complexityfilter ) { + ch_shortreads_filtered = SHORTREAD_COMPLEXITYFILTERING ( ch_shortreads_preprocessed ).reads + } else { + ch_shortreads_filtered = ch_shortreads_preprocessed + } + /* COMBINE READS WITH POSSIBLE DATABASES */ // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] - ch_input_for_profiling = ch_shortreads_preprocessed + ch_input_for_profiling = ch_shortreads_filtered .mix( ch_longreads_preprocessed ) .combine(DB_CHECK.out.dbs) .branch { @@ -177,6 +182,12 @@ workflow TAXPROFILER { /* MODULE: MultiQC */ + + CUSTOM_DUMPSOFTWAREVERSIONS ( + ch_versions.unique().collectFile(name: 'collated_versions.yml') + ) + + workflow_summary = WorkflowTaxprofiler.paramsSummaryMultiqc(workflow, summary_params) ch_workflow_summary = Channel.value(workflow_summary) @@ -188,21 +199,30 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) if (params.shortread_clipmerge) { - ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_PREPROCESSING.out.mqc) - } - if (params.longread_clip) { - ch_multiqc_files = ch_multiqc_files.mix(LONGREAD_PREPROCESSING.out.mqc) - } - if (params.run_kraken2) { - ch_multiqc_files = ch_multiqc_files.mix(KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([])) - ch_versions = ch_versions.mix(KRAKEN2_KRAKEN2.out.versions.first()) - } - if (params.run_malt) { - ch_multiqc_files = ch_multiqc_files.mix(MALT_RUN.out.log.collect{it[1]}.ifEmpty([])) - ch_versions = ch_versions.mix(MALT_RUN.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_shortclipmerge") + ch_versions = ch_versions.mix( SHORTREAD_PREPROCESSING.out.versions ) + } + + if (params.longread_clip) { + ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_longclipmerge") + ch_versions = ch_versions.mix( LONGREAD_PREPROCESSING.out.versions ) + } + + if (params.shortread_complexityfilter){ + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_compelxity") + ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) + } + + if (params.run_kraken2) { + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_kraken") + ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) + } + + if (params.run_malt) { + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_malt") + ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) } - // TODO MALT results overwriting per database? // TODO Versions for Karken/MALT not report? MULTIQC ( ch_multiqc_files.collect() From 4eab257d68a36b333bc1c5e087e6a79721b5b87e Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 17:05:46 +0200 Subject: [PATCH 090/306] style: prettify comment --- subworkflows/local/shortread_adapterremoval.nf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 5e005db..3598e3e 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -24,8 +24,10 @@ workflow SHORTREAD_ADAPTERREMOVAL { ADAPTERREMOVAL_SINGLE ( ch_input_for_adapterremoval.single, [] ) ADAPTERREMOVAL_PAIRED ( ch_input_for_adapterremoval.paired, [] ) - // due to the slightly ugly output implementation of the current AdapterRemoval2 version, each file - // has to be exported in a separate channel, and we must manually recombine when necessary + /* + * Due to the ~slightly~ very ugly output implementation of the current AdapterRemoval2 version, each file + * has to be exported in a separate channel and we must manually recombine when necessary. + */ if ( params.shortread_clipmerge_mergepairs && !params.shortread_clipmerge_excludeunmerged ) { ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed From 7f1a7fb4e7be661e3a01ab0ffb0a61fe5de314ae Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 2 Apr 2022 17:05:54 +0200 Subject: [PATCH 091/306] Add complexity filtering to test profile --- conf/test.config | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/conf/test.config b/conf/test.config index 92a10e4..bce5918 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,8 +24,9 @@ params { // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - run_kraken2 = true - run_malt = true - shortread_clipmerge = true + run_kraken2 = true + run_malt = true + shortread_clipmerge = true + shortread_complexityfiltering = true } From 469e4f36825d86bf83a341d8c95bb4768f682bfd Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 2 Apr 2022 17:07:30 +0200 Subject: [PATCH 092/306] Remove remaining debugging .dumps( --- workflows/taxprofiler.nf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index a403589..1c86edb 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -90,7 +90,7 @@ workflow TAXPROFILER { /* MODULE: Run FastQC */ - ch_input_for_fastqc = INPUT_CHECK.out.fastq.mix( INPUT_CHECK.out.nanopore ).dump(tag: "input_to_fastq") + ch_input_for_fastqc = INPUT_CHECK.out.fastq.mix( INPUT_CHECK.out.nanopore ) FASTQC ( ch_input_for_fastqc @@ -199,27 +199,27 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) if (params.shortread_clipmerge) { - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_shortclipmerge") + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( SHORTREAD_PREPROCESSING.out.versions ) } if (params.longread_clip) { - ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_longclipmerge") + ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( LONGREAD_PREPROCESSING.out.versions ) } if (params.shortread_complexityfilter){ - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_compelxity") + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } if (params.run_kraken2) { - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_kraken") + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) } if (params.run_malt) { - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ).dump(tag: "mqc_malt") + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) } From 02d950c58c84f4e6c571cdb055154016fc78a22d Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 2 Apr 2022 17:18:20 +0200 Subject: [PATCH 093/306] Fix test so complexity filter actually executes --- conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index bce5918..3e7530c 100644 --- a/conf/test.config +++ b/conf/test.config @@ -27,6 +27,6 @@ params { run_kraken2 = true run_malt = true shortread_clipmerge = true - shortread_complexityfiltering = true + shortread_complexityfilter = true } From be93eae64057d21c7c5da34162ac2bc51fd86922 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sat, 2 Apr 2022 21:50:13 +0200 Subject: [PATCH 094/306] fix: insert `.fastq` file extension The MetaPhlAn3 module computes the input type from the file extension without the possibility to configure an override. Since AdapterRemoval2 creates files without `.fastq` extensions, MetaPhlAn3 input was determined to be SAM. --- modules/local/ensure_fastq_extension.nf | 31 +++++ .../local/shortread_adapterremoval.nf | 126 ++++++++++++------ 2 files changed, 118 insertions(+), 39 deletions(-) create mode 100644 modules/local/ensure_fastq_extension.nf diff --git a/modules/local/ensure_fastq_extension.nf b/modules/local/ensure_fastq_extension.nf new file mode 100644 index 0000000..6de223b --- /dev/null +++ b/modules/local/ensure_fastq_extension.nf @@ -0,0 +1,31 @@ +process ENSURE_FASTQ_EXTENSION { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "conda-forge::bash=5.0" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv2/biocontainers_v1.2.0_cv2.img' : + 'biocontainers/biocontainers:v1.2.0_cv2' }" + + + input: + tuple val(meta), path(reads) + + output: + tuple val(meta), path('*.fastq.gz'), emit: reads + + script: + if (meta.single_end) { + fastq = "${reads.baseName}.fastq.gz" + """ + ln -s '${reads}' '${fastq}' + """ + } else { + first = "${reads[0].baseName}.fastq.gz" + second = "${reads[1].baseName}.fastq.gz" + """ + ln -s '${reads[0]}' '${first}' + ln -s '${reads[1]}' '${second}' + """ + } +} diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 3598e3e..661062d 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -5,6 +5,11 @@ Process short raw reads with AdapterRemoval include { ADAPTERREMOVAL as ADAPTERREMOVAL_SINGLE } from '../../modules/nf-core/modules/adapterremoval/main' include { ADAPTERREMOVAL as ADAPTERREMOVAL_PAIRED } from '../../modules/nf-core/modules/adapterremoval/main' include { CAT_FASTQ } from '../../modules/nf-core/modules/cat/fastq/main' +include { + ENSURE_FASTQ_EXTENSION as ENSURE_FASTQ_EXTENSION1; + ENSURE_FASTQ_EXTENSION as ENSURE_FASTQ_EXTENSION2; + ENSURE_FASTQ_EXTENSION as ENSURE_FASTQ_EXTENSION3; +} from '../../modules/local/ensure_fastq_extension' workflow SHORTREAD_ADAPTERREMOVAL { @@ -30,59 +35,102 @@ workflow SHORTREAD_ADAPTERREMOVAL { */ if ( params.shortread_clipmerge_mergepairs && !params.shortread_clipmerge_excludeunmerged ) { - ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed - .mix( - ADAPTERREMOVAL_PAIRED.out.collapsed_truncated, - ADAPTERREMOVAL_PAIRED.out.singles_truncated, - ADAPTERREMOVAL_PAIRED.out.pair1_truncated, - ADAPTERREMOVAL_PAIRED.out.pair2_truncated - ) - .map { - meta, reads -> - def meta_new = meta.clone() - meta_new.single_end = true - [ meta_new, reads ] - } - .groupTuple() + ENSURE_FASTQ_EXTENSION1( + Channel.empty().mix( + ADAPTERREMOVAL_PAIRED.out.collapsed, + ADAPTERREMOVAL_PAIRED.out.collapsed_truncated, + ADAPTERREMOVAL_PAIRED.out.singles_truncated, + ADAPTERREMOVAL_PAIRED.out.pair1_truncated, + ADAPTERREMOVAL_PAIRED.out.pair2_truncated + ) + .map { meta, reads -> + meta.single_end = true + [meta, reads] + } + ) - ch_adapterremoval_reads_prepped = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads - .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + CAT_FASTQ( + ENSURE_FASTQ_EXTENSION1.out.reads + .groupTuple() + ) + + ENSURE_FASTQ_EXTENSION2(ADAPTERREMOVAL_SINGLE.out.singles_truncated) + + ch_adapterremoval_reads_prepped = CAT_FASTQ.out.reads + .mix(ENSURE_FASTQ_EXTENSION2.out.reads) } else if ( params.shortread_clipmerge_mergepairs && params.shortread_clipmerge_excludeunmerged ) { - ch_adapterremoval_for_cat = ADAPTERREMOVAL_PAIRED.out.collapsed - .mix( ADAPTERREMOVAL_PAIRED.out.collapsed_truncated ) - .map { - meta, reads -> - def meta_new = meta.clone() - meta_new['single_end'] = true - [ meta_new, reads ] - } - .groupTuple(by: 0) + ENSURE_FASTQ_EXTENSION1( + Channel.empty().mix( + ADAPTERREMOVAL_PAIRED.out.collapsed, + ADAPTERREMOVAL_PAIRED.out.collapsed_truncated + ) + .map { meta, reads -> + meta.single_end = true + [meta, reads] + } + ) - ch_adapterremoval_reads_prepped = CAT_FASTQ ( ch_adapterremoval_for_cat ).reads - .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) + CAT_FASTQ( + ENSURE_FASTQ_EXTENSION1.out.reads + .groupTuple() + ) + + ENSURE_FASTQ_EXTENSION2(ADAPTERREMOVAL_SINGLE.out.singles_truncated) + + ch_adapterremoval_reads_prepped = CAT_FASTQ.out.reads + .mix(ENSURE_FASTQ_EXTENSION2.out.reads) } else { - ch_adapterremoval_reads_prepped = ADAPTERREMOVAL_PAIRED.out.pair1_truncated - .join( ADAPTERREMOVAL_PAIRED.out.pair2_truncated ) - .groupTuple() - .map { meta, pair1, pair2 -> - [ meta, [ pair1, pair2 ].flatten() ] - } - .mix( ADAPTERREMOVAL_SINGLE.out.singles_truncated ) - } + ENSURE_FASTQ_EXTENSION1( + ADAPTERREMOVAL_PAIRED.out.pair1_truncated + .map { meta, reads -> + meta.single_end = true + [meta, reads] + } + ) - ch_processed_reads = ch_adapterremoval_reads_prepped + ENSURE_FASTQ_EXTENSION2( + ADAPTERREMOVAL_PAIRED.out.pair2_truncated + .map { meta, reads -> + meta.single_end = true + [meta, reads] + } + ) + + ENSURE_FASTQ_EXTENSION3(ADAPTERREMOVAL_SINGLE.out.singles_truncated) + + ch_adapterremoval_reads_prepped = ENSURE_FASTQ_EXTENSION1.out.reads + .join(ENSURE_FASTQ_EXTENSION2.out.reads) + .groupTuple() + .map { meta, pair1, pair2 -> + meta.single_end = false + [ meta, [ pair1, pair2 ].flatten() ] + } + .mix(ENSURE_FASTQ_EXTENSION3.out.reads) + + } ch_versions = ch_versions.mix( ADAPTERREMOVAL_SINGLE.out.versions.first() ) ch_versions = ch_versions.mix( ADAPTERREMOVAL_PAIRED.out.versions.first() ) - ch_multiqc_files = ch_multiqc_files.mix( ADAPTERREMOVAL_PAIRED.out.log.collect{it[1]}, ADAPTERREMOVAL_SINGLE.out.log.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( + ADAPTERREMOVAL_PAIRED.out.log.collect{it[1]}, + ADAPTERREMOVAL_SINGLE.out.log.collect{it[1]} + ) emit: - reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] - versions = ch_versions // channel: [ versions.yml ] + reads = ch_adapterremoval_reads_prepped // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] mqc = ch_multiqc_files } + +def ensureFastQExtension(row) { + def (meta, read) = row + def filename = file(read.parent).resolve("${read.baseName}.fastq.gz") + + read.renameTo(filename.toString()) + return [meta, read] +} From f5baf910be4a7a6207c6070fef407873201d3ce4 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Sun, 3 Apr 2022 00:21:09 +0200 Subject: [PATCH 095/306] Apply suggestions from code review Co-authored-by: James A. Fellows Yates --- nextflow.config | 1 - subworkflows/local/shortread_adapterremoval.nf | 7 ------- 2 files changed, 8 deletions(-) diff --git a/nextflow.config b/nextflow.config index cd6de9c..bc66734 100644 --- a/nextflow.config +++ b/nextflow.config @@ -159,7 +159,6 @@ if (!params.igenomes_ignore) { env { PYTHONNOUSERSITE = '1' - // PYTHONUTF8 = '1' // MetaPhlAn3 may fail with a UnicodeDecodeError if the locale is not set appropriately. R_PROFILE_USER = "/.Rprofile" R_ENVIRON_USER = "/.Renviron" JULIA_DEPOT_PATH = "/usr/local/share/julia" diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 661062d..e522a1a 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -127,10 +127,3 @@ workflow SHORTREAD_ADAPTERREMOVAL { mqc = ch_multiqc_files } -def ensureFastQExtension(row) { - def (meta, read) = row - def filename = file(read.parent).resolve("${read.baseName}.fastq.gz") - - read.renameTo(filename.toString()) - return [meta, read] -} From d3572e18787eee40ed4e856bd6caf2f573246094 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 3 Apr 2022 07:28:01 +0200 Subject: [PATCH 096/306] Final MQC fix for AR2 (remove too-early collect) --- subworkflows/local/shortread_adapterremoval.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index b173e4c..bbb108e 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -77,7 +77,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { ch_versions = ch_versions.mix( ADAPTERREMOVAL_SINGLE.out.versions.first() ) ch_versions = ch_versions.mix( ADAPTERREMOVAL_PAIRED.out.versions.first() ) - ch_multiqc_files = ch_multiqc_files.mix( ADAPTERREMOVAL_PAIRED.out.log.collect{it[1]}, ADAPTERREMOVAL_SINGLE.out.log.collect{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( ADAPTERREMOVAL_PAIRED.out.log, ADAPTERREMOVAL_SINGLE.out.log ) emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] From 482112bb42ddd314de8e92b6a3c1a94e530828f7 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 3 Apr 2022 07:58:40 +0200 Subject: [PATCH 097/306] Start work on host removal --- modules.json | 11 ++- modules/nf-core/modules/bowtie2/align/main.nf | 77 +++++++++++++++++++ .../nf-core/modules/bowtie2/align/meta.yml | 51 ++++++++++++ modules/nf-core/modules/bowtie2/build/main.nf | 30 ++++++++ .../nf-core/modules/bowtie2/build/meta.yml | 33 ++++++++ .../nf-core/modules/samtools/flagstat/main.nf | 34 ++++++++ .../modules/samtools/flagstat/meta.yml | 49 ++++++++++++ nextflow.config | 4 + subworkflows/local/shortread_hostremoval.nf | 39 ++++++++++ workflows/taxprofiler.nf | 12 ++- 10 files changed, 336 insertions(+), 4 deletions(-) create mode 100644 modules/nf-core/modules/bowtie2/align/main.nf create mode 100644 modules/nf-core/modules/bowtie2/align/meta.yml create mode 100644 modules/nf-core/modules/bowtie2/build/main.nf create mode 100644 modules/nf-core/modules/bowtie2/build/meta.yml create mode 100644 modules/nf-core/modules/samtools/flagstat/main.nf create mode 100644 modules/nf-core/modules/samtools/flagstat/meta.yml create mode 100644 subworkflows/local/shortread_hostremoval.nf diff --git a/modules.json b/modules.json index dcfbd3f..7c3facc 100644 --- a/modules.json +++ b/modules.json @@ -6,6 +6,12 @@ "adapterremoval": { "git_sha": "f0800157544a82ae222931764483331a81812012" }, + "bowtie2/align": { + "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + }, + "bowtie2/build": { + "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + }, "cat/fastq": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, @@ -30,9 +36,12 @@ "porechop": { "git_sha": "e20e57f90b6787ac9a010a980cf6ea98bd990046" }, + "samtools/flagstat": { + "git_sha": "1ad73f1b2abdea9398680d6d20014838135c9a35" + }, "untar": { "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/bowtie2/align/main.nf b/modules/nf-core/modules/bowtie2/align/main.nf new file mode 100644 index 0000000..7e8a965 --- /dev/null +++ b/modules/nf-core/modules/bowtie2/align/main.nf @@ -0,0 +1,77 @@ +process BOWTIE2_ALIGN { + tag "$meta.id" + label 'process_high' + + conda (params.enable_conda ? 'bioconda::bowtie2=2.4.4 bioconda::samtools=1.14 conda-forge::pigz=2.6' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:4d235f41348a00533f18e47c9669f1ecb327f629-0' : + 'quay.io/biocontainers/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:4d235f41348a00533f18e47c9669f1ecb327f629-0' }" + + input: + tuple val(meta), path(reads) + path index + val save_unaligned + + output: + tuple val(meta), path('*.bam') , emit: bam + tuple val(meta), path('*.log') , emit: log + tuple val(meta), path('*fastq.gz'), emit: fastq, optional:true + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + if (meta.single_end) { + def unaligned = save_unaligned ? "--un-gz ${prefix}.unmapped.fastq.gz" : '' + """ + INDEX=`find -L ./ -name "*.rev.1.bt2" | sed 's/.rev.1.bt2//'` + bowtie2 \\ + -x \$INDEX \\ + -U $reads \\ + --threads $task.cpus \\ + $unaligned \\ + $args \\ + 2> ${prefix}.bowtie2.log \\ + | samtools view -@ $task.cpus $args2 -bhS -o ${prefix}.bam - + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bowtie2: \$(echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//') + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + pigz: \$( pigz --version 2>&1 | sed 's/pigz //g' ) + END_VERSIONS + """ + } else { + def unaligned = save_unaligned ? "--un-conc-gz ${prefix}.unmapped.fastq.gz" : '' + """ + INDEX=`find -L ./ -name "*.rev.1.bt2" | sed 's/.rev.1.bt2//'` + bowtie2 \\ + -x \$INDEX \\ + -1 ${reads[0]} \\ + -2 ${reads[1]} \\ + --threads $task.cpus \\ + $unaligned \\ + $args \\ + 2> ${prefix}.bowtie2.log \\ + | samtools view -@ $task.cpus $args2 -bhS -o ${prefix}.bam - + + if [ -f ${prefix}.unmapped.fastq.1.gz ]; then + mv ${prefix}.unmapped.fastq.1.gz ${prefix}.unmapped_1.fastq.gz + fi + if [ -f ${prefix}.unmapped.fastq.2.gz ]; then + mv ${prefix}.unmapped.fastq.2.gz ${prefix}.unmapped_2.fastq.gz + fi + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bowtie2: \$(echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//') + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + pigz: \$( pigz --version 2>&1 | sed 's/pigz //g' ) + END_VERSIONS + """ + } +} diff --git a/modules/nf-core/modules/bowtie2/align/meta.yml b/modules/nf-core/modules/bowtie2/align/meta.yml new file mode 100644 index 0000000..f80421e --- /dev/null +++ b/modules/nf-core/modules/bowtie2/align/meta.yml @@ -0,0 +1,51 @@ +name: bowtie2_align +description: Align reads to a reference genome using bowtie2 +keywords: + - align + - fasta + - genome + - reference +tools: + - bowtie2: + description: | + Bowtie 2 is an ultrafast and memory-efficient tool for aligning + sequencing reads to long reference sequences. + homepage: http://bowtie-bio.sourceforge.net/bowtie2/index.shtml + documentation: http://bowtie-bio.sourceforge.net/bowtie2/manual.shtml + doi: 10.1038/nmeth.1923 + licence: ["GPL-3.0-or-later"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + - index: + type: file + description: Bowtie2 genome index files + pattern: "*.ebwt" +output: + - bam: + type: file + description: Output BAM file containing read alignments + pattern: "*.{bam}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - fastq: + type: file + description: Unaligned FastQ files + pattern: "*.fastq.gz" + - log: + type: file + description: Aligment log + pattern: "*.log" +authors: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/modules/bowtie2/build/main.nf b/modules/nf-core/modules/bowtie2/build/main.nf new file mode 100644 index 0000000..a4da62d --- /dev/null +++ b/modules/nf-core/modules/bowtie2/build/main.nf @@ -0,0 +1,30 @@ +process BOWTIE2_BUILD { + tag "$fasta" + label 'process_high' + + conda (params.enable_conda ? 'bioconda::bowtie2=2.4.4' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bowtie2:2.4.4--py39hbb4e92a_0' : + 'quay.io/biocontainers/bowtie2:2.4.4--py39hbb4e92a_0' }" + + input: + path fasta + + output: + path 'bowtie2' , emit: index + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + """ + mkdir bowtie2 + bowtie2-build $args --threads $task.cpus $fasta bowtie2/${fasta.baseName} + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bowtie2: \$(echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/bowtie2/build/meta.yml b/modules/nf-core/modules/bowtie2/build/meta.yml new file mode 100644 index 0000000..2da9a21 --- /dev/null +++ b/modules/nf-core/modules/bowtie2/build/meta.yml @@ -0,0 +1,33 @@ +name: bowtie2_build +description: Builds bowtie index for reference genome +keywords: + - build + - index + - fasta + - genome + - reference +tools: + - bowtie2: + description: | + Bowtie 2 is an ultrafast and memory-efficient tool for aligning + sequencing reads to long reference sequences. + homepage: http://bowtie-bio.sourceforge.net/bowtie2/index.shtml + documentation: http://bowtie-bio.sourceforge.net/bowtie2/manual.shtml + doi: 10.1038/nmeth.1923 + licence: ["GPL-3.0-or-later"] +input: + - fasta: + type: file + description: Input genome fasta file +output: + - index: + type: file + description: Bowtie2 genome index files + pattern: "*.bt2" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/modules/samtools/flagstat/main.nf b/modules/nf-core/modules/samtools/flagstat/main.nf new file mode 100644 index 0000000..9e3440a --- /dev/null +++ b/modules/nf-core/modules/samtools/flagstat/main.nf @@ -0,0 +1,34 @@ +process SAMTOOLS_FLAGSTAT { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::samtools=1.15" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.15--h1170115_1' : + 'quay.io/biocontainers/samtools:1.15--h1170115_1' }" + + input: + tuple val(meta), path(bam), path(bai) + + output: + tuple val(meta), path("*.flagstat"), emit: flagstat + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + """ + samtools \\ + flagstat \\ + --threads ${task.cpus-1} \\ + $bam \\ + > ${bam}.flagstat + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/samtools/flagstat/meta.yml b/modules/nf-core/modules/samtools/flagstat/meta.yml new file mode 100644 index 0000000..9526906 --- /dev/null +++ b/modules/nf-core/modules/samtools/flagstat/meta.yml @@ -0,0 +1,49 @@ +name: samtools_flagstat +description: Counts the number of alignments in a BAM/CRAM/SAM file for each FLAG type +keywords: + - stats + - mapping + - counts + - bam + - sam + - cram +tools: + - samtools: + description: | + SAMtools is a set of utilities for interacting with and post-processing + short DNA sequence read alignments in the SAM, BAM and CRAM formats, written by Heng Li. + These files are generated as output by short read aligners like BWA. + homepage: http://www.htslib.org/ + documentation: hhttp://www.htslib.org/doc/samtools.html + doi: 10.1093/bioinformatics/btp352 + licence: ["MIT"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: BAM/CRAM/SAM file + pattern: "*.{bam,cram,sam}" + - bai: + type: file + description: Index for BAM/CRAM/SAM file + pattern: "*.{bai,crai,sai}" +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - flagstat: + type: file + description: File containing samtools flagstat output + pattern: "*.{flagstat}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@drpatelh" diff --git a/nextflow.config b/nextflow.config index 7be36a6..8aa8722 100644 --- a/nextflow.config +++ b/nextflow.config @@ -65,6 +65,10 @@ params { shortread_clipmerge_minlength = 15 longread_clip = false + // Host Removal + shortread_hostremoval_reference = null + shortread_hostremoval_index = null + // MALT run_malt = false malt_mode = 'BlastN' diff --git a/subworkflows/local/shortread_hostremoval.nf b/subworkflows/local/shortread_hostremoval.nf new file mode 100644 index 0000000..4b0861c --- /dev/null +++ b/subworkflows/local/shortread_hostremoval.nf @@ -0,0 +1,39 @@ +// +// Remove host reads via alignment and export off-target reads +// + +include { BOWTIE2_ALIGN } from '../../../modules/nf-core/modules/bowtie2/align/main' +include { BOWTIE2_BUILD } from '../../../modules/nf-core/modules/bowtie2/build/main' +include { SAMTOOLS_VIEW } from '../../../modules/nf-core/modules/samtools/view/main' +include { SAMTOOLS_FASTQ } from '../../../modules/nf-core/modules/samtools/fastq/main' +include { SAMTOOLS_FLAGSTAT } from '../../../modules/nf-core/modules/samtools/flagstat/main' + +workflow SHORTREAD_PREPROCESSING { + take: + reads // [ [ meta ], [ reads ] ] + reference // /path/to/fasta + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + if ( !params.shortread_hostremoval_index ) { + file( , checkIfExists: true ) + BOWTIE2_BUILD ( reference ) + ch_versions = ch_versions.mix( BOWTIE2_BUILD.out.versions ) + } + + BOWTIE2_ALIGN ( reads, BOWTIE2_BUILD.out.index ) + ch_versions = ch_versions.mix( BOWTIE2_BUILD.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( SAMTOOLS_FLAGSTAT.out.log ) + + SAMTOOLS_FLAGSTAT ( BOWTIE2_ALIGN.out.bam ) + ch_versions = ch_versions.mix( SAMTOOLS_FLAGSTAT.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( SAMTOOLS_FLAGSTAT.out.flagstat ) + + emit: + reads = BOWTIE2_ALIGN.out.fastq // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} + diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index ce89a91..c5678f1 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -11,15 +11,21 @@ WorkflowTaxprofiler.initialise(params, log) // TODO nf-core: Add all file path parameters for the pipeline to the list below // Check input path parameters to see if they exist -def checkPathParamList = [ params.input, params.databases, params.multiqc_config ] +def checkPathParamList = [ params.input, params.databases, params.shortread_hostremoval_reference, + params.shortread_hostremoval_index, params.multiqc_config + ] 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 samplesheet not specified!' } -if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } +if (params.input ) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet not specified!' } +if (params.databases ) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] warning: MALT does not except uncollapsed paired-reads. Pairs will be profiled as separate files." if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "[nf-core/taxprofiler] error: cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" +// TODO Add check if index but no reference exit 1 +if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } else { } +if (params.shortread_hostremoval_index) { ch_reference_index = file(params.shortread_hostremoval_index ) } else { ch_reference_index = [] } + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIG FILES From 066ceb2bcaeb21c0a2a07fd1a5359e30277869ce Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 3 Apr 2022 17:23:14 +0200 Subject: [PATCH 098/306] Remove flagstat as bowtie2 reports this itself --- conf/modules.config | 19 ++++++- conf/test.config | 12 +++-- modules.json | 3 -- .../nf-core/modules/samtools/flagstat/main.nf | 34 ------------- .../modules/samtools/flagstat/meta.yml | 49 ------------------- nextflow.config | 1 + nextflow_schema.json | 14 +++++- subworkflows/local/shortread_hostremoval.nf | 29 +++++------ workflows/taxprofiler.nf | 27 ++++++++-- 9 files changed, 72 insertions(+), 116 deletions(-) delete mode 100644 modules/nf-core/modules/samtools/flagstat/main.nf delete mode 100644 modules/nf-core/modules/samtools/flagstat/meta.yml diff --git a/conf/modules.config b/conf/modules.config index dc8b138..41faa62 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -132,7 +132,6 @@ process { ] } - withName: PORECHOP { ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -142,6 +141,24 @@ process { ] } + withName: BOWTIE2_BUILD { + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/bowtie2/build" }, + mode: 'copy', + pattern: '*.bt2' + ] + } + + withName: BOWTIE2_ALIGN { + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/bowtie2/align" }, + mode: 'copy', + pattern: '*.{fastq.gz,bam}' + ] + } + withName: CAT_FASTQ { publishDir = [ path: { "${params.outdir}/prepared_sequences" }, diff --git a/conf/test.config b/conf/test.config index 92a10e4..90ea241 100644 --- a/conf/test.config +++ b/conf/test.config @@ -22,10 +22,12 @@ params { // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' - databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - run_kraken2 = true - run_malt = true - shortread_clipmerge = true + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' + run_kraken2 = true + run_malt = true + shortread_clipmerge = true + shortread_hostremoval = true + shortread_hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' } diff --git a/modules.json b/modules.json index 7c3facc..7395d68 100644 --- a/modules.json +++ b/modules.json @@ -36,9 +36,6 @@ "porechop": { "git_sha": "e20e57f90b6787ac9a010a980cf6ea98bd990046" }, - "samtools/flagstat": { - "git_sha": "1ad73f1b2abdea9398680d6d20014838135c9a35" - }, "untar": { "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" } diff --git a/modules/nf-core/modules/samtools/flagstat/main.nf b/modules/nf-core/modules/samtools/flagstat/main.nf deleted file mode 100644 index 9e3440a..0000000 --- a/modules/nf-core/modules/samtools/flagstat/main.nf +++ /dev/null @@ -1,34 +0,0 @@ -process SAMTOOLS_FLAGSTAT { - tag "$meta.id" - label 'process_low' - - conda (params.enable_conda ? "bioconda::samtools=1.15" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/samtools:1.15--h1170115_1' : - 'quay.io/biocontainers/samtools:1.15--h1170115_1' }" - - input: - tuple val(meta), path(bam), path(bai) - - output: - tuple val(meta), path("*.flagstat"), emit: flagstat - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - """ - samtools \\ - flagstat \\ - --threads ${task.cpus-1} \\ - $bam \\ - > ${bam}.flagstat - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') - END_VERSIONS - """ -} diff --git a/modules/nf-core/modules/samtools/flagstat/meta.yml b/modules/nf-core/modules/samtools/flagstat/meta.yml deleted file mode 100644 index 9526906..0000000 --- a/modules/nf-core/modules/samtools/flagstat/meta.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: samtools_flagstat -description: Counts the number of alignments in a BAM/CRAM/SAM file for each FLAG type -keywords: - - stats - - mapping - - counts - - bam - - sam - - cram -tools: - - samtools: - description: | - SAMtools is a set of utilities for interacting with and post-processing - short DNA sequence read alignments in the SAM, BAM and CRAM formats, written by Heng Li. - These files are generated as output by short read aligners like BWA. - homepage: http://www.htslib.org/ - documentation: hhttp://www.htslib.org/doc/samtools.html - doi: 10.1093/bioinformatics/btp352 - licence: ["MIT"] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM/CRAM/SAM file - pattern: "*.{bam,cram,sam}" - - bai: - type: file - description: Index for BAM/CRAM/SAM file - pattern: "*.{bai,crai,sai}" -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - flagstat: - type: file - description: File containing samtools flagstat output - pattern: "*.{flagstat}" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@drpatelh" diff --git a/nextflow.config b/nextflow.config index 8aa8722..e559a85 100644 --- a/nextflow.config +++ b/nextflow.config @@ -66,6 +66,7 @@ params { longread_clip = false // Host Removal + shortread_hostremoval = false shortread_hostremoval_reference = null shortread_hostremoval_index = null diff --git a/nextflow_schema.json b/nextflow_schema.json index fb2ca31..0b5162b 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -266,8 +266,7 @@ "type": "boolean" }, "shortread_clipmerge_excludeunmerged": { - "type": "boolean", - "default": false + "type": "boolean" }, "longread_clip": { "type": "boolean" @@ -304,6 +303,17 @@ "shortread_clipmerge_minlength": { "type": "integer", "default": 15 + }, + "shortread_hostremoval": { + "type": "boolean" + }, + "shortread_hostremoval_reference": { + "type": "string", + "default": null + }, + "shortread_hostremoval_index": { + "type": "string", + "default": null } } } diff --git a/subworkflows/local/shortread_hostremoval.nf b/subworkflows/local/shortread_hostremoval.nf index 4b0861c..505f989 100644 --- a/subworkflows/local/shortread_hostremoval.nf +++ b/subworkflows/local/shortread_hostremoval.nf @@ -2,38 +2,33 @@ // Remove host reads via alignment and export off-target reads // -include { BOWTIE2_ALIGN } from '../../../modules/nf-core/modules/bowtie2/align/main' -include { BOWTIE2_BUILD } from '../../../modules/nf-core/modules/bowtie2/build/main' -include { SAMTOOLS_VIEW } from '../../../modules/nf-core/modules/samtools/view/main' -include { SAMTOOLS_FASTQ } from '../../../modules/nf-core/modules/samtools/fastq/main' -include { SAMTOOLS_FLAGSTAT } from '../../../modules/nf-core/modules/samtools/flagstat/main' +include { BOWTIE2_BUILD } from '../../modules/nf-core/modules/bowtie2/build/main' +include { BOWTIE2_ALIGN } from '../../modules/nf-core/modules/bowtie2/align/main' -workflow SHORTREAD_PREPROCESSING { +workflow SHORTREAD_HOSTREMOVAL { take: reads // [ [ meta ], [ reads ] ] reference // /path/to/fasta + index // /path/to/index main: ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() if ( !params.shortread_hostremoval_index ) { - file( , checkIfExists: true ) - BOWTIE2_BUILD ( reference ) - ch_versions = ch_versions.mix( BOWTIE2_BUILD.out.versions ) + ch_bowtie2_index = BOWTIE2_BUILD ( reference ).index + ch_versions = ch_versions.mix( BOWTIE2_BUILD.out.versions ) + } else { + ch_bowtie2_index = index.first() } - BOWTIE2_ALIGN ( reads, BOWTIE2_BUILD.out.index ) - ch_versions = ch_versions.mix( BOWTIE2_BUILD.out.versions ) - ch_multiqc_files = ch_multiqc_files.mix( SAMTOOLS_FLAGSTAT.out.log ) - - SAMTOOLS_FLAGSTAT ( BOWTIE2_ALIGN.out.bam ) - ch_versions = ch_versions.mix( SAMTOOLS_FLAGSTAT.out.versions ) - ch_multiqc_files = ch_multiqc_files.mix( SAMTOOLS_FLAGSTAT.out.flagstat ) + BOWTIE2_ALIGN ( reads, ch_bowtie2_index, true ) + ch_versions = ch_versions.mix( BOWTIE2_ALIGN.out.versions.first() ) + ch_multiqc_files = ch_multiqc_files.mix( BOWTIE2_ALIGN.out.log ) emit: reads = BOWTIE2_ALIGN.out.fastq // channel: [ val(meta), [ reads ] ] - versions = ch_versions // channel: [ versions.yml ] + versions = ch_versions // channel: [ versions.yml ] mqc = ch_multiqc_files } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index c5678f1..631aee6 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -23,8 +23,11 @@ if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-cor if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "[nf-core/taxprofiler] error: cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" // TODO Add check if index but no reference exit 1 -if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } else { } -if (params.shortread_hostremoval_index) { ch_reference_index = file(params.shortread_hostremoval_index ) } else { ch_reference_index = [] } +if (params.shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } +if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } + +if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } else { ch_reference = [] } +if (params.shortread_hostremoval_index ) { ch_reference_index = file(params.shortread_hostremoval_index ) } else { ch_reference_index = [] } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -49,6 +52,7 @@ include { INPUT_CHECK } from '../subworkflows/local/input_check' include { DB_CHECK } from '../subworkflows/local/db_check' include { SHORTREAD_PREPROCESSING } from '../subworkflows/local/shortread_preprocessing' include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' +include { SHORTREAD_HOSTREMOVAL } from '../subworkflows/local/shortread_hostremoval' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -120,17 +124,24 @@ workflow TAXPROFILER { if ( params.longread_clip ) { ch_longreads_preprocessed = LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ).reads .map { it -> [ it[0], [it[1]] ] } - ch_versions = ch_versions.mix(LONGREAD_PREPROCESSING.out.versions.first()) - } else { + ch_versions = ch_versions.mix(LONGREAD_PREPROCESSING.out.versions.first()) + } else {SHORTREAD_HOSTREMOVAL ch_longreads_preprocessed = INPUT_CHECK.out.nanopore } + if ( params.shortread_hostremoval ) { + ch_shortreads_hostremoved = SHORTREAD_HOSTREMOVAL ( ch_shortreads_preprocessed, ch_reference, ch_reference_index ).reads + ch_versions = ch_versions.mix(SHORTREAD_HOSTREMOVAL.out.versions.first()) + } else { + ch_shortreads_hostremoved = ch_shortreads_preprocessed + } + /* COMBINE READS WITH POSSIBLE DATABASES */ // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] - ch_input_for_profiling = ch_shortreads_preprocessed + ch_input_for_profiling = ch_shortreads_hostremoved .mix( ch_longreads_preprocessed ) .combine(DB_CHECK.out.dbs) .branch { @@ -196,9 +207,15 @@ workflow TAXPROFILER { if (params.shortread_clipmerge) { ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_PREPROCESSING.out.mqc) } + + if (params.shortread_hostremoval) { + ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_HOSTREMOVAL.out.mqc.collect{it[1]}.ifEmpty([])) + } + if (params.longread_clip) { ch_multiqc_files = ch_multiqc_files.mix(LONGREAD_PREPROCESSING.out.mqc) } + if (params.run_kraken2) { ch_multiqc_files = ch_multiqc_files.mix(KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([])) ch_versions = ch_versions.mix(KRAKEN2_KRAKEN2.out.versions.first()) From a76576c16b44be09548f8f8c6bede81aef2a4d99 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 3 Apr 2022 17:24:50 +0200 Subject: [PATCH 099/306] Prettier --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 398c351..cd1a0a9 100644 --- a/modules.json +++ b/modules.json @@ -44,4 +44,4 @@ } } } -} \ No newline at end of file +} From 3a562065138a4ef9035e43b558c844f58a36611d Mon Sep 17 00:00:00 2001 From: sofstam Date: Mon, 4 Apr 2022 16:55:48 +0200 Subject: [PATCH 100/306] Centrifuge classification --- nextflow.config | 2 +- nextflow_schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index 725f892..a99481c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -74,7 +74,7 @@ params { // centrifuge run_centrifuge = false - centrifuge_db_name = false + centrifuge_db_name = 'minigut_cf' centrifuge_save_unaligned = false centrifuge_save_aligned = false centrifuge_sam_format = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 3ec2aae..777b82f 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -336,7 +336,7 @@ }, "centrifuge_db_name": { "type": "string", - "default": "false" + "default": null } } } \ No newline at end of file From a384162810cb1fe1d800d7c3f2053f01c25083da Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 4 Apr 2022 21:16:51 +0200 Subject: [PATCH 101/306] Add prinseq as alternative complexity filtering --- CITATIONS.md | 12 ++-- conf/modules.config | 15 ++++- modules.json | 5 +- .../nf-core/modules/prinseqplusplus/main.nf | 61 +++++++++++++++++++ .../nf-core/modules/prinseqplusplus/meta.yml | 60 ++++++++++++++++++ nextflow.config | 13 ++-- nextflow_schema.json | 37 ++++++++--- .../local/shortread_complexityfiltering.nf | 6 +- 8 files changed, 189 insertions(+), 20 deletions(-) create mode 100644 modules/nf-core/modules/prinseqplusplus/main.nf create mode 100644 modules/nf-core/modules/prinseqplusplus/meta.yml diff --git a/CITATIONS.md b/CITATIONS.md index 0eb8141..ec424b1 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -18,23 +18,27 @@ - [fastp](https://doi.org/10.1093/bioinformatics/bty560) - > Chen, Shifu, Yanqing Zhou, Yaru Chen, and Jia Gu. 2018. “Fastp: An Ultra-Fast All-in-One FASTQ Preprocessor.” Bioinformatics 34 (17): i884-90. 10.1093/bioinformatics/bty560. + > Chen, Shifu, Yanqing Zhou, Yaru Chen, and Jia Gu. 2018. Fastp: An Ultra-Fast All-in-One FASTQ Preprocessor. Bioinformatics 34 (17): i884-90. 10.1093/bioinformatics/bty560. - [AdapterRemoval2](https://doi.org/10.1186/s13104-016-1900-2) - > Schubert, Mikkel, Stinus Lindgreen, and Ludovic Orlando. 2016. “AdapterRemoval v2: Rapid Adapter Trimming, Identification, and Read Merging.” BMC Research Notes 9 (February): 88. doi:10.1186/s13104-016-1900-2. + > Schubert, Mikkel, Stinus Lindgreen, and Ludovic Orlando. 2016. AdapterRemoval v2: Rapid Adapter Trimming, Identification, and Read Merging. BMC Research Notes 9 (February): 88. doi:10.1186/s13104-016-1900-2. - [Porechop](https://github.com/rrwick/Porechop) - [BBTools](http://sourceforge.net/projects/bbmap/) +- [PRINSEQ++](https://doi.org/10.7287/peerj.preprints.27553v1) + + > Cantu, Vito Adrian, Jeffrey Sadural, and Robert Edwards. 2019. PRINSEQ++, a Multi-Threaded Tool for Fast and Efficient Quality Control and Preprocessing of Sequencing Datasets. e27553v1. PeerJ Preprints. doi: 10.7287/peerj.preprints.27553v1. + - [Kraken2](https://doi.org/10.1186/s13059-019-1891-0) - > Wood, Derrick E., Jennifer Lu, and Ben Langmead. 2019. “Improved Metagenomic Analysis with Kraken 2.” Genome Biology 20 (1): 257. doi: 10.1186/s13059-019-1891-0. + > Wood, Derrick E., Jennifer Lu, and Ben Langmead. 2019. Improved Metagenomic Analysis with Kraken 2. Genome Biology 20 (1): 257. doi: 10.1186/s13059-019-1891-0. - [MALT](https://doi.org/10.1038/s41559-017-0446-6) - > Vågene, Åshild J., Alexander Herbig, Michael G. Campana, Nelly M. Robles García, Christina Warinner, Susanna Sabin, Maria A. Spyrou, et al. 2018. “Salmonella Enterica Genomes from Victims of a Major Sixteenth-Century Epidemic in Mexico.” Nature Ecology & Evolution 2 (3): 520-28. doi: 10.1038/s41559-017-0446-6. + > Vågene, Åshild J., Alexander Herbig, Michael G. Campana, Nelly M. Robles García, Christina Warinner, Susanna Sabin, Maria A. Spyrou, et al. 2018. Salmonella Enterica Genomes from Victims of a Major Sixteenth-Century Epidemic in Mexico. Nature Ecology & Evolution 2 (3): 520-28. doi: 10.1038/s41559-017-0446-6. ## Software packaging/containerisation tools diff --git a/conf/modules.config b/conf/modules.config index 601f915..9d2edc3 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -143,7 +143,7 @@ process { withName: BBMAP_BBDUK { ext.args = [ - "entropy=${params.shortread_complexityfilter_bbduk_entropy}", + "entropy=${params.shortread_complexityfilter_entropy}", "entropywindow=${params.shortread_complexityfilter_bbduk_windowsize}", params.shortread_complexityfilter_bbduk_mask ? "entropymask=t" : "entropymask=f" ].join(' ').trim() @@ -155,6 +155,19 @@ process { ] } + withName: PRINSEQPLUSPLUS { + ext.args = [ + params.shortread_complexityfilter_prinseqplusplus_mode == 'dust' ? "-lc_dust=${params.shortread_complexityfilter_prinseqplusplus_dustscore}" : "-lc_entropy=${params.shortread_complexityfilter_entropy}", + "-trim_qual_left=0 -trim_qual_left=0 -trim_qual_window=0 -trim_qual_step=0" + ].join(' ').trim() + ext.prefix = { "${meta.id}-${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/prinseqplusplus/" }, + mode: params.publish_dir_mode, + pattern: '*{_good_out.fastq.gz,_good_out_R1.fastq.gz,_good_out_R2.fastq.gz,log}' + ] + } + withName: MALT_RUN { ext.args = { "${meta.db_params}" } ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } diff --git a/modules.json b/modules.json index 64e6c3c..355dc9e 100644 --- a/modules.json +++ b/modules.json @@ -33,9 +33,12 @@ "porechop": { "git_sha": "e20e57f90b6787ac9a010a980cf6ea98bd990046" }, + "prinseqplusplus": { + "git_sha": "f1c5384c31e985591716afdd732cf8c2ae29d05b" + }, "untar": { "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/prinseqplusplus/main.nf b/modules/nf-core/modules/prinseqplusplus/main.nf new file mode 100644 index 0000000..ebd8c58 --- /dev/null +++ b/modules/nf-core/modules/prinseqplusplus/main.nf @@ -0,0 +1,61 @@ +process PRINSEQPLUSPLUS { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::prinseq-plus-plus=1.2.3" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/prinseq-plus-plus:1.2.3--hc90279e_1': + 'quay.io/biocontainers/prinseq-plus-plus:1.2.3--hc90279e_1' }" + + input: + tuple val(meta), path(reads) + + output: + tuple val(meta), path("*_good_out*.fastq.gz") , emit: good_reads + tuple val(meta), path("*_single_out*.fastq.gz"), optional: true, emit: single_reads + tuple val(meta), path("*_bad_out*.fastq.gz") , optional: true, emit: bad_reads + tuple val(meta), path("*.log") , emit: log + 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}" + + if (meta.single_end) { + """ + prinseq++ \\ + -threads $task.cpus \\ + -fastq ${reads} \\ + -out_name ${prefix} \\ + -out_gz \\ + -VERBOSE 1 \\ + $args \\ + | tee ${prefix}.log + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + prinseqplusplus: \$(echo \$(prinseq++ --version | cut -f 2 -d ' ' )) + END_VERSIONS + """ + } else { + """ + prinseq++ \\ + -threads $task.cpus \\ + -fastq ${reads[0]} \\ + -fastq2 ${reads[1]} \\ + -out_name ${prefix} \\ + -out_gz \\ + -VERBOSE 1 \\ + $args \\ + | tee ${prefix}.log + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + prinseqplusplus: \$(echo \$(prinseq++ --version | cut -f 2 -d ' ' )) + END_VERSIONS + """ + } +} diff --git a/modules/nf-core/modules/prinseqplusplus/meta.yml b/modules/nf-core/modules/prinseqplusplus/meta.yml new file mode 100644 index 0000000..8155df9 --- /dev/null +++ b/modules/nf-core/modules/prinseqplusplus/meta.yml @@ -0,0 +1,60 @@ +name: "prinseqplusplus" +description: PRINSEQ++ is a C++ implementation of the prinseq-lite.pl program. It can be used to filter, reformat or trim genomic and metagenomic sequence data +keywords: + - fastq + - fasta + - filter + - trim +tools: + - "prinseqplusplus": + description: "PRINSEQ++ - Multi-threaded C++ sequence cleaning" + homepage: "https://github.com/Adrian-Cantu/PRINSEQ-plus-plus" + documentation: "https://github.com/Adrian-Cantu/PRINSEQ-plus-plus" + tool_dev_url: "https://github.com/Adrian-Cantu/PRINSEQ-plus-plus" + doi: "10.7287/peerj.preprints.27553v1" + licence: "['GPL v2']" + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end + data, respectively. + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - good_reads: + type: file + description: Reads passing filter(s) in gzipped FASTQ format + pattern: "*_good_out_{R1,R2}.fastq.gz" + - single_reads: + type: file + description: | + Single reads without the pair passing filter(s) in gzipped FASTQ format + pattern: "*_single_out_{R1,R2}.fastq.gz" + - bad_reads: + type: file + description: | + Reads without not passing filter(s) in gzipped FASTQ format + pattern: "*_bad_out_{R1,R2}.fastq.gz" + - log: + type: file + description: | + Verbose level 2 STDOUT information in a log file + pattern: "*.log" + +authors: + - "@jfy133" diff --git a/nextflow.config b/nextflow.config index d57743a..03dfbb9 100644 --- a/nextflow.config +++ b/nextflow.config @@ -66,11 +66,14 @@ params { longread_clip = false // Complexity filtering - shortread_complexityfilter = false - shortread_complexityfilter_tool = 'bbduk' - shortread_complexityfilter_bbduk_entropy = 0.3 - shortread_complexityfilter_bbduk_windowsize = 50 - shortread_complexityfilter_bbduk_mask = false + shortread_complexityfilter = false + shortread_complexityfilter_tool = 'bbduk' + shortread_complexityfilter_entropy = 0.3 + shortread_complexityfilter_bbduk_windowsize = 50 + shortread_complexityfilter_bbduk_mask = false + shortread_complexityfilter_prinseqplusplus_mode = 'entropy' + shortread_complexityfilter_prinseqplusplus_dustscore = 0.5 + // MALT run_malt = false diff --git a/nextflow_schema.json b/nextflow_schema.json index bd8b438..6dee045 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -284,7 +294,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -308,10 +321,6 @@ "type": "string", "default": "bbduk" }, - "shortread_complexityfilter_bbduk_entropy": { - "type": "number", - "default": 0.3 - }, "shortread_complexityfilter_bbduk_windowsize": { "type": "integer", "default": 50 @@ -321,6 +330,18 @@ }, "shortread_complexityfilter": { "type": "boolean" + }, + "shortread_complexityfilter_entropy": { + "type": "number", + "default": 0.3 + }, + "shortread_complexityfilter_prinseqplusplus_mode": { + "type": "string", + "default": "entropy" + }, + "shortread_complexityfilter_prinseqplusplus_dustscore": { + "type": "number", + "default": 0.5 } } -} +} \ No newline at end of file diff --git a/subworkflows/local/shortread_complexityfiltering.nf b/subworkflows/local/shortread_complexityfiltering.nf index 3d69ed6..12686d7 100644 --- a/subworkflows/local/shortread_complexityfiltering.nf +++ b/subworkflows/local/shortread_complexityfiltering.nf @@ -2,7 +2,8 @@ // Check input samplesheet and get read channels // -include { BBMAP_BBDUK } from '../../modules/nf-core/modules/bbmap/bbduk/main' +include { BBMAP_BBDUK } from '../../modules/nf-core/modules/bbmap/bbduk/main' +include { PRINSEQPLUSPLUS } from '../../modules/nf-core/modules/prinseqplusplus/main' workflow SHORTREAD_COMPLEXITYFILTERING { take: @@ -16,6 +17,9 @@ workflow SHORTREAD_COMPLEXITYFILTERING { ch_filtered_reads = BBMAP_BBDUK ( reads, [] ).reads ch_versions = ch_versions.mix( BBMAP_BBDUK.out.versions.first() ) ch_multiqc_files = ch_multiqc_files.mix( BBMAP_BBDUK.out.log ) + } else if ( params.shortread_complexityfilter_tool == 'prinseqplusplus' ) { + ch_filtered_reads = PRINSEQPLUSPLUS ( reads ).good_reads + ch_versions = ch_versions.mix( PRINSEQPLUSPLUS.out.versions.first() ) } else { ch_filtered_reads = reads } From 613f6a5565dbaaf6bef06ba980a6b017506490f7 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 4 Apr 2022 21:21:29 +0200 Subject: [PATCH 102/306] Prettier --- modules.json | 2 +- nextflow_schema.json | 21 ++++----------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/modules.json b/modules.json index 355dc9e..d8ac3db 100644 --- a/modules.json +++ b/modules.json @@ -41,4 +41,4 @@ } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index 6dee045..75e1abe 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -294,10 +284,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -344,4 +331,4 @@ "default": 0.5 } } -} \ No newline at end of file +} From c54c32c4947df993231c364ac3fe19f2abb85eed Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Tue, 5 Apr 2022 10:18:34 +0200 Subject: [PATCH 103/306] chore: update adapterremoval --- modules.json | 4 +- .../nf-core/modules/adapterremoval/main.nf | 44 ++++++++++++++----- .../nf-core/modules/adapterremoval/meta.yml | 14 +++--- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/modules.json b/modules.json index 9953edb..51f13ea 100644 --- a/modules.json +++ b/modules.json @@ -4,7 +4,7 @@ "repos": { "nf-core/modules": { "adapterremoval": { - "git_sha": "f0800157544a82ae222931764483331a81812012" + "git_sha": "879d42c5e28661fe0a5e744c9e2c515868f9e08a" }, "cat/fastq": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" @@ -38,4 +38,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/adapterremoval/main.nf b/modules/nf-core/modules/adapterremoval/main.nf index 9d16b9c..0e17c05 100644 --- a/modules/nf-core/modules/adapterremoval/main.nf +++ b/modules/nf-core/modules/adapterremoval/main.nf @@ -12,15 +12,14 @@ process ADAPTERREMOVAL { path(adapterlist) output: - tuple val(meta), path("${prefix}.truncated.gz") , optional: true, emit: singles_truncated - tuple val(meta), path("${prefix}.discarded.gz") , optional: true, emit: discarded - tuple val(meta), path("${prefix}.pair1.truncated.gz") , optional: true, emit: pair1_truncated - tuple val(meta), path("${prefix}.pair2.truncated.gz") , optional: true, emit: pair2_truncated - tuple val(meta), path("${prefix}.collapsed.gz") , optional: true, emit: collapsed - tuple val(meta), path("${prefix}.collapsed.truncated.gz") , optional: true, emit: collapsed_truncated - tuple val(meta), path("${prefix}.paired.gz") , optional: true, emit: paired_interleaved - tuple val(meta), path('*.log') , emit: log - path "versions.yml" , emit: versions + tuple val(meta), path("${prefix}.truncated.fastq.gz") , optional: true, emit: singles_truncated + tuple val(meta), path("${prefix}.discarded.fastq.gz") , optional: true, emit: discarded + tuple val(meta), path("${prefix}.pair{1,2}.truncated.fastq.gz") , optional: true, emit: paired_truncated + tuple val(meta), path("${prefix}.collapsed.fastq.gz") , optional: true, emit: collapsed + tuple val(meta), path("${prefix}.collapsed.truncated.fastq.gz") , optional: true, emit: collapsed_truncated + tuple val(meta), path("${prefix}.paired.fastq.gz") , optional: true, emit: paired_interleaved + tuple val(meta), path('*.settings') , emit: settings + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -38,10 +37,19 @@ process ADAPTERREMOVAL { $adapterlist \\ --basename ${prefix} \\ --threads ${task.cpus} \\ - --settings ${prefix}.log \\ --seed 42 \\ --gzip + ensure_fastq() { + if [ -f "\${1}" ]; then + mv "\${1}" "\${1::-3}.fastq.gz" + fi + + } + + ensure_fastq '${prefix}.truncated.gz' + ensure_fastq '${prefix}.discarded.gz' + cat <<-END_VERSIONS > versions.yml "${task.process}": adapterremoval: \$(AdapterRemoval --version 2>&1 | sed -e "s/AdapterRemoval ver. //g") @@ -56,10 +64,24 @@ process ADAPTERREMOVAL { $adapterlist \\ --basename ${prefix} \\ --threads $task.cpus \\ - --settings ${prefix}.log \\ --seed 42 \\ --gzip + ensure_fastq() { + if [ -f "\${1}" ]; then + mv "\${1}" "\${1::-3}.fastq.gz" + fi + + } + + ensure_fastq '${prefix}.truncated.gz' + ensure_fastq '${prefix}.discarded.gz' + ensure_fastq '${prefix}.pair1.truncated.gz' + ensure_fastq '${prefix}.pair2.truncated.gz' + ensure_fastq '${prefix}.collapsed.gz' + ensure_fastq '${prefix}.collapsed.truncated.gz' + ensure_fastq '${prefix}.paired.gz' + cat <<-END_VERSIONS > versions.yml "${task.process}": adapterremoval: \$(AdapterRemoval --version 2>&1 | sed -e "s/AdapterRemoval ver. //g") diff --git a/modules/nf-core/modules/adapterremoval/meta.yml b/modules/nf-core/modules/adapterremoval/meta.yml index 5faad04..77273f6 100644 --- a/modules/nf-core/modules/adapterremoval/meta.yml +++ b/modules/nf-core/modules/adapterremoval/meta.yml @@ -43,43 +43,43 @@ output: Adapter trimmed FastQ files of either single-end reads, or singleton 'orphaned' reads from merging of paired-end data (i.e., one of the pair was lost due to filtering thresholds). - pattern: "*.truncated.gz" + pattern: "*.truncated.fastq.gz" - discarded: type: file description: | Adapter trimmed FastQ files of reads that did not pass filtering thresholds. - pattern: "*.discarded.gz" + pattern: "*.discarded.fastq.gz" - pair1_truncated: type: file description: | Adapter trimmed R1 FastQ files of paired-end reads that did not merge with their respective R2 pair due to long templates. The respective pair is stored in 'pair2_truncated'. - pattern: "*.pair1.truncated.gz" + pattern: "*.pair1.truncated.fastq.gz" - pair2_truncated: type: file description: | Adapter trimmed R2 FastQ files of paired-end reads that did not merge with their respective R1 pair due to long templates. The respective pair is stored in 'pair1_truncated'. - pattern: "*.pair2.truncated.gz" + pattern: "*.pair2.truncated.fastq.gz" - collapsed: type: file description: | Collapsed FastQ of paired-end reads that successfully merged with their respective R1 pair but were not trimmed. - pattern: "*.collapsed.gz" + pattern: "*.collapsed.fastq.gz" - collapsed_truncated: type: file description: | Collapsed FastQ of paired-end reads that successfully merged with their respective R1 pair and were trimmed of adapter due to sufficient overlap. - pattern: "*.collapsed.truncated.gz" + pattern: "*.collapsed.truncated.fastq.gz" - log: type: file description: AdapterRemoval log file - pattern: "*.log" + pattern: "*.settings" - versions: type: file description: File containing software versions From 0e9b2989e3fd59b6e05a77a8faf6ed89d6ddea03 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Tue, 5 Apr 2022 10:53:04 +0200 Subject: [PATCH 104/306] refactor: remove superfluous ENSURE_FASTQ_EXTENSION --- .../local/shortread_adapterremoval.nf | 75 ++++--------------- 1 file changed, 16 insertions(+), 59 deletions(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index e522a1a..8c02f6f 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -5,11 +5,6 @@ Process short raw reads with AdapterRemoval include { ADAPTERREMOVAL as ADAPTERREMOVAL_SINGLE } from '../../modules/nf-core/modules/adapterremoval/main' include { ADAPTERREMOVAL as ADAPTERREMOVAL_PAIRED } from '../../modules/nf-core/modules/adapterremoval/main' include { CAT_FASTQ } from '../../modules/nf-core/modules/cat/fastq/main' -include { - ENSURE_FASTQ_EXTENSION as ENSURE_FASTQ_EXTENSION1; - ENSURE_FASTQ_EXTENSION as ENSURE_FASTQ_EXTENSION2; - ENSURE_FASTQ_EXTENSION as ENSURE_FASTQ_EXTENSION3; -} from '../../modules/local/ensure_fastq_extension' workflow SHORTREAD_ADAPTERREMOVAL { @@ -36,34 +31,25 @@ workflow SHORTREAD_ADAPTERREMOVAL { if ( params.shortread_clipmerge_mergepairs && !params.shortread_clipmerge_excludeunmerged ) { - ENSURE_FASTQ_EXTENSION1( - Channel.empty().mix( + ch_concat_fastq = Channel.empty() + .mix( ADAPTERREMOVAL_PAIRED.out.collapsed, ADAPTERREMOVAL_PAIRED.out.collapsed_truncated, ADAPTERREMOVAL_PAIRED.out.singles_truncated, - ADAPTERREMOVAL_PAIRED.out.pair1_truncated, - ADAPTERREMOVAL_PAIRED.out.pair2_truncated + ADAPTERREMOVAL_PAIRED.out.paired_truncated ) - .map { meta, reads -> - meta.single_end = true - [meta, reads] - } - ) + .groupTuple() + .map { [it.head(), it.tail().flatten()] } - CAT_FASTQ( - ENSURE_FASTQ_EXTENSION1.out.reads - .groupTuple() - ) - - ENSURE_FASTQ_EXTENSION2(ADAPTERREMOVAL_SINGLE.out.singles_truncated) + CAT_FASTQ(ch_concat_fastq) ch_adapterremoval_reads_prepped = CAT_FASTQ.out.reads - .mix(ENSURE_FASTQ_EXTENSION2.out.reads) + .mix(ADAPTERREMOVAL_SINGLE.out.singles_truncated) } else if ( params.shortread_clipmerge_mergepairs && params.shortread_clipmerge_excludeunmerged ) { - ENSURE_FASTQ_EXTENSION1( - Channel.empty().mix( + ch_concat_fastq = Channel.empty() + .mix( ADAPTERREMOVAL_PAIRED.out.collapsed, ADAPTERREMOVAL_PAIRED.out.collapsed_truncated ) @@ -71,54 +57,25 @@ workflow SHORTREAD_ADAPTERREMOVAL { meta.single_end = true [meta, reads] } - ) + .groupTuple() - CAT_FASTQ( - ENSURE_FASTQ_EXTENSION1.out.reads - .groupTuple() - ) - - ENSURE_FASTQ_EXTENSION2(ADAPTERREMOVAL_SINGLE.out.singles_truncated) + CAT_FASTQ(ch_concat_fastq) ch_adapterremoval_reads_prepped = CAT_FASTQ.out.reads - .mix(ENSURE_FASTQ_EXTENSION2.out.reads) + .mix(ADAPTERREMOVAL_SINGLE.out.singles_truncated) } else { - ENSURE_FASTQ_EXTENSION1( - ADAPTERREMOVAL_PAIRED.out.pair1_truncated - .map { meta, reads -> - meta.single_end = true - [meta, reads] - } - ) - - ENSURE_FASTQ_EXTENSION2( - ADAPTERREMOVAL_PAIRED.out.pair2_truncated - .map { meta, reads -> - meta.single_end = true - [meta, reads] - } - ) - - ENSURE_FASTQ_EXTENSION3(ADAPTERREMOVAL_SINGLE.out.singles_truncated) - - ch_adapterremoval_reads_prepped = ENSURE_FASTQ_EXTENSION1.out.reads - .join(ENSURE_FASTQ_EXTENSION2.out.reads) - .groupTuple() - .map { meta, pair1, pair2 -> - meta.single_end = false - [ meta, [ pair1, pair2 ].flatten() ] - } - .mix(ENSURE_FASTQ_EXTENSION3.out.reads) + ch_adapterremoval_reads_prepped = ADAPTERREMOVAL_PAIRED.out.paired_truncated + .mix(ADAPTERREMOVAL_SINGLE.out.singles_truncated) } ch_versions = ch_versions.mix( ADAPTERREMOVAL_SINGLE.out.versions.first() ) ch_versions = ch_versions.mix( ADAPTERREMOVAL_PAIRED.out.versions.first() ) ch_multiqc_files = ch_multiqc_files.mix( - ADAPTERREMOVAL_PAIRED.out.log.collect{it[1]}, - ADAPTERREMOVAL_SINGLE.out.log.collect{it[1]} + ADAPTERREMOVAL_PAIRED.out.settings.collect{it[1]}, + ADAPTERREMOVAL_SINGLE.out.settings.collect{it[1]} ) emit: From d46ddd972c2fc2c3ce0e7fe03b3ad42a4821332a Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 5 Apr 2022 11:04:30 +0200 Subject: [PATCH 105/306] Expand tests and fix schema after review --- .github/workflows/ci.yml | 8 ++++++++ conf/test.config | 13 +++++++------ nextflow.config | 2 +- nextflow_schema.json | 3 ++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8975b5..79148f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,16 @@ jobs: - NXF_VER: "" NXF_EDGE: "1" parameters: + - "--longread_clip false" + - "--shortread_clip false" - "--shortread_clipmerge_tool fastp" + - "--shortread_clipmerge_tool fastp --shortread_clipmerge_mergepairs --shortread_clipmerge_excludeunmerged" + - "--shortread_clipmerge_tool fastp --shortread_clipmerge_mergepairs" - "--shortread_clipmerge_tool adapterremoval" + - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs --shortread_clipmerge_excludeunmerged" + - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs" + - "--shortread_complexityfilter_tool bbduk" + - "--shortread_complexityfilter_tool prinseq" steps: - name: Check out pipeline code diff --git a/conf/test.config b/conf/test.config index ff92fe7..bdad2c1 100644 --- a/conf/test.config +++ b/conf/test.config @@ -22,11 +22,12 @@ params { // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' - databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - run_kraken2 = true - run_malt = true - run_metaphlan3 = true - shortread_clipmerge = true + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' + run_kraken2 = true + run_malt = true + run_metaphlan3 = true + shortread_clipmerge = true + longread_clip = false shortread_complexityfilter = true } diff --git a/nextflow.config b/nextflow.config index cd81f08..e6d6307 100644 --- a/nextflow.config +++ b/nextflow.config @@ -51,7 +51,7 @@ params { max_cpus = 16 max_time = '240.h' - // Databaess + // Databases databases = null // FASTQ preprocessing diff --git a/nextflow_schema.json b/nextflow_schema.json index bec9ea4..6f6125e 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -328,7 +328,8 @@ }, "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", - "default": "entropy" + "default": "entropy", + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", From 59b2088aa183a58523f712ec5ee85cbdab8e1647 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Tue, 5 Apr 2022 11:16:43 +0200 Subject: [PATCH 106/306] style: apply prettier --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 51f13ea..becb501 100644 --- a/modules.json +++ b/modules.json @@ -38,4 +38,4 @@ } } } -} \ No newline at end of file +} From 39e5e802c7af4e7e36927afa471801ff3b8aab6e Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Tue, 5 Apr 2022 11:29:39 +0200 Subject: [PATCH 107/306] refactor: make code more explicit, add comment --- subworkflows/local/shortread_adapterremoval.nf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 8c02f6f..906a33f 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -39,7 +39,9 @@ workflow SHORTREAD_ADAPTERREMOVAL { ADAPTERREMOVAL_PAIRED.out.paired_truncated ) .groupTuple() - .map { [it.head(), it.tail().flatten()] } + // Paired-end reads cause a nested tuple during grouping. + // We want to present a flat list of files to `CAT_FASTQ`. + .map { meta, fastq -> [meta, fastq.flatten()] } CAT_FASTQ(ch_concat_fastq) From 245a4d1f5dae7e29ec76aa1265154edbbe3eb3db Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 5 Apr 2022 13:08:44 +0200 Subject: [PATCH 108/306] Fix MQC staging and remove debugging dump --- subworkflows/local/shortread_adapterremoval.nf | 4 ++-- workflows/taxprofiler.nf | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 9d49b10..473a05f 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -117,8 +117,8 @@ workflow SHORTREAD_ADAPTERREMOVAL { ch_versions = ch_versions.mix( ADAPTERREMOVAL_SINGLE.out.versions.first() ) ch_versions = ch_versions.mix( ADAPTERREMOVAL_PAIRED.out.versions.first() ) ch_multiqc_files = ch_multiqc_files.mix( - ADAPTERREMOVAL_PAIRED.out.log.collect{it[1]}, - ADAPTERREMOVAL_SINGLE.out.log.collect{it[1]} + ADAPTERREMOVAL_PAIRED.out.log, + ADAPTERREMOVAL_SINGLE.out.log ) emit: diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index ddd5914..eb2b461 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -171,7 +171,6 @@ workflow TAXPROFILER { } ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 - .dump(tag: "input_metaphlan3") .multiMap { it -> reads: [it[0] + it[2], it[1]] @@ -213,34 +212,34 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) if (params.shortread_clipmerge) { - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]).dump(tag: "clipmerge") ) ch_versions = ch_versions.mix( SHORTREAD_PREPROCESSING.out.versions ) } if (params.longread_clip) { - ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]).dump(tag: "clip") ) ch_versions = ch_versions.mix( LONGREAD_PREPROCESSING.out.versions ) } if (params.shortread_complexityfilter){ - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]).dump(tag: "complex") ) ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } if (params.run_kraken2) { - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]).dump(tag: "kraken") ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) } if (params.run_malt) { - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]).dump(tag: "malt") ) ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) } // TODO Versions for Karken/MALT not report? // TODO create multiQC module for metaphlan MULTIQC ( - ch_multiqc_files.collect() + ch_multiqc_files.collect().dump(tag: "input_to_mqc") ) multiqc_report = MULTIQC.out.report.toList() ch_versions = ch_versions.mix(MULTIQC.out.versions) From 5b9355725dea7af56745a28e6807ae5768798e8b Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 5 Apr 2022 13:14:06 +0200 Subject: [PATCH 109/306] Whoops --- workflows/taxprofiler.nf | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index eb2b461..8dfe996 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -17,7 +17,7 @@ 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 samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } -if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] warning: MALT does not except uncollapsed paired-reads. Pairs will be profiled as separate files." +if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] warning: MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "[nf-core/taxprofiler] error: cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" /* @@ -212,34 +212,34 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) if (params.shortread_clipmerge) { - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]).dump(tag: "clipmerge") ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( SHORTREAD_PREPROCESSING.out.versions ) } if (params.longread_clip) { - ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]).dump(tag: "clip") ) + ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( LONGREAD_PREPROCESSING.out.versions ) } if (params.shortread_complexityfilter){ - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]).dump(tag: "complex") ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } if (params.run_kraken2) { - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]).dump(tag: "kraken") ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) } if (params.run_malt) { - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]).dump(tag: "malt") ) + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) } // TODO Versions for Karken/MALT not report? // TODO create multiQC module for metaphlan MULTIQC ( - ch_multiqc_files.collect().dump(tag: "input_to_mqc") + ch_multiqc_files.collect() ) multiqc_report = MULTIQC.out.report.toList() ch_versions = ch_versions.mix(MULTIQC.out.versions) From 98f082d7b6df9e7ef8da41b98bafab63770d3582 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 5 Apr 2022 13:17:54 +0200 Subject: [PATCH 110/306] Fix mistake in previous upstream merge with AR2 output channel for settings file --- subworkflows/local/shortread_adapterremoval.nf | 5 +++-- workflows/taxprofiler.nf | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 6512596..bfed76a 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -75,9 +75,10 @@ workflow SHORTREAD_ADAPTERREMOVAL { ch_versions = ch_versions.mix( ADAPTERREMOVAL_SINGLE.out.versions.first() ) ch_versions = ch_versions.mix( ADAPTERREMOVAL_PAIRED.out.versions.first() ) + ch_multiqc_files = ch_multiqc_files.mix( - ADAPTERREMOVAL_PAIRED.out.log, - ADAPTERREMOVAL_SINGLE.out.log + ADAPTERREMOVAL_PAIRED.out.settings, + ADAPTERREMOVAL_SINGLE.out.settings ) emit: diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 8dfe996..3b08402 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -222,7 +222,7 @@ workflow TAXPROFILER { } if (params.shortread_complexityfilter){ - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } From 82aa89ad63d8769989b533da4695dc9387d81355 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 5 Apr 2022 13:55:11 +0200 Subject: [PATCH 111/306] re add missing switch meta of merged reads to true --- subworkflows/local/shortread_adapterremoval.nf | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index bfed76a..b573be9 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -38,11 +38,17 @@ workflow SHORTREAD_ADAPTERREMOVAL { ADAPTERREMOVAL_PAIRED.out.singles_truncated, ADAPTERREMOVAL_PAIRED.out.paired_truncated ) + .map { meta, reads -> + def meta_new = meta.clone() + meta_new.single_end = true + [meta_new, reads] + } .groupTuple() // Paired-end reads cause a nested tuple during grouping. // We want to present a flat list of files to `CAT_FASTQ`. .map { meta, fastq -> [meta, fastq.flatten()] } + CAT_FASTQ(ch_concat_fastq) ch_adapterremoval_reads_prepped = CAT_FASTQ.out.reads @@ -56,10 +62,13 @@ workflow SHORTREAD_ADAPTERREMOVAL { ADAPTERREMOVAL_PAIRED.out.collapsed_truncated ) .map { meta, reads -> - meta.single_end = true - [meta, reads] + def meta_new = meta.clone() + meta_new.single_end = true + [meta_new, reads] } .groupTuple() + .map { meta, fastq -> [meta, fastq.flatten()] } + CAT_FASTQ(ch_concat_fastq) From c40a9a00113c3e178bf20a4f1e0870d0994d9383 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Tue, 5 Apr 2022 12:13:45 +0200 Subject: [PATCH 112/306] refactor: avoid publishing unpacked databases --- conf/modules.config | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 2a8789f..22db8d0 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -35,11 +35,7 @@ process { } withName: UNTAR { - publishDir = [ - path: { "${params.outdir}/databases" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + publishDir = null } withName: FASTQC { From af12435fa5e3adf240d48bf9f764a063c62593b8 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Tue, 5 Apr 2022 12:16:26 +0200 Subject: [PATCH 113/306] fix: use parameter for publish mode everywhere --- conf/modules.config | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 22db8d0..a910b8e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -43,7 +43,7 @@ process { ext.prefix = { "${meta.id}_${meta.run_accession}_raw" } publishDir = [ path: { "${params.outdir}/fastqc/raw" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.html' ] } @@ -53,7 +53,7 @@ process { ext.prefix = { "${meta.id}_${meta.run_accession}_processed" } publishDir = [ path: { "${params.outdir}/fastqc/processed" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.html' ] } @@ -69,7 +69,7 @@ process { ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ path: { "${params.outdir}/fastp" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.fastq.gz' ] } @@ -88,7 +88,7 @@ process { ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ path: { "${params.outdir}/fastp" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.fastq.gz' ] } @@ -104,7 +104,7 @@ process { ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ path: { "${params.outdir}/adapterremoval" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.fastq.gz' ] } @@ -123,7 +123,7 @@ process { ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ path: { "${params.outdir}/adapterremoval" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.fastq.gz' ] } @@ -132,7 +132,7 @@ process { ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ path: { "${params.outdir}/porechop" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.fastq.gz' ] } @@ -169,7 +169,7 @@ process { ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/malt/${meta.db_name}" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.{rma6,tab,text,sam,log}' ] } @@ -179,7 +179,7 @@ process { ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, - mode: 'copy', + mode: params.publish_dir_mode, pattern: '*.{fastq.gz,txt}' ] } From 8827b143a22b07c21c7cbe5776fb112015f5ae8b Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Tue, 5 Apr 2022 12:30:06 +0200 Subject: [PATCH 114/306] fix: disable publishing correctly --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index a910b8e..ce9597b 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -35,7 +35,7 @@ process { } withName: UNTAR { - publishDir = null + publishDir = [enabled: false] } withName: FASTQC { From 018b9a8ea60afd0c5d123e4420fdccaecafbc9f7 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Wed, 6 Apr 2022 10:21:43 +0200 Subject: [PATCH 115/306] refactor: do not generally publish output --- conf/modules.config | 6 ------ 1 file changed, 6 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index ce9597b..f9cfe3b 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -12,12 +12,6 @@ process { - publishDir = [ - path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - withName: SAMPLESHEET_CHECK { publishDir = [ path: { "${params.outdir}/pipeline_info" }, From 7ab3aa546b8e1d0388981757d259284bf67f5fc8 Mon Sep 17 00:00:00 2001 From: "Moritz E. Beber" Date: Wed, 6 Apr 2022 10:27:11 +0200 Subject: [PATCH 116/306] refactor: use flags to publish reads --- conf/modules.config | 25 ++++++++++++++----------- nextflow.config | 2 ++ nextflow_schema.json | 8 ++++++++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index f9cfe3b..b59850f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -28,10 +28,6 @@ process { ] } - withName: UNTAR { - publishDir = [enabled: false] - } - withName: FASTQC { ext.args = '--quiet' ext.prefix = { "${meta.id}_${meta.run_accession}_raw" } @@ -64,7 +60,8 @@ process { publishDir = [ path: { "${params.outdir}/fastp" }, mode: params.publish_dir_mode, - pattern: '*.fastq.gz' + pattern: '*.fastq.gz', + enabled: params.save_preprocessed_reads ] } @@ -83,7 +80,8 @@ process { publishDir = [ path: { "${params.outdir}/fastp" }, mode: params.publish_dir_mode, - pattern: '*.fastq.gz' + pattern: '*.fastq.gz', + enabled: params.save_preprocessed_reads ] } @@ -99,7 +97,8 @@ process { publishDir = [ path: { "${params.outdir}/adapterremoval" }, mode: params.publish_dir_mode, - pattern: '*.fastq.gz' + pattern: '*.fastq.gz', + enabled: params.save_preprocessed_reads ] } @@ -118,7 +117,8 @@ process { publishDir = [ path: { "${params.outdir}/adapterremoval" }, mode: params.publish_dir_mode, - pattern: '*.fastq.gz' + pattern: '*.fastq.gz', + enabled: params.save_preprocessed_reads ] } @@ -127,7 +127,8 @@ process { publishDir = [ path: { "${params.outdir}/porechop" }, mode: params.publish_dir_mode, - pattern: '*.fastq.gz' + pattern: '*.fastq.gz', + enabled: params.save_preprocessed_reads ] } @@ -141,7 +142,8 @@ process { publishDir = [ path: { "${params.outdir}/bbduk/" }, mode: params.publish_dir_mode, - pattern: '*.{fastq.gz,log}' + pattern: '*.{fastq.gz,log}', + enabled: params.save_complexityfiltered_reads ] } @@ -154,7 +156,8 @@ process { publishDir = [ path: { "${params.outdir}/prinseqplusplus/" }, mode: params.publish_dir_mode, - pattern: '*{_good_out.fastq.gz,_good_out_R1.fastq.gz,_good_out_R2.fastq.gz,log}' + pattern: '*{_good_out.fastq.gz,_good_out_R1.fastq.gz,_good_out_R2.fastq.gz,log}', + enabled: params.save_complexityfiltered_reads ] } diff --git a/nextflow.config b/nextflow.config index e6d6307..19cc823 100644 --- a/nextflow.config +++ b/nextflow.config @@ -64,6 +64,7 @@ params { shortread_clipmerge_adapter2 = null shortread_clipmerge_minlength = 15 longread_clip = false + save_preprocessed_reads = false // Complexity filtering shortread_complexityfilter = false @@ -73,6 +74,7 @@ params { shortread_complexityfilter_bbduk_mask = false shortread_complexityfilter_prinseqplusplus_mode = 'entropy' shortread_complexityfilter_prinseqplusplus_dustscore = 0.5 + save_complexityfiltered_reads = false // MALT diff --git a/nextflow_schema.json b/nextflow_schema.json index 6f6125e..6858409 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -308,6 +308,10 @@ "type": "integer", "default": 15 }, + "save_preprocessed_reads": { + "type": "boolean", + "default": false + }, "shortread_complexityfilter_tool": { "type": "string", "default": "bbduk" @@ -334,6 +338,10 @@ "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", "default": 0.5 + }, + "save_complexityfiltered_reads": { + "type": "boolean", + "default": false } } } From 26779a4420f5d71d55a7bb99ff85ee1863740401 Mon Sep 17 00:00:00 2001 From: sofstam Date: Thu, 7 Apr 2022 16:27:19 +0200 Subject: [PATCH 117/306] Remove db_name from nextflow.config --- conf/test.config | 4 ++-- nextflow.config | 1 - nextflow_schema.json | 7 +++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/conf/test.config b/conf/test.config index 6e82300..d392306 100644 --- a/conf/test.config +++ b/conf/test.config @@ -25,10 +25,10 @@ params { input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' run_kraken2 = true - run_malt = true + run_malt = false run_metaphlan3 = true run_centrifuge = true shortread_clipmerge = true longread_clip = false - shortread_complexityfilter = true + shortread_complexityfilter = false } diff --git a/nextflow.config b/nextflow.config index 37f886f..b4a8d91 100644 --- a/nextflow.config +++ b/nextflow.config @@ -86,7 +86,6 @@ params { // centrifuge run_centrifuge = false - centrifuge_db_name = 'minigut_cf' centrifuge_save_unaligned = false centrifuge_save_aligned = false centrifuge_sam_format = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 2ed80ed..23eb83b 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -358,7 +358,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -369,4 +372,4 @@ "default": false } } -} +} \ No newline at end of file From 3d45ac57aead82baa98a8f0ee8aa668775bf4021 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Thu, 7 Apr 2022 16:42:22 +0200 Subject: [PATCH 118/306] Prettier --- modules.json | 2 +- nextflow_schema.json | 26 +++++--------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/modules.json b/modules.json index 5abebf6..7fbc65c 100644 --- a/modules.json +++ b/modules.json @@ -47,4 +47,4 @@ } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index 23eb83b..2b115eb 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -310,10 +300,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -358,10 +345,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -372,4 +356,4 @@ "default": false } } -} \ No newline at end of file +} From 2dfe3b3cc1d3a08aace2f521e84fa2c9820c3d98 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli <91951607+sofstam@users.noreply.github.com> Date: Fri, 8 Apr 2022 10:00:04 +0200 Subject: [PATCH 119/306] Update conf/test.config Co-authored-by: James A. Fellows Yates --- conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index d392306..270ad95 100644 --- a/conf/test.config +++ b/conf/test.config @@ -30,5 +30,5 @@ params { run_centrifuge = true shortread_clipmerge = true longread_clip = false - shortread_complexityfilter = false + shortread_complexityfilter = true } From 48d28cf8d4ac889b80736c86b3ed97eb46554af2 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli <91951607+sofstam@users.noreply.github.com> Date: Fri, 8 Apr 2022 10:00:10 +0200 Subject: [PATCH 120/306] Update conf/test.config Co-authored-by: James A. Fellows Yates --- conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index 270ad95..6e82300 100644 --- a/conf/test.config +++ b/conf/test.config @@ -25,7 +25,7 @@ params { input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' run_kraken2 = true - run_malt = false + run_malt = true run_metaphlan3 = true run_centrifuge = true shortread_clipmerge = true From fd5ebea9a697b7cc2e97cc2c056f131b64848bae Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Fri, 8 Apr 2022 10:57:58 +0200 Subject: [PATCH 121/306] Remove old centrifuge module --- modules.json | 2 +- .../nf-core/modules/centrifuge/main.nf | 63 ---------------- .../nf-core/modules/centrifuge/meta.yml | 73 ------------------- 3 files changed, 1 insertion(+), 137 deletions(-) delete mode 100644 modules/nf-core/modules/nf-core/modules/centrifuge/main.nf delete mode 100644 modules/nf-core/modules/nf-core/modules/centrifuge/meta.yml diff --git a/modules.json b/modules.json index 7fbc65c..5abebf6 100644 --- a/modules.json +++ b/modules.json @@ -47,4 +47,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/nf-core/modules/centrifuge/main.nf b/modules/nf-core/modules/nf-core/modules/centrifuge/main.nf deleted file mode 100644 index 7eb566d..0000000 --- a/modules/nf-core/modules/nf-core/modules/centrifuge/main.nf +++ /dev/null @@ -1,63 +0,0 @@ -process CENTRIFUGE { - tag "$meta.id" - label 'process_high' - - conda (params.enable_conda ? "bioconda::centrifuge=1.0.4_beta" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/centrifuge:1.0.4_beta--h9a82719_6' : - 'quay.io/biocontainers/centrifuge:1.0.4_beta--h9a82719_6' }" - - input: - tuple val(meta), path(reads) - path db - val save_unaligned - val save_aligned - val sam_format - - output: - tuple val(meta), path('*report.txt') , emit: report - tuple val(meta), path('*results.txt') , emit: results - tuple val(meta), path('*kreport.txt') , emit: kreport - tuple val(meta), path('*.sam') , optional: true, emit: sam - tuple val(meta), path('*.mapped.fastq{,.1,.2}.gz') , optional: true, emit: fastq_mapped - tuple val(meta), path('*.unmapped.fastq{,.1,.2}.gz') , optional: true, emit: fastq_unmapped - 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}" - def paired = meta.single_end ? "-U ${reads}" : "-1 ${reads[0]} -2 ${reads[1]}" - def db_name = db.toString().replace(".tar.gz","") - def unaligned = '' - def aligned = '' - if (meta.single_end) { - unaligned = save_unaligned ? "--un-gz ${prefix}.unmapped.fastq.gz" : '' - aligned = save_aligned ? "--al-gz ${prefix}.mapped.fastq.gz" : '' - } else { - unaligned = save_unaligned ? "--un-conc-gz ${prefix}.unmapped.fastq.gz" : '' - aligned = save_aligned ? "--al-conc-gz ${prefix}.mapped.fastq.gz" : '' - } - def sam_output = sam_format ? "--out-fmt 'sam'" : '' - """ - tar -xf $db - centrifuge \\ - -x $db_name \\ - -p $task.cpus \\ - $paired \\ - --report-file ${prefix}.report.txt \\ - -S ${prefix}.results.txt \\ - $unaligned \\ - $aligned \\ - $sam_output \\ - $args - centrifuge-kreport -x $db_name ${prefix}.results.txt > ${prefix}.kreport.txt - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - centrifuge: \$( centrifuge --version | sed -n 1p | sed 's/^.*centrifuge-class version //') - END_VERSIONS - """ -} diff --git a/modules/nf-core/modules/nf-core/modules/centrifuge/meta.yml b/modules/nf-core/modules/nf-core/modules/centrifuge/meta.yml deleted file mode 100644 index 3adf0e2..0000000 --- a/modules/nf-core/modules/nf-core/modules/centrifuge/meta.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: centrifuge -description: Classifies metagenomic sequence data -keywords: - - classify - - metagenomics - - fastq - - db -tools: - - centrifuge: - description: Centrifuge is a classifier for metagenomic sequences. - homepage: https://ccb.jhu.edu/software/centrifuge/ - documentation: https://ccb.jhu.edu/software/centrifuge/manual.shtml - doi: 10.1101/gr.210641.116 - licence: ["GPL v3"] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - - db: - type: directory - description: Centrifuge database in .tar.gz format - pattern: "*.tar.gz" - - save_unaligned: - type: value - description: If true unmapped fastq files are saved - - save_aligned: - type: value - description: If true mapped fastq files are saved -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - report: - type: file - description: | - File containing a classification summary - pattern: "*.{report.txt}" - - results: - type: file - description: | - File containing classification results - pattern: "*.{results.txt}" - - kreport: - type: file - description: | - File containing kraken-style report from centrifuge - out files. - pattern: "*.{kreport.txt}" - - fastq_unmapped: - type: file - description: Unmapped fastq files - pattern: "*.unmapped.fastq.gz" - - fastq_mapped: - type: file - description: Mapped fastq files - pattern: "*.mapped.fastq.gz" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@sofstam" - - "@jfy133" - - "@sateeshperi" From 63bc597daf008a4de9ed5e34c0b8d682f349a34a Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Fri, 8 Apr 2022 11:01:45 +0200 Subject: [PATCH 122/306] Prettier --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 5abebf6..7fbc65c 100644 --- a/modules.json +++ b/modules.json @@ -47,4 +47,4 @@ } } } -} \ No newline at end of file +} From 7b08c49cd6cb6471384a26c1202733cad0fe58ae Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 11:54:54 +0200 Subject: [PATCH 123/306] Re-add run merging and gonna let GHA see if it works >.> --- .github/workflows/ci.yml | 1 + nextflow.config | 2 ++ workflows/taxprofiler.nf | 25 +++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79148f0..7678645 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,7 @@ jobs: - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs" - "--shortread_complexityfilter_tool bbduk" - "--shortread_complexityfilter_tool prinseq" + - "--run_merging" steps: - name: Check out pipeline code diff --git a/nextflow.config b/nextflow.config index 19cc823..1c69d36 100644 --- a/nextflow.config +++ b/nextflow.config @@ -76,6 +76,8 @@ params { shortread_complexityfilter_prinseqplusplus_dustscore = 0.5 save_complexityfiltered_reads = false + // run merging + run_merging = false // MALT run_malt = false diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 3b08402..61eda6e 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -125,13 +125,34 @@ workflow TAXPROFILER { ch_shortreads_filtered = ch_shortreads_preprocessed } + /* + STEP: Run merging + */ + + if ( params.run_merging ) { + ch_reads_for_cat = ch_shortreads_filtered + .mix( ch_longreads_preprocessed ) + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['run_accession'].remove() + [ meta_new, reads ] + } + .groupTuple() + + ch_reads_runmerged = CAT_FASTQ ( ch_reads_for_cat ) + + } else { + ch_reads_runmerged = ch_shortreads_filtered + .mix( ch_longreads_preprocessed ) + } + /* COMBINE READS WITH POSSIBLE DATABASES */ // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] - ch_input_for_profiling = ch_shortreads_filtered - .mix( ch_longreads_preprocessed ) + ch_input_for_profiling = ch_reads_runmerged .combine(DB_CHECK.out.dbs) .branch { malt: it[2]['tool'] == 'malt' From 74c496f6af04e15e1625d7311791004a645b3a21 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 11:58:16 +0200 Subject: [PATCH 124/306] Fix CAT_FASTQ output --- workflows/taxprofiler.nf | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 61eda6e..2d0c17d 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -140,8 +140,7 @@ workflow TAXPROFILER { } .groupTuple() - ch_reads_runmerged = CAT_FASTQ ( ch_reads_for_cat ) - + ch_reads_runmerged = CAT_FASTQ ( ch_reads_for_cat ).reads } else { ch_reads_runmerged = ch_shortreads_filtered .mix( ch_longreads_preprocessed ) @@ -247,6 +246,10 @@ workflow TAXPROFILER { ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } + if (params.run_merging){ + ch_versions = ch_versions.mix(CAT_FASTQ.out.versions) + } + if (params.run_kraken2) { ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) From 4d726a87e98f56bf1a4e0d52259d9e83f89539ce Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 12:01:07 +0200 Subject: [PATCH 125/306] Fix metadata removal --- workflows/taxprofiler.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 2d0c17d..33d9725 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -135,7 +135,7 @@ workflow TAXPROFILER { .map { meta, reads -> def meta_new = meta.clone() - meta_new['run_accession'].remove() + meta_new.remove('run_accession') [ meta_new, reads ] } .groupTuple() From d130a72d74899c3fd85db3c1c751b7b6848fd031 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 13:09:23 +0200 Subject: [PATCH 126/306] Get this working --- .github/workflows/ci.yml | 1 + conf/modules.config | 6 +++--- subworkflows/local/shortread_fastp.nf | 4 ++-- workflows/taxprofiler.nf | 18 ++++++++++++++++-- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7678645..53423cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,7 @@ jobs: - "--shortread_complexityfilter_tool bbduk" - "--shortread_complexityfilter_tool prinseq" - "--run_merging" + - "--run_merging --shortread_clipmerge_mergepairs" steps: - name: Check out pipeline code diff --git a/conf/modules.config b/conf/modules.config index b59850f..7602e3f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -163,7 +163,7 @@ process { withName: MALT_RUN { ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = if params.run_merging : { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/malt/${meta.db_name}" }, mode: params.publish_dir_mode, @@ -173,7 +173,7 @@ process { withName: KRAKEN2_KRAKEN2 { ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = if params.run_merging : { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: params.publish_dir_mode, @@ -183,7 +183,7 @@ process { withName: METAPHLAN3 { publishDir = [ - path: { "${params.outdir}/metaphlan3/${meta.db_name}" }, + ext.prefix = if params.run_merging : { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } mode: params.publish_dir_mode, pattern: '*.{biom,txt}' ] diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index 18baf17..04057b1 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -28,8 +28,8 @@ workflow SHORTREAD_FASTP { .map { meta, reads -> def meta_new = meta.clone() - meta_new['single_end'] = 1 - [ meta_new, reads ] + meta_new['single_end'] = true + [ meta_new, reads.flatten() ] } ch_fastp_reads_prepped = ch_fastp_reads_prepped_pe.mix( FASTP_SINGLE.out.reads ) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 33d9725..e04d4d6 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -130,7 +130,8 @@ workflow TAXPROFILER { */ if ( params.run_merging ) { - ch_reads_for_cat = ch_shortreads_filtered + + ch_reads_for_cat_branch = ch_shortreads_filtered .mix( ch_longreads_preprocessed ) .map { meta, reads -> @@ -139,8 +140,21 @@ workflow TAXPROFILER { [ meta_new, reads ] } .groupTuple() + .map { + meta, reads -> + [ meta, reads.flatten() ] + } + .branch { + // we can't concate files if there is not a second run, we branch + // here to separate them out, and mix after + cat: ( it[0]['single_end'] && it[1].size() > 1 ) || ( !it[0]['single_end'] && it[1].size() > 2 ) + skip: true + } + + ch_reads_for_cat_branch.cat.dump(tag: "for_catting") + + ch_reads_runmerged = CAT_FASTQ ( ch_reads_for_cat_branch.cat ).reads.mix( ch_reads_for_cat_branch.skip ) - ch_reads_runmerged = CAT_FASTQ ( ch_reads_for_cat ).reads } else { ch_reads_runmerged = ch_shortreads_filtered .mix( ch_longreads_preprocessed ) From ca011ccc5b363ea9d6c1eaf44713a9c09e471f39 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 13:28:37 +0200 Subject: [PATCH 127/306] Fix cat_fastq naming logic? --- conf/modules.config | 19 +++++++++++++++---- nextflow.config | 1 + nextflow_schema.json | 38 +++++++++++++++++++++++++++++--------- workflows/taxprofiler.nf | 4 ---- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 7602e3f..97e9510 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -161,9 +161,19 @@ process { ] } + withName: CAT_FASTQ { + ext.prefix = { "${meta.id}-${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/run_merging/" }, + mode: params.publish_dir_mode, + pattern: '*.fastq.gz', + enabled: params.save_runmerged_reads + ] + } + withName: MALT_RUN { ext.args = { "${meta.db_params}" } - ext.prefix = if params.run_merging : { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.run_merging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/malt/${meta.db_name}" }, mode: params.publish_dir_mode, @@ -173,7 +183,7 @@ process { withName: KRAKEN2_KRAKEN2 { ext.args = { "${meta.db_params}" } - ext.prefix = if params.run_merging : { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.run_merging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: params.publish_dir_mode, @@ -182,12 +192,13 @@ process { } withName: METAPHLAN3 { + ext.args = { "${meta.db_params}" } + ext.prefix = params.run_merging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ - ext.prefix = if params.run_merging : { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + path: { "${params.outdir}/metaphlan3/${meta.db_name}" }, mode: params.publish_dir_mode, pattern: '*.{biom,txt}' ] - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: CUSTOM_DUMPSOFTWAREVERSIONS { diff --git a/nextflow.config b/nextflow.config index 1c69d36..d969ed9 100644 --- a/nextflow.config +++ b/nextflow.config @@ -78,6 +78,7 @@ params { // run merging run_merging = false + save_runmerged_reads = false // MALT run_malt = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 6858409..0b4b4fb 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -288,7 +298,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -309,8 +322,7 @@ "default": 15 }, "save_preprocessed_reads": { - "type": "boolean", - "default": false + "type": "boolean" }, "shortread_complexityfilter_tool": { "type": "string", @@ -333,15 +345,23 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", "default": 0.5 }, "save_complexityfiltered_reads": { - "type": "boolean", - "default": false + "type": "boolean" + }, + "run_merging": { + "type": "boolean" + }, + "save_runmerged_reads": { + "type": "boolean" } } -} +} \ No newline at end of file diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index e04d4d6..bdb93ab 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -140,10 +140,6 @@ workflow TAXPROFILER { [ meta_new, reads ] } .groupTuple() - .map { - meta, reads -> - [ meta, reads.flatten() ] - } .branch { // we can't concate files if there is not a second run, we branch // here to separate them out, and mix after From a634814d848a68252d81b231e34942f5fa616c83 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 13:34:00 +0200 Subject: [PATCH 128/306] Formatting and fix fastp output --- nextflow_schema.json | 26 ++++--------------- .../local/shortread_adapterremoval.nf | 2 +- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 0b4b4fb..64836df 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -298,10 +288,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -345,10 +332,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -364,4 +348,4 @@ "type": "boolean" } } -} \ No newline at end of file +} diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index b573be9..a7948e7 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -67,7 +67,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { [meta_new, reads] } .groupTuple() - .map { meta, fastq -> [meta, fastq.flatten()] } + .map { meta, fastq -> [meta, [ fastq ].flatten()] } CAT_FASTQ(ch_concat_fastq) From 6c14f2b230a6c6df99e89c44ed8904cb0a5a7b59 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 13:44:52 +0200 Subject: [PATCH 129/306] Remove the flattening? --- subworkflows/local/shortread_adapterremoval.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index a7948e7..735d3b8 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -67,7 +67,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { [meta_new, reads] } .groupTuple() - .map { meta, fastq -> [meta, [ fastq ].flatten()] } + .map { meta, fastq -> [meta, fastq] } CAT_FASTQ(ch_concat_fastq) From 35cb6e042acf3fbe34c9664bebb9c27ef0d96179 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 13:46:12 +0200 Subject: [PATCH 130/306] Flatten the right thing --- subworkflows/local/shortread_adapterremoval.nf | 2 +- subworkflows/local/shortread_fastp.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index 735d3b8..b573be9 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -67,7 +67,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { [meta_new, reads] } .groupTuple() - .map { meta, fastq -> [meta, fastq] } + .map { meta, fastq -> [meta, fastq.flatten()] } CAT_FASTQ(ch_concat_fastq) diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index 04057b1..4626691 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -29,7 +29,7 @@ workflow SHORTREAD_FASTP { meta, reads -> def meta_new = meta.clone() meta_new['single_end'] = true - [ meta_new, reads.flatten() ] + [ meta_new, [ reads ].flatten() ] } ch_fastp_reads_prepped = ch_fastp_reads_prepped_pe.mix( FASTP_SINGLE.out.reads ) From 8839fe22b8856fb1d69e11e2e1e0a4367cb97dc3 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 20:02:22 +0200 Subject: [PATCH 131/306] Fix output tuple for reads --- workflows/taxprofiler.nf | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index bdb93ab..7d5f60f 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -149,7 +149,15 @@ workflow TAXPROFILER { ch_reads_for_cat_branch.cat.dump(tag: "for_catting") - ch_reads_runmerged = CAT_FASTQ ( ch_reads_for_cat_branch.cat ).reads.mix( ch_reads_for_cat_branch.skip ) + ch_reads_runmerged = CAT_FASTQ ( ch_reads_for_cat_branch.cat ).reads + .mix( ch_reads_for_cat_branch.skip ) + .map { + meta, reads -> + + [ meta, [ reads ].flatten() ] + } + + ch_reads_runmerged.dump(tag: "ch_reads_runmerged" ) } else { ch_reads_runmerged = ch_shortreads_filtered From afb66e445fcb2f9c60f4152e1184f5f8600a843c Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 8 Apr 2022 21:36:59 +0200 Subject: [PATCH 132/306] Append pairment to ID at profiling to prevent multiqc-level filename crash --- workflows/taxprofiler.nf | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 7d5f60f..1da812d 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -147,8 +147,6 @@ workflow TAXPROFILER { skip: true } - ch_reads_for_cat_branch.cat.dump(tag: "for_catting") - ch_reads_runmerged = CAT_FASTQ ( ch_reads_for_cat_branch.cat ).reads .mix( ch_reads_for_cat_branch.skip ) .map { @@ -157,8 +155,6 @@ workflow TAXPROFILER { [ meta, [ reads ].flatten() ] } - ch_reads_runmerged.dump(tag: "ch_reads_runmerged" ) - } else { ch_reads_runmerged = ch_shortreads_filtered .mix( ch_longreads_preprocessed ) @@ -168,8 +164,15 @@ workflow TAXPROFILER { COMBINE READS WITH POSSIBLE DATABASES */ - // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] + // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], [ /2612.merged.fastq.gz ], ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] ch_input_for_profiling = ch_reads_runmerged + .map { + meta, reads -> + def meta_new = meta.clone() + pairtype = meta_new['single_end'] ? '_se' : '_pe' + meta_new['id'] = meta_new['id'] + pairtype + [meta_new, reads] + } .combine(DB_CHECK.out.dbs) .branch { malt: it[2]['tool'] == 'malt' From ecf0eea4f99f0601124661557b41c5092d507ca6 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 10 Apr 2022 06:43:30 +0200 Subject: [PATCH 133/306] Move profiling to subworkflow and standardise outputs --- conf/modules.config | 24 +++---- subworkflows/local/profiling.nf | 120 ++++++++++++++++++++++++++++++++ workflows/taxprofiler.nf | 97 ++------------------------ 3 files changed, 136 insertions(+), 105 deletions(-) create mode 100644 subworkflows/local/profiling.nf diff --git a/conf/modules.config b/conf/modules.config index de41e69..531bd5a 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -167,7 +167,7 @@ process { publishDir = [ path: { "${params.outdir}/malt/${meta.db_name}" }, mode: params.publish_dir_mode, - pattern: '*.{rma6,tab,text,sam,log}' + pattern: '*.{log}' ] } @@ -177,7 +177,7 @@ process { publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: params.publish_dir_mode, - pattern: '*.{fastq.gz,txt}' + pattern: '*.{txt}' ] } @@ -190,6 +190,16 @@ process { ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } + withName: CENTRIFUGE_CENTRIFUGE { + publishDir = [ + path: { "${params.outdir}/centrifuge/${meta.db_name}" }, + mode: params.publish_dir_mode, + pattern: '*.txt' + ] + ext.args = { "${meta.db_params}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + } + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ path: { "${params.outdir}/pipeline_info" }, @@ -198,14 +208,4 @@ process { ] } - withName: CENTRIFUGE_CENTRIFUGE { - publishDir = [ - path: { "${params.outdir}/centrifuge/${meta.db_name}" }, - mode: params.publish_dir_mode, - pattern: '*.{fastq.gz,txt}' - ] - ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } - } - } diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf new file mode 100644 index 0000000..ac48d77 --- /dev/null +++ b/subworkflows/local/profiling.nf @@ -0,0 +1,120 @@ +// +// Run profiling +// + +include { MALT_RUN } from '../../modules/nf-core/modules/malt/run/main' +include { KRAKEN2_KRAKEN2 } from '../../modules/nf-core/modules/kraken2/kraken2/main' +include { CENTRIFUGE_CENTRIFUGE } from '../../modules/nf-core/modules/centrifuge/centrifuge/main' +include { METAPHLAN3 } from '../../modules/nf-core/modules/metaphlan3/main' + +workflow PROFILING { + take: + shortreads // [ [ meta ], [ reads ] ] + longreads // [ [ meta ], [ reads ] ] + databases // [ [ meta ], path ] + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + +/* + COMBINE READS WITH POSSIBLE DATABASES + */ + + // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] + ch_input_for_profiling = shortreads + .mix( longreads ) + .combine(databases) + .branch { + malt: it[2]['tool'] == 'malt' + kraken2: it[2]['tool'] == 'kraken2' + metaphlan3: it[2]['tool'] == 'metaphlan3' + centrifuge: it[2]['tool'] == 'centrifuge' + unknown: true + } + + /* + PREPARE PROFILER INPUT CHANNELS + */ + + // Each tool as a slightly different input structure and generally separate + // input channels for reads vs databases. We restructure the channel tuple + // for each tool and make liberal use of multiMap to keep reads/databases + // channel element order in sync with each other + + // MALT: We groupTuple to have all samples in one channel for MALT as database + // loading takes a long time, so we only want to run it once per database + // TODO document somewhere we only accept illumina short reads for MALT? + ch_input_for_malt = ch_input_for_profiling.malt + .filter { it[0]['instrument_platform'] == 'ILLUMINA' } + .map { + it -> + def temp_meta = [ id: it[2]['db_name']] + it[2] + def db = it[3] + [ temp_meta, it[1], db ] + } + .groupTuple(by: [0,2]) + .multiMap { + it -> + reads: [ it[0], it[1].flatten() ] + db: it[2] + } + + // All subsequent tools can easily run on a per-sample basis + + ch_input_for_kraken2 = ch_input_for_profiling.kraken2 + .multiMap { + it -> + reads: [ it[0] + it[2], it[1] ] + db: it[3] + } + + ch_input_for_centrifuge = ch_input_for_profiling.centrifuge + .dump(tag: "input for centrifuge") + .multiMap { + it -> + reads: [ it[0] + it[2], it[1] ] + db: it[3] + } + + ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 + .multiMap { + it -> + reads: [it[0] + it[2], it[1]] + db: it[3] + } + + /* + RUN PROFILING + */ + + if ( params.run_malt ) { + MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) + ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) + } + + if ( params.run_kraken2 ) { + KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) + ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) + } + + if ( params.run_centrifuge ) { + CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_unaligned, params.centrifuge_save_aligned, params.centrifuge_sam_format ) + ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) + } + + if ( params.run_metaphlan3 ) { + METAPHLAN3 ( ch_input_for_metaphlan3.reads, ch_input_for_metaphlan3.db ) + ch_versions = ch_versions.mix( METAPHLAN3.out.versions.first() ) + } + + + emit: + // TODO work out if there is enough standardisation of output to export as one? + //output = ch_filtered_reads // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} + diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 6b89c66..38afdda 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -44,6 +44,7 @@ include { DB_CHECK } from '../subworkflows/local/db_check' include { SHORTREAD_PREPROCESSING } from '../subworkflows/local/shortread_preprocessing' include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' include { SHORTREAD_COMPLEXITYFILTERING } from '../subworkflows/local/shortread_complexityfiltering' +include { PROFILING } from '../subworkflows/local/profiling' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -59,10 +60,6 @@ include { MULTIQC } from '../modules/nf-core/modules/multiqc include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fastq/main' -include { MALT_RUN } from '../modules/nf-core/modules/malt/run/main' -include { KRAKEN2_KRAKEN2 } from '../modules/nf-core/modules/kraken2/kraken2/main' -include { CENTRIFUGE_CENTRIFUGE } from '../modules/nf-core/modules/centrifuge/centrifuge/main' -include { METAPHLAN3 } from '../modules/nf-core/modules/metaphlan3/main' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -127,88 +124,10 @@ workflow TAXPROFILER { } /* - COMBINE READS WITH POSSIBLE DATABASES + SUBWORKFLOW: PROFILING */ - // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] - ch_input_for_profiling = ch_shortreads_filtered - .mix( ch_longreads_preprocessed ) - .combine(DB_CHECK.out.dbs) - .branch { - malt: it[2]['tool'] == 'malt' - kraken2: it[2]['tool'] == 'kraken2' - metaphlan3: it[2]['tool'] == 'metaphlan3' - centrifuge: it[2]['tool'] == 'centrifuge' - unknown: true - } - - /* - PREPARE PROFILER INPUT CHANNELS - */ - - // We groupTuple to have all samples in one channel for MALT as database - // loading takes a long time, so we only want to run it once per database - // TODO document somewhere we only accept illumina short reads for MALT? - ch_input_for_malt = ch_input_for_profiling.malt - .filter { it[0]['instrument_platform'] == 'ILLUMINA' } - .map { - it -> - def temp_meta = [ id: it[2]['db_name']] + it[2] - def db = it[3] - [ temp_meta, it[1], db ] - } - .groupTuple(by: [0,2]) - .multiMap { - it -> - reads: [ it[0], it[1].flatten() ] - db: it[2] - } - - // We can run Kraken2 one-by-one sample-wise - ch_input_for_kraken2 = ch_input_for_profiling.kraken2 - .multiMap { - it -> - reads: [ it[0] + it[2], it[1] ] - db: it[3] - } - - // We can run centrifuge one-by-one sample-wise - ch_input_for_centrifuge = ch_input_for_profiling.centrifuge - .dump(tag: "input for centrifuge") - .multiMap { - it -> - reads: [ it[0] + it[2], it[1] ] - db: it[3] - } - - // - // RUN PROFILING - // - ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 - .multiMap { - it -> - reads: [it[0] + it[2], it[1]] - db: it[3] - } - - /* - MODULE: RUN PROFILING - */ - if ( params.run_malt ) { - MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) - } - - if ( params.run_kraken2 ) { - KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) - } - - if ( params.run_centrifuge ) { - CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_unaligned, params.centrifuge_save_aligned, params.centrifuge_sam_format ) - } - - if ( params.run_metaphlan3 ) { - METAPHLAN3 ( ch_input_for_metaphlan3.reads, ch_input_for_metaphlan3.db ) - } + PROFILING ( ch_shortreads_filtered, ch_longreads_preprocessed, DB_CHECK.out.dbs ) /* MODULE: MultiQC @@ -244,17 +163,9 @@ workflow TAXPROFILER { ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } - if (params.run_kraken2) { - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) - } + ch_multiqc_files = ch_multiqc_files.mix( PROFILING.out.mqc ) - if (params.run_malt) { - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) - } - // TODO Versions for Karken/MALT not report? // TODO create multiQC module for metaphlan MULTIQC ( ch_multiqc_files.collect() From 082093f3dee43655b4911e5d81ba0eb0c693454c Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 10 Apr 2022 06:44:10 +0200 Subject: [PATCH 134/306] Prettier --- conf/modules.config | 8 ++++++++ workflows/taxprofiler.nf | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index 531bd5a..2f5710e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -208,4 +208,12 @@ process { ] } + withName: MULTIQC { + publishDir = [ + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 38afdda..4afdd6c 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -165,7 +165,6 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix( PROFILING.out.mqc ) - // TODO create multiQC module for metaphlan MULTIQC ( ch_multiqc_files.collect() From 80c08af11b28f89289bc0739fbd13da661b45644 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 10 Apr 2022 06:48:25 +0200 Subject: [PATCH 135/306] Mix in profiling versions into mainline versons channel --- workflows/taxprofiler.nf | 1 + 1 file changed, 1 insertion(+) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 4afdd6c..237f1ea 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -128,6 +128,7 @@ workflow TAXPROFILER { */ PROFILING ( ch_shortreads_filtered, ch_longreads_preprocessed, DB_CHECK.out.dbs ) + ch_versions = ch_versions.mix( PROFILING.out.versions ) /* MODULE: MultiQC From 030099c559a5eeeee2a65c523a9e946748327b45 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 10 Apr 2022 07:26:20 +0200 Subject: [PATCH 136/306] A bit of clean up --- subworkflows/local/profiling.nf | 1 - subworkflows/local/shortread_fastp.nf | 2 +- workflows/taxprofiler.nf | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index ac48d77..c74c583 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -70,7 +70,6 @@ workflow PROFILING { } ch_input_for_centrifuge = ch_input_for_profiling.centrifuge - .dump(tag: "input for centrifuge") .multiMap { it -> reads: [ it[0] + it[2], it[1] ] diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index 18baf17..9fb9425 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -10,7 +10,7 @@ workflow SHORTREAD_FASTP { reads // [[meta], [reads]] main: - ch_versions = Channel.empty() + ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() ch_input_for_fastp = reads diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 237f1ea..4b9f927 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -85,6 +85,7 @@ workflow TAXPROFILER { DB_CHECK ( ch_databases ) + ch_versions = ch_versions.mix(DB_CHECK.out.versions) /* MODULE: Run FastQC @@ -101,6 +102,7 @@ workflow TAXPROFILER { SUBWORKFLOW: PERFORM PREPROCESSING */ if ( params.shortread_clipmerge ) { + ch_shortreads_preprocessed = SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ).reads } else { ch_shortreads_preprocessed = INPUT_CHECK.out.fastq From b892fd0be5b754cc361ab6d9c71f15b732353057 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 10 Apr 2022 22:17:44 +0200 Subject: [PATCH 137/306] Initial attempt at docs --- README.md | 32 ++++++++-------- assets/samplesheet.csv | 9 +++-- docs/usage.md | 84 +++++++++++++++++++++++++++++------------- 3 files changed, 80 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index e976f73..88f643b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ -**nf-core/taxprofiler** is a bioinformatics best-practice analysis pipeline for taxonomic profiling of shotgun metagenomic data. It allows for in-parallel profiling against multiple profiling tools and databases and produces standardised output tables. +**nf-core/taxprofiler** is a bioinformatics best-practice analysis pipeline for taxonomic profiling of shotgun metagenomic data. It allows for in-parallel profiling with multiple profiling tools against multiple databases, produces standardised output tables. 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! @@ -32,20 +32,20 @@ On release, automated continuous integration tests run the pipeline on a full-si 1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) 2. Performs optional read pre-processing - - Adapter clipping and merging (short, and nanopore reads) - - Low complexity filtering - - Host read removal + - Adapter clipping and merging (short read: [fastp](https://github.com/OpenGene/fastp), [AdapterRemoval2](https://github.com/MikkelSchubert/adapterremoval); long read: [porechop](https://github.com/rrwick/Porechop)) + - Low complexity filtering ([bbduk](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/), [PRINSEQ++](https://github.com/Adrian-Cantu/PRINSEQ-plus-plus)) + - Host read removal ([BowTie2](http://bowtie-bio.sourceforge.net/bowtie2/)) - Run merging -3. Performs taxonomic profiling a choice of: - - Kraken2 - - MetaPhlAn3 - - MALT - - DIAMOND - - Centrifuge - - Kaiju - - mOTUs +3. Performs taxonomic profiling via a choice of any or all of: + - [Kraken2](https://ccb.jhu.edu/software/kraken2/) + - [MetaPhlAn3](https://huttenhower.sph.harvard.edu/metaphlan/) + - [MALT](https://uni-tuebingen.de/fakultaeten/mathematisch-naturwissenschaftliche-fakultaet/fachbereiche/informatik/lehrstuehle/algorithms-in-bioinformatics/software/malt/) + - [DIAMOND](https://github.com/bbuchfink/diamond) + - [Centrifuge](https://ccb.jhu.edu/software/centrifuge/) + - [Kaiju](https://kaiju.binf.ku.dk/) + - [mOTUs](https://motu-tool.org/) 4. Perform optional post-processing with: - - bracken + - [bracken](https://ccb.jhu.edu/software/bracken/) 5. Standardises output tables 6. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) @@ -70,10 +70,8 @@ On release, automated continuous integration tests run the pipeline on a full-si 4. Start running your own analysis! - - ```console - nextflow run nf-core/taxprofiler --input samplesheet.csv --outdir --genome GRCh37 -profile + nextflow run nf-core/taxprofiler --input samplesheet.csv --databases database.csv --outdir --run_ --run_ -profile ``` ## Documentation @@ -86,7 +84,7 @@ nf-core/taxprofiler was originally written by nf-core community. We thank the following people for their extensive assistance in the development of this pipeline: - +[James A. Fellows Yates](https://github.com/jfy133), [Moritz Beber](https://github.com/Midnighter), [Lauri Mesilaakso](https://github.com/ljmesi), [Sofia Stamouli](https://github.com/sofsam), [Maxime Borry](https://github.com/maxibor). ## Contributions and Support diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index 5f653ab..82565b1 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,3 +1,6 @@ -sample,fastq_1,fastq_2 -SAMPLE_PAIRED_END,/path/to/fastq/files/AEG588A1_S1_L002_R1_001.fastq.gz,/path/to/fastq/files/AEG588A1_S1_L002_R2_001.fastq.gz -SAMPLE_SINGLE_END,/path/to/fastq/files/AEG588A4_S4_L003_R1_001.fastq.gz, +sample,run_accession,instrument_platform,fastq_1,fastq_2,fasta +2611,ERR5766174,ILLUMINA,,,///fasta/ERX5474930_ERR5766174_1.fa.gz +2612,ERR5766176,ILLUMINA,///fastq/ERX5474932_ERR5766176_1.fastq.gz,///fastq/ERX5474932_ERR5766176_2.fastq.gz, +2612,ERR5766180,ILLUMINA,///fastq/ERX5474936_ERR5766180_1.fastq.gz,, +2613,ERR5766181,ILLUMINA,///fastq/ERX5474937_ERR5766181_1.fastq.gz,///fastq/ERX5474937_ERR5766181_2.fastq.gz, +ERR3201952,ERR3201952,OXFORD_NANOPORE,///fastq/ERR3201952.fastq.gz,, diff --git a/docs/usage.md b/docs/usage.md index bd840a4..0091d57 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -8,56 +8,90 @@ -## Samplesheet input +## Samplesheet inputs -You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row as shown in the examples below. +You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 6 columns, and a header row as shown in the examples below. Furthermother, nf-core/taxprofiler also requires a second comma-separated file of 3 columns with a header row as in the examples below. + +This samplesheet is then specified on the command line as follows: ```console ---input '[path to samplesheet file]' +--input '[path to samplesheet file]' --databases '[path to database sheet file]' ``` ### Multiple runs of the same sample -The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will concatenate the raw reads before performing any downstream analysis. Below is an example for the same sample sequenced across 3 lanes: +The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will processed reads before performing profiling. Below is an example for the same sample sequenced across 3 lanes: ```console -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -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 +sample,run_accession,instrument_platform,fastq_1,fastq_2,fasta +2612,run1,ILLUMINA,2612_run1_R1.fq.gz,, +2612,run2,ILLUMINA,2612_run2_R1.fq.gz,, +2612,run3,ILLUMINA,2612_run3_R1.fq.gz,2612_run3_R2.fq.gz, + ``` +> ⚠️ Runs of the sample sample sequenced on Illumina platforms with a combination of single and paired-end data will **not** be run-wise concatenated, unless pair-merging is specified. In the example above, `run3` will be profiled independently of `run1` and `run2` if pairs not merged. + ### Full samplesheet -The pipeline will auto-detect whether a sample is single- or paired-end using the information provided in the samplesheet. The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 3 columns to match those defined in the table below. +The pipeline will auto-detect whether a sample is single- or paired-end using the information provided in the samplesheet. The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 6 columns to match those defined in the table below. -A final samplesheet file consisting of both single- and paired-end data may look something like the one below. This is for 6 samples, where `TREATMENT_REP3` has been sequenced twice. +A final samplesheet file consisting of both single- and paired-end data, as well as long-read FASTA fies may look something like the one below. This is for 6 samples, where `2612` has been sequenced twice. ```console -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -CONTROL_REP2,AEG588A2_S2_L002_R1_001.fastq.gz,AEG588A2_S2_L002_R2_001.fastq.gz -CONTROL_REP3,AEG588A3_S3_L002_R1_001.fastq.gz,AEG588A3_S3_L002_R2_001.fastq.gz -TREATMENT_REP1,AEG588A4_S4_L003_R1_001.fastq.gz, -TREATMENT_REP2,AEG588A5_S5_L003_R1_001.fastq.gz, -TREATMENT_REP3,AEG588A6_S6_L003_R1_001.fastq.gz, -TREATMENT_REP3,AEG588A6_S6_L004_R1_001.fastq.gz, +2611,ERR5766174,ILLUMINA,,,///fasta/ERX5474930_ERR5766174_1.fa.gz +2612,ERR5766176,ILLUMINA,///fastq/ERX5474932_ERR5766176_1.fastq.gz,///fastq/ERX5474932_ERR5766176_2.fastq.gz, +2612,ERR5766180,ILLUMINA,///fastq/ERX5474936_ERR5766180_1.fastq.gz,, +2613,ERR5766181,ILLUMINA,///fastq/ERX5474937_ERR5766181_1.fastq.gz,///fastq/ERX5474937_ERR5766181_2.fastq.gz, +ERR3201952,ERR3201952,OXFORD_NANOPORE,///fastq/ERR3201952.fastq.gz,, ``` -| Column | Description | -| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | -| `fastq_1` | Full path to FastQ file for Illumina short reads 1 or Nanopore reads. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | -| `fastq_2` | Full path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| Column | Description | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sample` | Unique sample name [required]. | +| `run_accession` | Run ID or name unique for each (pairs of) file(s) .Can also supply sample name again here, if only a single run was generated [required]. | +| `instrument_platform` | Sequencing platform reads generated on, selected from the EBI ENA [controlled vocabulary](https://www.ebi.ac.uk/ena/portal/api/controlledVocab?field=instrument_platform) [required]. | +| `fastq_1` | Path or URL to sequencing reads or for Illumina R1 sequencing reads in FASTQ format. GZipped compressed files accepted. Can be left empty if data in FASTA is specifed. Cannot be combined with `fasta`. | +| `fastq_2` | Path or URL to Illumina R2 sequencing reads in FASTQ format. GZipped compressed files accepted. Can be left empty if single end data. Cannot be combined with `fasta`. | +| `fasta` | Path or URL to long-reads or contigs in FASTA format. GZipped compressed files accepted. Can be left empty if data in FASTA is specifed. Cannot be combined with `fastq_1` or `fastq_2`. | An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. +### Full database sheet + +nf-core/taxprofiler supports multiple databases being profiled in parallel for each tool. These databases, and specific parameters for each, can be specified in a 4 column comma-separated sheet. + +> ⚠️ nf-core/taxprofiler does not provide any databases by default, nor currently generates them for you. This must be performed manually by the user. + +An example database sheet can look as follows, where 4 tools are being used, and `malt` and `kraken2` will be used against two databases each. + +```console +tool,db_name,db_params,db_path +malt,malt85,-id 85,///malt/testdb-malt/ +malt,malt95,-id 90,///malt/testdb-malt.tar.gz +kraken2,db1,,///kraken2/testdb-kraken2.tar.gz +kraken2,db2,--quick,///kraken2/testdb-kraken2.tar.gz +centrifuge,db1,,///centrifuge/minigut_cf.tar.gz +metaphlan3,db1,,///metaphlan3/metaphlan_database/ +``` + +Column specifications are as follows: + +| Column | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `tool` | Taxonomic profiling tool (supported by nf-core/taxprofiler) that the database has been indexed for [required]. | +| `db_name` | A unique name of the particular database [required]. | +| `db_params` | Any parameters of the given taxonomic profiler that you wish to specify that the taxonomic profiling tool should use when profiling against this specific. Can be empty to use taxonomic profiler defaults Must not be surrounded by quotes [required]. | +| `db_path` | Path to the database. Can either be a path to a directory containing the database index files or a `.tar.gz` file which contains the compressed database directory with the same name as the tar archive, minus `.tar.gz` [required]. | + +> 💡 You can also specify the same database directory/file twice (ensuring unique `db_name`s) and specify different parameters for each database to compare the effect of different parameters during profiling. + ## Running the pipeline The typical command for running the pipeline is as follows: ```console -nextflow run nf-core/taxprofiler --input samplesheet.csv --outdir --genome GRCh37 -profile docker +nextflow run nf-core/taxprofiler --input samplesheet.csv --databases databases.csv --outdir -profile docker --run_ --run_ ``` This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. @@ -66,7 +100,7 @@ Note that the pipeline will create the following files in your working directory ```console work # Directory containing the nextflow working files - # Finished results in specified location (defined with --outdir) + # Finished results in specified location (defined with --outdir) .nextflow_log # Log file from Nextflow # Other nextflow hidden files, eg. history of pipeline runs and old logs. ``` From b8b11fd065ee04ced28f586c505c2e180109b30e Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 11 Apr 2022 10:30:29 +0200 Subject: [PATCH 138/306] Apply suggestions from code review Co-authored-by: Moritz E. Beber --- README.md | 2 +- docs/usage.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 88f643b..f1d59d5 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ On release, automated continuous integration tests run the pipeline on a full-si - Low complexity filtering ([bbduk](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/), [PRINSEQ++](https://github.com/Adrian-Cantu/PRINSEQ-plus-plus)) - Host read removal ([BowTie2](http://bowtie-bio.sourceforge.net/bowtie2/)) - Run merging -3. Performs taxonomic profiling via a choice of any or all of: +3. Performs taxonomic profiling using one or more of: - [Kraken2](https://ccb.jhu.edu/software/kraken2/) - [MetaPhlAn3](https://huttenhower.sph.harvard.edu/metaphlan/) - [MALT](https://uni-tuebingen.de/fakultaeten/mathematisch-naturwissenschaftliche-fakultaet/fachbereiche/informatik/lehrstuehle/algorithms-in-bioinformatics/software/malt/) diff --git a/docs/usage.md b/docs/usage.md index 0091d57..239a55d 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -20,7 +20,7 @@ This samplesheet is then specified on the command line as follows: ### Multiple runs of the same sample -The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will processed reads before performing profiling. Below is an example for the same sample sequenced across 3 lanes: +The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will process reads before performing profiling. Below is an example for the same sample sequenced across 3 lanes: ```console sample,run_accession,instrument_platform,fastq_1,fastq_2,fasta @@ -30,7 +30,7 @@ sample,run_accession,instrument_platform,fastq_1,fastq_2,fasta ``` -> ⚠️ Runs of the sample sample sequenced on Illumina platforms with a combination of single and paired-end data will **not** be run-wise concatenated, unless pair-merging is specified. In the example above, `run3` will be profiled independently of `run1` and `run2` if pairs not merged. +> ⚠️ Runs of the same sample sequenced on Illumina platforms with a combination of single and paired-end data will **not** be run-wise concatenated, unless pair-merging is specified. In the example above, `run3` will be profiled independently of `run1` and `run2` if pairs are not merged. ### Full samplesheet @@ -61,7 +61,7 @@ An [example samplesheet](../assets/samplesheet.csv) has been provided with the p nf-core/taxprofiler supports multiple databases being profiled in parallel for each tool. These databases, and specific parameters for each, can be specified in a 4 column comma-separated sheet. -> ⚠️ nf-core/taxprofiler does not provide any databases by default, nor currently generates them for you. This must be performed manually by the user. +> ⚠️ nf-core/taxprofiler does not provide any databases by default, nor does it currently generate them for you. This must be performed manually by the user. An example database sheet can look as follows, where 4 tools are being used, and `malt` and `kraken2` will be used against two databases each. From 29073e3c93e1ea5a4221ca8ec2080547716b503c Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 11 Apr 2022 13:38:42 +0200 Subject: [PATCH 139/306] Update CI tests --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79148f0..5e57889 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,7 @@ jobs: - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs" - "--shortread_complexityfilter_tool bbduk" - "--shortread_complexityfilter_tool prinseq" + - "--shortread_complexityfilter false --shortread_hostremoval" steps: - name: Check out pipeline code From 78182c2a8ca72d17b3a1004f100180d44439db42 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 11 Apr 2022 13:40:24 +0200 Subject: [PATCH 140/306] Add comma --- workflows/taxprofiler.nf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index abb330d..95e3588 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -125,7 +125,7 @@ workflow TAXPROFILER { } else { ch_longreads_preprocessed = INPUT_CHECK.out.nanopore } - + /* SUBWORKFLOW: COMPLEXITY FILTERING */ @@ -146,12 +146,12 @@ workflow TAXPROFILER { } else { ch_shortreads_hostremoved = ch_shortreads_filtered } - + /* SUBWORKFLOW: PROFILING */ - PROFILING ( ch_shortreads_hostremoved ch_longreads_preprocessed, DB_CHECK.out.dbs ) + PROFILING ( ch_shortreads_hostremoved, ch_longreads_preprocessed, DB_CHECK.out.dbs ) ch_versions = ch_versions.mix( PROFILING.out.versions ) /* @@ -191,7 +191,7 @@ workflow TAXPROFILER { if (params.shortread_hostremoval) { ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_HOSTREMOVAL.out.mqc.collect{it[1]}.ifEmpty([])) } - + ch_multiqc_files = ch_multiqc_files.mix( PROFILING.out.mqc ) // TODO create multiQC module for metaphlan From 989dc55ce107ae01331727eb9678a0d41e22de03 Mon Sep 17 00:00:00 2001 From: sofstam Date: Mon, 11 Apr 2022 17:42:38 +0200 Subject: [PATCH 141/306] Delete extra nf-core directory --- nf-core/modules/centrifuge/centrifuge/main.nf | 61 ----------------- .../modules/centrifuge/centrifuge/meta.yml | 66 ------------------- 2 files changed, 127 deletions(-) delete mode 100644 nf-core/modules/centrifuge/centrifuge/main.nf delete mode 100644 nf-core/modules/centrifuge/centrifuge/meta.yml diff --git a/nf-core/modules/centrifuge/centrifuge/main.nf b/nf-core/modules/centrifuge/centrifuge/main.nf deleted file mode 100644 index 3d23fc9..0000000 --- a/nf-core/modules/centrifuge/centrifuge/main.nf +++ /dev/null @@ -1,61 +0,0 @@ -process CENTRIFUGE_CENTRIFUGE { - tag "$meta.id" - label 'process_high' - - conda (params.enable_conda ? "bioconda::centrifuge=1.0.4_beta" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/centrifuge:1.0.4_beta--h9a82719_6' : - 'quay.io/biocontainers/centrifuge:1.0.4_beta--h9a82719_6' }" - - input: - tuple val(meta), path(reads) - path db - val save_unaligned - val save_aligned - val sam_format - - output: - tuple val(meta), path('*report.txt') , emit: report - tuple val(meta), path('*results.txt') , emit: results - tuple val(meta), path('*.sam') , optional: true, emit: sam - tuple val(meta), path('*.mapped.fastq{,.1,.2}.gz') , optional: true, emit: fastq_mapped - tuple val(meta), path('*.unmapped.fastq{,.1,.2}.gz') , optional: true, emit: fastq_unmapped - 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}" - def paired = meta.single_end ? "-U ${reads}" : "-1 ${reads[0]} -2 ${reads[1]}" - def unaligned = '' - def aligned = '' - if (meta.single_end) { - unaligned = save_unaligned ? "--un-gz ${prefix}.unmapped.fastq.gz" : '' - aligned = save_aligned ? "--al-gz ${prefix}.mapped.fastq.gz" : '' - } else { - unaligned = save_unaligned ? "--un-conc-gz ${prefix}.unmapped.fastq.gz" : '' - aligned = save_aligned ? "--al-conc-gz ${prefix}.mapped.fastq.gz" : '' - } - def sam_output = sam_format ? "--out-fmt 'sam'" : '' - """ - ## we add "-no-name ._" to ensure silly Mac OSX metafiles files aren't included - db_name=`find -L ${db} -name "*.1.cf" -not -name "._*" | sed 's/.1.cf//'` - centrifuge \\ - -x \$db_name \\ - -p $task.cpus \\ - $paired \\ - --report-file ${prefix}.report.txt \\ - -S ${prefix}.results.txt \\ - $unaligned \\ - $aligned \\ - $sam_output \\ - $args - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - centrifuge: \$( centrifuge --version | sed -n 1p | sed 's/^.*centrifuge-class version //') - END_VERSIONS - """ -} diff --git a/nf-core/modules/centrifuge/centrifuge/meta.yml b/nf-core/modules/centrifuge/centrifuge/meta.yml deleted file mode 100644 index a252c00..0000000 --- a/nf-core/modules/centrifuge/centrifuge/meta.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: centrifuge_centrifuge -description: Classifies metagenomic sequence data -keywords: - - classify - - metagenomics - - fastq - - db -tools: - - centrifuge: - description: Centrifuge is a classifier for metagenomic sequences. - homepage: https://ccb.jhu.edu/software/centrifuge/ - documentation: https://ccb.jhu.edu/software/centrifuge/manual.shtml - doi: 10.1101/gr.210641.116 - licence: ["GPL v3"] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - - db: - type: directory - description: Path to directory containing centrifuge database files - - save_unaligned: - type: value - description: If true unmapped fastq files are saved - - save_aligned: - type: value - description: If true mapped fastq files are saved -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - report: - type: file - description: | - File containing a classification summary - pattern: "*.{report.txt}" - - results: - type: file - description: | - File containing classification results - pattern: "*.{results.txt}" - - fastq_unmapped: - type: file - description: Unmapped fastq files - pattern: "*.unmapped.fastq.gz" - - fastq_mapped: - type: file - description: Mapped fastq files - pattern: "*.mapped.fastq.gz" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@sofstam" - - "@jfy133" - - "@sateeshperi" From a5f4fc42d53fce99863a3d135b017b9e468722be Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 12 Apr 2022 09:25:27 +0200 Subject: [PATCH 142/306] Fix run merging for unmerged PE data --- conf/modules.config | 2 +- workflows/taxprofiler.nf | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 97e9510..eb448bb 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -162,7 +162,7 @@ process { } withName: CAT_FASTQ { - ext.prefix = { "${meta.id}-${meta.run_accession}" } + ext.prefix = { "${meta.id}" } publishDir = [ path: { "${params.outdir}/run_merging/" }, mode: params.publish_dir_mode, diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 1da812d..2eb7e8c 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -139,7 +139,11 @@ workflow TAXPROFILER { meta_new.remove('run_accession') [ meta_new, reads ] } - .groupTuple() + .groupTuple(by: 0) + .map { + meta, reads -> + [ meta, reads.flatten() ] + } .branch { // we can't concate files if there is not a second run, we branch // here to separate them out, and mix after @@ -151,7 +155,6 @@ workflow TAXPROFILER { .mix( ch_reads_for_cat_branch.skip ) .map { meta, reads -> - [ meta, [ reads ].flatten() ] } From 9f221f84cc66ca121a46d8f68db8f727dec523c9 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 12 Apr 2022 10:12:17 +0200 Subject: [PATCH 143/306] Only supply single input channel to profiling, as these are merged into single input channel at run_merging --- subworkflows/local/profiling.nf | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index c74c583..07b6b72 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -9,8 +9,7 @@ include { METAPHLAN3 } from '../../modules/nf-core/modules/meta workflow PROFILING { take: - shortreads // [ [ meta ], [ reads ] ] - longreads // [ [ meta ], [ reads ] ] + reads // [ [ meta ], [ reads ] ] databases // [ [ meta ], path ] main: @@ -22,9 +21,9 @@ workflow PROFILING { */ // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] - ch_input_for_profiling = shortreads - .mix( longreads ) + ch_input_for_profiling = reads .combine(databases) + .dump(tag: "combined_withdbs") .branch { malt: it[2]['tool'] == 'malt' kraken2: it[2]['tool'] == 'kraken2' From 2ef21c6ef3e46ae824ad5c99970b8a5250ef8e38 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 12 Apr 2022 10:14:05 +0200 Subject: [PATCH 144/306] Fix input to profiling --- subworkflows/local/profiling.nf | 1 - workflows/taxprofiler.nf | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 07b6b72..ac744aa 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -23,7 +23,6 @@ workflow PROFILING { // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] ch_input_for_profiling = reads .combine(databases) - .dump(tag: "combined_withdbs") .branch { malt: it[2]['tool'] == 'malt' kraken2: it[2]['tool'] == 'kraken2' diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 0916cac..7c02f4c 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -167,7 +167,7 @@ workflow TAXPROFILER { SUBWORKFLOW: PROFILING */ - PROFILING ( ch_reads_runmerged, ch_longreads_preprocessed, DB_CHECK.out.dbs ) + PROFILING ( ch_reads_runmerged, DB_CHECK.out.dbs ) ch_versions = ch_versions.mix( PROFILING.out.versions ) /* From 26399718b2440499f294ebc59d669dd78dd7cdb6 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 12 Apr 2022 10:46:03 +0200 Subject: [PATCH 145/306] Re-add pairment attachment --- subworkflows/local/profiling.nf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index ac744aa..b03b83e 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -23,6 +23,12 @@ workflow PROFILING { // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] ch_input_for_profiling = reads .combine(databases) + meta, reads -> + def meta_new = meta.clone() + pairtype = meta_new['single_end'] ? '_se' : '_pe' + meta_new['id'] = meta_new['id'] + pairtype + [meta_new, reads] + } .branch { malt: it[2]['tool'] == 'malt' kraken2: it[2]['tool'] == 'kraken2' From 8d689141924a52cf72e666e76db1b575d541a341 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 12 Apr 2022 10:47:44 +0200 Subject: [PATCH 146/306] Re-add operator name --- subworkflows/local/profiling.nf | 1 + 1 file changed, 1 insertion(+) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index b03b83e..8a156c2 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -23,6 +23,7 @@ workflow PROFILING { // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] ch_input_for_profiling = reads .combine(databases) + .map { meta, reads -> def meta_new = meta.clone() pairtype = meta_new['single_end'] ? '_se' : '_pe' From a15c45b00cd18e2148baf7112f15c340261acdef Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 12 Apr 2022 10:51:27 +0200 Subject: [PATCH 147/306] Put map in the rigt place --- subworkflows/local/profiling.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 8a156c2..18de739 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -22,7 +22,6 @@ workflow PROFILING { // e.g. output [DUMP: reads_plus_db] [['id':'2612', 'run_accession':'combined', 'instrument_platform':'ILLUMINA', 'single_end':1], /2612.merged.fastq.gz, ['tool':'malt', 'db_name':'mal95', 'db_params':'"-id 90"'], /malt90] ch_input_for_profiling = reads - .combine(databases) .map { meta, reads -> def meta_new = meta.clone() @@ -30,6 +29,7 @@ workflow PROFILING { meta_new['id'] = meta_new['id'] + pairtype [meta_new, reads] } + .combine(databases) .branch { malt: it[2]['tool'] == 'malt' kraken2: it[2]['tool'] == 'kraken2' From 16a3556bfcfddf8195bbcd486653325aaa1de4ee Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Apr 2022 08:26:08 +0200 Subject: [PATCH 148/306] Changes after code review --- workflows/taxprofiler.nf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 7c02f4c..58671b3 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -139,15 +139,16 @@ workflow TAXPROFILER { meta_new.remove('run_accession') [ meta_new, reads ] } - .groupTuple(by: 0) + .groupTuple() .map { meta, reads -> [ meta, reads.flatten() ] } .branch { + meta, reads -> // we can't concatenate files if there is not a second run, we branch // here to separate them out, and mix back in after for efficiency - cat: ( it[0]['single_end'] && it[1].size() > 1 ) || ( !it[0]['single_end'] && it[1].size() > 2 ) + cat: ( meta.single_end && reads.size() > 1 ) || ( !meta.single_end && reads.size() > 2 ) skip: true } From de5734052601ce6bedd7be2389fef4bacd0affb1 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Apr 2022 11:49:35 +0200 Subject: [PATCH 149/306] Start work --- modules.json | 5 +- .../nf-core/modules/megan/rma2info/main.nf | 38 ++++++++++++++ .../nf-core/modules/megan/rma2info/meta.yml | 51 +++++++++++++++++++ nextflow.config | 1 + subworkflows/local/profiling.nf | 22 ++++---- .../local/shortread_postprocessing.nf | 39 ++++++++++++++ 6 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 modules/nf-core/modules/megan/rma2info/main.nf create mode 100644 modules/nf-core/modules/megan/rma2info/meta.yml create mode 100644 subworkflows/local/shortread_postprocessing.nf diff --git a/modules.json b/modules.json index 7fbc65c..e921454 100644 --- a/modules.json +++ b/modules.json @@ -30,6 +30,9 @@ "malt/run": { "git_sha": "72b96f4e504eef673f2b5c13560a9d90b669129b" }, + "megan/rma2info": { + "git_sha": "2d38566eca4cc15142b2ffa7c11837569b39aece" + }, "metaphlan3": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, @@ -47,4 +50,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/megan/rma2info/main.nf b/modules/nf-core/modules/megan/rma2info/main.nf new file mode 100644 index 0000000..80d1975 --- /dev/null +++ b/modules/nf-core/modules/megan/rma2info/main.nf @@ -0,0 +1,38 @@ +process MEGAN_RMA2INFO { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::megan=6.21.7" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/megan:6.21.7--h9ee0642_0': + 'quay.io/biocontainers/megan:6.21.7--h9ee0642_0' }" + + input: + tuple val(meta), path(rma6) + val(megan_summary) + + output: + tuple val(meta), path("*.txt.gz") , emit: txt + tuple val(meta), path("*.megan"), optional: true, emit: megan_summary + 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}" + def summary = megan_summary ? "-es ${prefix}.megan" : "" + """ + rma2info \\ + -i ${rma6} \\ + -o ${prefix}.txt.gz \\ + ${summary} \\ + $args + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + megan: \$(echo \$(rma2info 2>&1) | grep version | sed 's/.*version //g;s/, built.*//g') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/megan/rma2info/meta.yml b/modules/nf-core/modules/megan/rma2info/meta.yml new file mode 100644 index 0000000..0f2d5a9 --- /dev/null +++ b/modules/nf-core/modules/megan/rma2info/meta.yml @@ -0,0 +1,51 @@ +name: "megan_rma2info" +description: Analyses an RMA file and exports information in text format +keywords: + - megan + - rma6 + - classification + - conversion +tools: + - "megan": + description: "A tool for studying the taxonomic content of a set of DNA reads" + homepage: "https://uni-tuebingen.de/fakultaeten/mathematisch-naturwissenschaftliche-fakultaet/fachbereiche/informatik/lehrstuehle/algorithms-in-bioinformatics/software/megan6/" + documentation: "https://software-ab.informatik.uni-tuebingen.de/download/megan6/welcome.html" + tool_dev_url: "https://github.com/husonlab/megan-ce" + doi: "10.1371/journal.pcbi.1004957" + licence: "['GPL >=3']" + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - rma6: + type: file + description: RMA6 file from MEGAN or MALT + pattern: "*.rma6" + - megan_summary: + type: boolean + description: Specify whether to generate an MEGAN summary file + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - txt: + type: file + description: Compressed text file + pattern: "*.txt.gz" + - megan_summary: + type: file + description: Optionally generated MEGAN summary file + pattern: "*.megan" + +authors: + - "@jfy133" diff --git a/nextflow.config b/nextflow.config index b4a8d91..8ddf365 100644 --- a/nextflow.config +++ b/nextflow.config @@ -89,6 +89,7 @@ params { centrifuge_save_unaligned = false centrifuge_save_aligned = false centrifuge_sam_format = false + // metaphlan3 run_metaphlan3 = false } diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index c74c583..59b0dc0 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -14,8 +14,9 @@ workflow PROFILING { databases // [ [ meta ], path ] main: - ch_versions = Channel.empty() - ch_multiqc_files = Channel.empty() + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + ch_raw_profiles = Channel.empty() /* COMBINE READS WITH POSSIBLE DATABASES @@ -89,30 +90,33 @@ workflow PROFILING { if ( params.run_malt ) { MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) + ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) + ch_raw_profiles = ch_raw_profiles.mix( MALT_RUN.out.rma6 ) } if ( params.run_kraken2 ) { KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) + ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) + ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.txt ) } if ( params.run_centrifuge ) { CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_unaligned, params.centrifuge_save_aligned, params.centrifuge_sam_format ) - ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) + ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) + ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_CENTRIFUGE.out.report ) } if ( params.run_metaphlan3 ) { METAPHLAN3 ( ch_input_for_metaphlan3.reads, ch_input_for_metaphlan3.db ) ch_versions = ch_versions.mix( METAPHLAN3.out.versions.first() ) + ch_raw_profiles = ch_raw_profiles.mix( METAPHLAN3.out.biom ) } emit: - // TODO work out if there is enough standardisation of output to export as one? - //output = ch_filtered_reads // channel: [ val(meta), [ reads ] ] + profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] versions = ch_versions // channel: [ versions.yml ] mqc = ch_multiqc_files } diff --git a/subworkflows/local/shortread_postprocessing.nf b/subworkflows/local/shortread_postprocessing.nf new file mode 100644 index 0000000..7fb0d70 --- /dev/null +++ b/subworkflows/local/shortread_postprocessing.nf @@ -0,0 +1,39 @@ +// +// Perform read trimming and merging +// + + +include { SHORTREAD_FASTP } from './shortread_fastp' +include { SHORTREAD_ADAPTERREMOVAL } from './shortread_adapterremoval' +include { FASTQC as FASTQC_PROCESSED } from '../../modules/nf-core/modules/fastqc/main' + +workflow SHORTREAD_POSTPROCESSING { + take: + input // [ [ meta ], [ taxon_table/file ] ] + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + if ( params.shortread_clipmerge_tool == "fastp" ) { + ch_processed_reads = SHORTREAD_FASTP ( reads ).reads + ch_versions = ch_versions.mix( SHORTREAD_FASTP.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_FASTP.out.mqc ) + } else if ( params.shortread_clipmerge_tool == "adapterremoval" ) { + ch_processed_reads = SHORTREAD_ADAPTERREMOVAL ( reads ).reads + ch_versions = ch_versions.mix( SHORTREAD_ADAPTERREMOVAL.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_ADAPTERREMOVAL.out.mqc ) + } else { + ch_processed_reads = reads + } + + FASTQC_PROCESSED ( ch_processed_reads ) + ch_versions = ch_versions.mix( FASTQC_PROCESSED.out.versions ) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) + + emit: + output = output // channel: [ val(meta), taxon_table ] + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files +} + From dfeaa0d1fe79a6f59c27bdb481904b3bbb620234 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Apr 2022 12:00:28 +0200 Subject: [PATCH 150/306] Rename subworkflow parameters for consistency --- .github/workflows/ci.yml | 8 ++++---- conf/modules.config | 6 +++--- conf/test.config | 6 +++--- nextflow.config | 11 ++++++----- nextflow_schema.json | 22 +++++++++++----------- workflows/taxprofiler.nf | 16 ++++++++-------- 6 files changed, 35 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53423cb..c373bc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,8 @@ jobs: - NXF_VER: "" NXF_EDGE: "1" parameters: - - "--longread_clip false" - - "--shortread_clip false" + - "--perform_longread_clip false" + - "--perform_shortread_clipmerge false" - "--shortread_clipmerge_tool fastp" - "--shortread_clipmerge_tool fastp --shortread_clipmerge_mergepairs --shortread_clipmerge_excludeunmerged" - "--shortread_clipmerge_tool fastp --shortread_clipmerge_mergepairs" @@ -39,8 +39,8 @@ jobs: - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs" - "--shortread_complexityfilter_tool bbduk" - "--shortread_complexityfilter_tool prinseq" - - "--run_merging" - - "--run_merging --shortread_clipmerge_mergepairs" + - "--perform_runmerging" + - "--perform_runmerging --shortread_clipmerge_mergepairs" steps: - name: Check out pipeline code diff --git a/conf/modules.config b/conf/modules.config index d93486f..42528de 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -173,7 +173,7 @@ process { withName: MALT_RUN { ext.args = { "${meta.db_params}" } - ext.prefix = params.run_merging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/malt/${meta.db_name}" }, mode: params.publish_dir_mode, @@ -183,7 +183,7 @@ process { withName: KRAKEN2_KRAKEN2 { ext.args = { "${meta.db_params}" } - ext.prefix = params.run_merging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: params.publish_dir_mode, @@ -193,7 +193,7 @@ process { withName: METAPHLAN3 { ext.args = { "${meta.db_params}" } - ext.prefix = params.run_merging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/metaphlan3/${meta.db_name}" }, mode: params.publish_dir_mode, diff --git a/conf/test.config b/conf/test.config index 6e82300..923dda7 100644 --- a/conf/test.config +++ b/conf/test.config @@ -28,7 +28,7 @@ params { run_malt = true run_metaphlan3 = true run_centrifuge = true - shortread_clipmerge = true - longread_clip = false - shortread_complexityfilter = true + perform_shortread_clipmerge = true + perform_longread_clip = false + perform_shortread_complexityfilter = true } diff --git a/nextflow.config b/nextflow.config index da8bbdb..b72b4f9 100644 --- a/nextflow.config +++ b/nextflow.config @@ -55,7 +55,7 @@ params { databases = null // FASTQ preprocessing - shortread_clipmerge = false + perform_shortread_clipmerge = false shortread_clipmerge_tool = 'fastp' shortread_clipmerge_skipadaptertrim = false shortread_clipmerge_mergepairs = false @@ -63,11 +63,11 @@ params { shortread_clipmerge_adapter1 = null shortread_clipmerge_adapter2 = null shortread_clipmerge_minlength = 15 - longread_clip = false + perform_longread_clip = false save_preprocessed_reads = false // Complexity filtering - shortread_complexityfilter = false + perform_shortread_complexityfilter = false shortread_complexityfilter_tool = 'bbduk' shortread_complexityfilter_entropy = 0.3 shortread_complexityfilter_bbduk_windowsize = 50 @@ -77,8 +77,8 @@ params { save_complexityfiltered_reads = false // run merging - run_merging = false - save_runmerged_reads = false + perform_runmerging = false + save_runmerged_reads = false // MALT run_malt = false @@ -92,6 +92,7 @@ params { centrifuge_save_unaligned = false centrifuge_save_aligned = false centrifuge_sam_format = false + // metaphlan3 run_metaphlan3 = false } diff --git a/nextflow_schema.json b/nextflow_schema.json index 4db7fa0..06bd94b 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -262,15 +262,9 @@ "type": "string", "default": "None" }, - "shortread_clipmerge": { - "type": "boolean" - }, "shortread_clipmerge_excludeunmerged": { "type": "boolean" }, - "longread_clip": { - "type": "boolean" - }, "run_malt": { "type": "boolean" }, @@ -334,9 +328,6 @@ "shortread_complexityfilter_bbduk_mask": { "type": "boolean" }, - "shortread_complexityfilter": { - "type": "boolean" - }, "shortread_complexityfilter_entropy": { "type": "number", "default": 0.3 @@ -353,10 +344,19 @@ "save_complexityfiltered_reads": { "type": "boolean" }, - "run_merging": { + "save_runmerged_reads": { "type": "boolean" }, - "save_runmerged_reads": { + "perform_shortread_clipmerge": { + "type": "boolean" + }, + "perform_longread_clip": { + "type": "boolean" + }, + "perform_shortread_complexityfilter": { + "type": "boolean" + }, + "perform_runmerging": { "type": "boolean" } } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 58671b3..f086557 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -101,14 +101,14 @@ workflow TAXPROFILER { /* SUBWORKFLOW: PERFORM PREPROCESSING */ - if ( params.shortread_clipmerge ) { + if ( params.perform_shortread_clipmerge ) { ch_shortreads_preprocessed = SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ).reads } else { ch_shortreads_preprocessed = INPUT_CHECK.out.fastq } - if ( params.longread_clip ) { + if ( params.perform_longread_clip ) { ch_longreads_preprocessed = LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ).reads .map { it -> [ it[0], [it[1]] ] } } else { @@ -119,7 +119,7 @@ workflow TAXPROFILER { SUBWORKFLOW: COMPLEXITY FILTERING */ - if ( params.shortread_complexityfilter ) { + if ( params.perform_shortread_complexityfilter ) { ch_shortreads_filtered = SHORTREAD_COMPLEXITYFILTERING ( ch_shortreads_preprocessed ).reads } else { ch_shortreads_filtered = ch_shortreads_preprocessed @@ -129,7 +129,7 @@ workflow TAXPROFILER { STEP: Run merging */ - if ( params.run_merging ) { + if ( params.perform_runmerging ) { ch_reads_for_cat_branch = ch_shortreads_filtered .mix( ch_longreads_preprocessed ) @@ -190,22 +190,22 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) - if (params.shortread_clipmerge) { + if (params.perform_shortread_clipmerge) { ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( SHORTREAD_PREPROCESSING.out.versions ) } - if (params.longread_clip) { + if (params.perform_longread_clip) { ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( LONGREAD_PREPROCESSING.out.versions ) } - if (params.shortread_complexityfilter){ + if (params.perform_shortread_complexityfilter){ ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } - if (params.run_merging){ + if (params.perform_runmerging){ ch_versions = ch_versions.mix(CAT_FASTQ.out.versions) } From aa2d07c42ae4e451711a4147264782d567576e88 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Apr 2022 14:19:19 +0200 Subject: [PATCH 151/306] Fix merge cockup --- .github/workflows/ci.yml | 2 +- conf/test.config | 2 +- nextflow.config | 2 +- nextflow_schema.json | 2 +- workflows/taxprofiler.nf | 13 +++++-------- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b18e601..a1ece72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: - "--shortread_complexityfilter_tool prinseq" - "--perform_runmerging" - "--perform_runmerging --shortread_clipmerge_mergepairs" - - "--shortread_complexityfilter false --shortread_hostremoval" + - "--shortread_complexityfilter false --perform_shortread_hostremoval" steps: - name: Check out pipeline code diff --git a/conf/test.config b/conf/test.config index 1d08d91..8f12312 100644 --- a/conf/test.config +++ b/conf/test.config @@ -31,6 +31,6 @@ params { perform_shortread_clipmerge = true perform_longread_clip = false perform_shortread_complexityfilter = true - shortread_hostremoval = true + perform_shortread_hostremoval = true shortread_hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' } diff --git a/nextflow.config b/nextflow.config index 6b0a79d..94c5837 100644 --- a/nextflow.config +++ b/nextflow.config @@ -81,7 +81,7 @@ params { save_runmerged_reads = false // Host Removal - shortread_hostremoval = false + perform_shortread_hostremoval = false shortread_hostremoval_reference = null shortread_hostremoval_index = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 07295e3..cf0edab 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -359,7 +359,7 @@ "perform_runmerging": { "type": "boolean" }, - "shortread_hostremoval": { + "perform_shortread_hostremoval": { "type": "boolean" }, "shortread_hostremoval_reference": { diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 894a1e1..9fe8cc8 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -22,11 +22,10 @@ if (params.databases) { ch_databases = file(params.databases) } else { exit 1, ' if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] warning: MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "[nf-core/taxprofiler] error: cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" -// TODO Add check if index but no reference exit 1 -if (params.shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } +if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } -if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } else { ch_reference = [] } +if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } if (params.shortread_hostremoval_index ) { ch_reference_index = file(params.shortread_hostremoval_index ) } else { ch_reference_index = [] } /* @@ -140,15 +139,13 @@ workflow TAXPROFILER { SUBWORKFLOW: HOST REMOVAL */ - if ( params.shortread_hostremoval ) { + if ( params.perform_shortread_hostremoval ) { ch_shortreads_hostremoved = SHORTREAD_HOSTREMOVAL ( ch_shortreads_filtered, ch_reference, ch_reference_index ).reads ch_versions = ch_versions.mix(SHORTREAD_HOSTREMOVAL.out.versions.first()) } else { ch_shortreads_hostremoved = ch_shortreads_filtered } - */ - if ( params.perform_runmerging ) { ch_reads_for_cat_branch = ch_shortreads_hostremoved @@ -225,11 +222,11 @@ workflow TAXPROFILER { ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } - if (params.shortread_hostremoval) { + if (params.perform_shortread_hostremoval) { ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_HOSTREMOVAL.out.mqc.collect{it[1]}.ifEmpty([])) ch_versions = ch_versions.mix(SHORTREAD_HOSTREMOVAL.out.versions) } - + if (params.perform_runmerging){ ch_versions = ch_versions.mix(CAT_FASTQ.out.versions) } From 5e80df0f949557883ea6e961b0ea7ba3214065c5 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 13 Apr 2022 15:27:53 +0200 Subject: [PATCH 152/306] Apply suggestions from code review --- conf/test.config | 4 ++-- nextflow.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/test.config b/conf/test.config index 8f12312..9fa5de8 100644 --- a/conf/test.config +++ b/conf/test.config @@ -31,6 +31,6 @@ params { perform_shortread_clipmerge = true perform_longread_clip = false perform_shortread_complexityfilter = true - perform_shortread_hostremoval = true - shortread_hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' + perform_shortread_hostremoval = true + shortread_hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' } diff --git a/nextflow.config b/nextflow.config index 94c5837..bf3ca92 100644 --- a/nextflow.config +++ b/nextflow.config @@ -81,7 +81,7 @@ params { save_runmerged_reads = false // Host Removal - perform_shortread_hostremoval = false + perform_shortread_hostremoval = false shortread_hostremoval_reference = null shortread_hostremoval_index = null From 68ce5843a475b6d4a28713a82ecc30816368d0ff Mon Sep 17 00:00:00 2001 From: sofstam Date: Wed, 13 Apr 2022 18:51:56 +0200 Subject: [PATCH 153/306] Add kaiju in taxprofiler --- conf/modules.config | 9 ++++ conf/test.config | 13 ++--- modules/nf-core/modules/kaiju/kaiju/main.nf | 41 +++++++++++++++ modules/nf-core/modules/kaiju/kaiju/meta.yml | 53 ++++++++++++++++++++ nextflow.config | 3 ++ subworkflows/local/profiling.nf | 13 +++++ 6 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 modules/nf-core/modules/kaiju/kaiju/main.nf create mode 100644 modules/nf-core/modules/kaiju/kaiju/meta.yml diff --git a/conf/modules.config b/conf/modules.config index ccd1748..f85df30 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -245,4 +245,13 @@ process { ] } + withName: KAIJU_KAIJU { + publishDir = [ + path: { "${params.outdir}/kaiju/${meta.db_name}" }, + mode: params.publish_dir_mode, + pattern: '*.tsv' + ] + ext.args = { "${meta.db_params}" } + ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + } } diff --git a/conf/test.config b/conf/test.config index 9fa5de8..884ff32 100644 --- a/conf/test.config +++ b/conf/test.config @@ -22,15 +22,16 @@ params { // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' - databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - run_kraken2 = true - run_malt = true - run_metaphlan3 = true - run_centrifuge = true + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' + run_kraken2 = true + run_malt = true + run_metaphlan3 = true + run_centrifuge = true perform_shortread_clipmerge = true perform_longread_clip = false perform_shortread_complexityfilter = true perform_shortread_hostremoval = true shortread_hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' + run_kaiju = true } diff --git a/modules/nf-core/modules/kaiju/kaiju/main.nf b/modules/nf-core/modules/kaiju/kaiju/main.nf new file mode 100644 index 0000000..ae8f99e --- /dev/null +++ b/modules/nf-core/modules/kaiju/kaiju/main.nf @@ -0,0 +1,41 @@ +process KAIJU_KAIJU { + tag "$meta.id" + label 'process_high' + + conda (params.enable_conda ? "bioconda::kaiju=1.8.2" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/kaiju:1.8.2--h5b5514e_1': + 'quay.io/biocontainers/kaiju:1.8.2--h5b5514e_1' }" + + input: + tuple val(meta), path(reads) + path(db) + + output: + tuple val(meta), path('*.tsv'), emit: results + 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}" + def input = meta.single_end ? "-i ${reads}" : "-i ${reads[0]} -j ${reads[1]}" + """ + dbnodes=`find -L ${db} -name "*nodes.dmp"` + dbname=`find -L ${db} -name "*.fmi" -not -name "._*"` + kaiju \\ + $args \\ + -z $task.cpus \\ + -t \$dbnodes \\ + -f \$dbname \\ + -o ${prefix}.tsv \\ + $input + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + kaiju: \$(echo \$( kaiju -h 2>&1 | sed -n 1p | sed 's/^.*Kaiju //' )) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/kaiju/kaiju/meta.yml b/modules/nf-core/modules/kaiju/kaiju/meta.yml new file mode 100644 index 0000000..e24c8ef --- /dev/null +++ b/modules/nf-core/modules/kaiju/kaiju/meta.yml @@ -0,0 +1,53 @@ +name: kaiju_kaiju +description: Taxonomic classification of metagenomic sequence data using a protein reference database +keywords: + - classify + - metagenomics + - fastq + - taxonomic profiling +tools: + - kaiju: + description: Fast and sensitive taxonomic classification for metagenomics + homepage: https://kaiju.binf.ku.dk/ + documentation: https://github.com/bioinformatics-centre/kaiju/blob/master/README.md + tool_dev_url: https://github.com/bioinformatics-centre/kaiju + doi: "10.1038/ncomms11257" + licence: ["GNU GPL v3"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input fastq/fasta files of size 1 and 2 for single-end and paired-end data, + respectively. + pattern: "*.{fastq,fq,fasta,fa,fsa,fas,fna,fastq.gz,fq.gz,fasta.gz,fa.gz,fsa.gz,fas.gz,fna.gz}" + - db: + type: files + description: | + List containing the database and nodes files for Kaiju + e.g. [ 'database.fmi', 'nodes.dmp' ] + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - results: + type: file + description: Results with taxonomic classification of each read + pattern: "*.tsv" + +authors: + - "@talnor" + - "@sofstam" + - "@jfy133" diff --git a/nextflow.config b/nextflow.config index bf3ca92..9ad84d5 100644 --- a/nextflow.config +++ b/nextflow.config @@ -100,6 +100,9 @@ params { // metaphlan3 run_metaphlan3 = false + + // kaiju + run_kaiju = false } // Load base.config by default for all pipelines diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 18de739..272b5b4 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -6,6 +6,7 @@ include { MALT_RUN } from '../../modules/nf-core/modules/malt include { KRAKEN2_KRAKEN2 } from '../../modules/nf-core/modules/kraken2/kraken2/main' include { CENTRIFUGE_CENTRIFUGE } from '../../modules/nf-core/modules/centrifuge/centrifuge/main' include { METAPHLAN3 } from '../../modules/nf-core/modules/metaphlan3/main' +include { KAIJU_KAIJU } from '../../modules/nf-core/modules/kaiju/kaiju/main' workflow PROFILING { take: @@ -35,6 +36,7 @@ workflow PROFILING { kraken2: it[2]['tool'] == 'kraken2' metaphlan3: it[2]['tool'] == 'metaphlan3' centrifuge: it[2]['tool'] == 'centrifuge' + kaiju: it[2]['tool'] == 'kaiju' unknown: true } @@ -88,6 +90,13 @@ workflow PROFILING { db: it[3] } + ch_input_for_kaiju = ch_input_for_profiling.kaiju + .multiMap { + it -> + reads: [it[0] + it[2], it[1]] + db: it[3] + } + /* RUN PROFILING */ @@ -114,6 +123,10 @@ workflow PROFILING { ch_versions = ch_versions.mix( METAPHLAN3.out.versions.first() ) } + if ( params.run_kaiju ) { + KAIJU_KAIJU ( ch_input_for_kaiju.reads, ch_input_for_kaiju.db ) + ch_versions = ch_versions.mix( KAIJU_KAIJU.out.versions.first() ) + } emit: // TODO work out if there is enough standardisation of output to export as one? From 6736a2a980edd1244c8f3943d385f07f5b8a69ff Mon Sep 17 00:00:00 2001 From: sofstam Date: Wed, 13 Apr 2022 19:00:52 +0200 Subject: [PATCH 154/306] Update README.md, nextflow_schema.json --- README.md | 1 + modules.json | 3 +++ nextflow_schema.json | 34 +++++++++++++++++++++++++++------- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f1d59d5..66578e5 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ On release, automated continuous integration tests run the pipeline on a full-si - [Centrifuge](https://ccb.jhu.edu/software/centrifuge/) - [Kaiju](https://kaiju.binf.ku.dk/) - [mOTUs](https://motu-tool.org/) + - [MetaMaps](https://github.com/DiltheyLab/MetaMaps) 4. Perform optional post-processing with: - [bracken](https://ccb.jhu.edu/software/bracken/) 5. Standardises output tables diff --git a/modules.json b/modules.json index 2be0b7f..726e701 100644 --- a/modules.json +++ b/modules.json @@ -50,6 +50,9 @@ }, "untar": { "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" + }, + "kaiju/kaiju": { + "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" } } } diff --git a/nextflow_schema.json b/nextflow_schema.json index cf0edab..75854bf 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +348,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -364,11 +380,15 @@ }, "shortread_hostremoval_reference": { "type": "string", - "default": null + "default": "None" }, "shortread_hostremoval_index": { "type": "string", - "default": null + "default": "None" + }, + "run_kaiju": { + "type": "string", + "default": "false" } } -} +} \ No newline at end of file From 0e5c9e7bdd4f00323969f5f6d7217cc33ba63f5b Mon Sep 17 00:00:00 2001 From: sofstam Date: Thu, 14 Apr 2022 09:14:05 +0200 Subject: [PATCH 155/306] Type:boolean for kaiju in nextflow_schema.json --- nextflow_schema.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 75854bf..5fe371d 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -387,8 +387,7 @@ "default": "None" }, "run_kaiju": { - "type": "string", - "default": "false" + "type": "boolean" } } -} \ No newline at end of file +} From cc73cdd51d26330d831ec306f8a5da081fb8665a Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 16 Apr 2022 07:42:30 +0200 Subject: [PATCH 156/306] Add generation of taxon-table like output for MALT --- CITATIONS.md | 10 ++++- conf/modules.config | 15 ++++++- nextflow.config | 1 + nextflow_schema.json | 7 +++- subworkflows/local/profiling.nf | 32 +++++++++++---- .../local/shortread_postprocessing.nf | 39 ------------------- 6 files changed, 52 insertions(+), 52 deletions(-) delete mode 100644 subworkflows/local/shortread_postprocessing.nf diff --git a/CITATIONS.md b/CITATIONS.md index e7bcf47..02621d9 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -40,9 +40,17 @@ > Vågene, Åshild J., Alexander Herbig, Michael G. Campana, Nelly M. Robles García, Christina Warinner, Susanna Sabin, Maria A. Spyrou, et al. 2018. Salmonella Enterica Genomes from Victims of a Major Sixteenth-Century Epidemic in Mexico. Nature Ecology & Evolution 2 (3): 520-28. doi: 10.1038/s41559-017-0446-6. +- [MEGAN](https://doi.org/10.1371/journal.pcbi.1004957) + + > Huson, Daniel H., Sina Beier, Isabell Flade, Anna Górska, Mohamed El-Hadidi, Suparna Mitra, Hans-Joachim Ruscheweyh, and Rewati Tappu. 2016. “MEGAN Community Edition - Interactive Exploration and Analysis of Large-Scale Microbiome Sequencing Data.” PLoS Computational Biology 12 (6): e1004957. doi: 10.1371/journal.pcbi.1004957. + - [MetaPhlAn3](https://doi.org/10.7554/eLife.65088) - > Beghini, Francesco, Lauren J McIver, Aitor Blanco-Míguez, Leonard Dubois, Francesco Asnicar, Sagun Maharjan, Ana Mailyan, et al. 2021. “Integrating Taxonomic, Functional, and Strain-Level Profiling of Diverse Microbial Communities with BioBakery 3.” Edited by Peter Turnbaugh, Eduardo Franco, and C Titus Brown. ELife 10 (May): e65088. + > Beghini, Francesco, Lauren J McIver, Aitor Blanco-Míguez, Leonard Dubois, Francesco Asnicar, Sagun Maharjan, Ana Mailyan, et al. 2021. “Integrating Taxonomic, Functional, and Strain-Level Profiling of Diverse Microbial Communities with BioBakery 3.” Edited by Peter Turnbaugh, Eduardo Franco, and C Titus Brown. ELife 10 (May): e65088. doi: 10.7554/eLife.65088 + +- [Centrifuge](https://doi.org/10.1101/gr.210641.116) + + > Kim, Daehwan, Li Song, Florian P. Breitwieser, and Steven L. Salzberg. 2016. “Centrifuge: Rapid and Sensitive Classification of Metagenomic Sequences.” Genome Research 26 (12): 1721-29. doi: 10.1101/gr.210641.116. ## Software packaging/containerisation tools diff --git a/conf/modules.config b/conf/modules.config index ccd1748..23455a4 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -191,11 +191,22 @@ process { withName: MALT_RUN { ext.args = { "${meta.db_params}" } - ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + // one run with multiple samples, so fix ID to just db name to ensure clean log name + ext.prefix = { "${meta.db_name}" } publishDir = [ path: { "${params.outdir}/malt/${meta.db_name}" }, mode: params.publish_dir_mode, - pattern: '*.{log}' + pattern: '*.{rma6,log,sam}' + ] + } + + withName: MEGAN_RMA2INFO { + ext.args = "-c2c Taxonomy" + ext.prefix = { "${meta.id}" } + publishDir = [ + path: { "${params.outdir}/malt/${meta.db_name}" }, + mode: params.publish_dir_mode, + pattern: '*.{txt.gz,megan}' ] } diff --git a/nextflow.config b/nextflow.config index bf3ca92..a618f66 100644 --- a/nextflow.config +++ b/nextflow.config @@ -88,6 +88,7 @@ params { // MALT run_malt = false malt_mode = 'BlastN' + malt_generatemegansummary = false // kraken2 run_kraken2 = false diff --git a/nextflow_schema.json b/nextflow_schema.json index cf0edab..2cbfb0e 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -364,11 +364,14 @@ }, "shortread_hostremoval_reference": { "type": "string", - "default": null + "default": "None" }, "shortread_hostremoval_index": { "type": "string", - "default": null + "default": "None" + }, + "malt_generatemegansummary": { + "type": "boolean" } } } diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 23b96e7..d7532b7 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -3,6 +3,7 @@ // include { MALT_RUN } from '../../modules/nf-core/modules/malt/run/main' +include { MEGAN_RMA2INFO } from '../../modules/nf-core/modules/megan/rma2info/main' include { KRAKEN2_KRAKEN2 } from '../../modules/nf-core/modules/kraken2/kraken2/main' include { CENTRIFUGE_CENTRIFUGE } from '../../modules/nf-core/modules/centrifuge/centrifuge/main' include { METAPHLAN3 } from '../../modules/nf-core/modules/metaphlan3/main' @@ -95,33 +96,48 @@ workflow PROFILING { if ( params.run_malt ) { MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) - ch_raw_profiles = ch_raw_profiles.mix( MALT_RUN.out.rma6 ) + + ch_maltrun_for_megan = MALT_RUN.out.rma6 + .transpose() + .map{ + meta, rma -> + // re-extract meta from file names, use filename without rma to + // ensure we keep paired-end information in downstream filenames + // when no pair-merging + def meta_new = meta.clone() + meta_new['db_name'] = meta.id + meta_new['id'] = rma.name - ( '.' + rma.extension ) + [ meta_new, rma ] + } + + MEGAN_RMA2INFO (ch_maltrun_for_megan, params.malt_generatemegansummary ) + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) + ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) + ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) } if ( params.run_kraken2 ) { KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) + ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.txt ) } if ( params.run_centrifuge ) { CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_unaligned, params.centrifuge_save_aligned, params.centrifuge_sam_format ) - ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) + ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_CENTRIFUGE.out.report ) } if ( params.run_metaphlan3 ) { METAPHLAN3 ( ch_input_for_metaphlan3.reads, ch_input_for_metaphlan3.db ) - ch_versions = ch_versions.mix( METAPHLAN3.out.versions.first() ) + ch_versions = ch_versions.mix( METAPHLAN3.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( METAPHLAN3.out.biom ) } emit: - profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] + profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom versions = ch_versions // channel: [ versions.yml ] mqc = ch_multiqc_files } diff --git a/subworkflows/local/shortread_postprocessing.nf b/subworkflows/local/shortread_postprocessing.nf deleted file mode 100644 index 7fb0d70..0000000 --- a/subworkflows/local/shortread_postprocessing.nf +++ /dev/null @@ -1,39 +0,0 @@ -// -// Perform read trimming and merging -// - - -include { SHORTREAD_FASTP } from './shortread_fastp' -include { SHORTREAD_ADAPTERREMOVAL } from './shortread_adapterremoval' -include { FASTQC as FASTQC_PROCESSED } from '../../modules/nf-core/modules/fastqc/main' - -workflow SHORTREAD_POSTPROCESSING { - take: - input // [ [ meta ], [ taxon_table/file ] ] - - main: - ch_versions = Channel.empty() - ch_multiqc_files = Channel.empty() - - if ( params.shortread_clipmerge_tool == "fastp" ) { - ch_processed_reads = SHORTREAD_FASTP ( reads ).reads - ch_versions = ch_versions.mix( SHORTREAD_FASTP.out.versions ) - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_FASTP.out.mqc ) - } else if ( params.shortread_clipmerge_tool == "adapterremoval" ) { - ch_processed_reads = SHORTREAD_ADAPTERREMOVAL ( reads ).reads - ch_versions = ch_versions.mix( SHORTREAD_ADAPTERREMOVAL.out.versions ) - ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_ADAPTERREMOVAL.out.mqc ) - } else { - ch_processed_reads = reads - } - - FASTQC_PROCESSED ( ch_processed_reads ) - ch_versions = ch_versions.mix( FASTQC_PROCESSED.out.versions ) - ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) - - emit: - output = output // channel: [ val(meta), taxon_table ] - versions = ch_versions // channel: [ versions.yml ] - mqc = ch_multiqc_files -} - From d0256ec9378eac0cad715fe364d997a84e9b312b Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 16 Apr 2022 07:45:24 +0200 Subject: [PATCH 157/306] Prettier --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 18c0e69..18dea60 100644 --- a/modules.json +++ b/modules.json @@ -56,4 +56,4 @@ } } } -} \ No newline at end of file +} From 818d3bd053beb2a33730d91d8556c544ff94b417 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 17 Apr 2022 17:53:22 +0200 Subject: [PATCH 158/306] Complete documentation for preprocessing subworkflows --- docs/usage.md | 107 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 239a55d..528aeef 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -10,6 +10,8 @@ ## Samplesheet inputs +nf-core/profiler can accept as input raw or preprocessed single- or paired-end short-read (e.g. Illumina) FASTQ files, long-read FASTQ files (e.g. Oxford Nanopore), or FASTA sequences (when accepted by a profiler). + You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 6 columns, and a header row as shown in the examples below. Furthermother, nf-core/taxprofiler also requires a second comma-separated file of 3 columns with a header row as in the examples below. This samplesheet is then specified on the command line as follows: @@ -20,7 +22,7 @@ This samplesheet is then specified on the command line as follows: ### Multiple runs of the same sample -The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will process reads before performing profiling. Below is an example for the same sample sequenced across 3 lanes: +The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will concatenate different runs FASTQ files of the same sample before performing profiling, when `--perform_runmerging` is supplied. Below is an example for the same sample sequenced across 3 lanes: ```console sample,run_accession,instrument_platform,fastq_1,fastq_2,fasta @@ -36,7 +38,7 @@ sample,run_accession,instrument_platform,fastq_1,fastq_2,fasta The pipeline will auto-detect whether a sample is single- or paired-end using the information provided in the samplesheet. The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 6 columns to match those defined in the table below. -A final samplesheet file consisting of both single- and paired-end data, as well as long-read FASTA fies may look something like the one below. This is for 6 samples, where `2612` has been sequenced twice. +A final samplesheet file consisting of both single- and paired-end data, as well as long-read FASTA files may look something like the one below. This is for 6 samples, where `2612` has been sequenced twice. ```console 2611,ERR5766174,ILLUMINA,,,///fasta/ERX5474930_ERR5766174_1.fa.gz @@ -59,9 +61,11 @@ An [example samplesheet](../assets/samplesheet.csv) has been provided with the p ### Full database sheet -nf-core/taxprofiler supports multiple databases being profiled in parallel for each tool. These databases, and specific parameters for each, can be specified in a 4 column comma-separated sheet. +nf-core/taxprofiler supports multiple databases being profiled in parallel for each tool. +Databases can be supplied either be in the form of a compressed `.tar.gz` archive of a folder containing all relevant database files or the path to an (uncompressed) directory. +The pipelines takes the locations these of databases as input, and specific parameters for each, via a 4 column comma-separated sheet. -> ⚠️ nf-core/taxprofiler does not provide any databases by default, nor does it currently generate them for you. This must be performed manually by the user. +> ⚠️ nf-core/taxprofiler does not provide any databases by default, nor does it currently generate them for you. This must be performed manually by the user. See below for more information of the expected database files. An example database sheet can look as follows, where 4 tools are being used, and `malt` and `kraken2` will be used against two databases each. @@ -86,6 +90,41 @@ Column specifications are as follows: > 💡 You can also specify the same database directory/file twice (ensuring unique `db_name`s) and specify different parameters for each database to compare the effect of different parameters during profiling. +nf-core/taxprofiler will automatically decompress and extract any compressed archives for you. + +Expected (uncompressed) database files for each tool are as follows: + +- **MALT** output of `malt-build`. A directory containing: + - `ref.idx` + - `taxonomy.idx` + - `taxonomy.map` + - `index0.idx` + - `table0.idx` + - `table0.db` + - `ref.inf` + - `ref.db` + - `taxonomy.tre` +- **Kraken2** output of `kraken2-build` command(s) A directory containing: + - `opts.k2d` + - `hash.k2d` + - `taxo.k2d` +- **Centrifuge** output of `centrifuge-build`. A directory containing: + - `..cf` + - `..cf` + - `..cf` + - `..cf` +- **MetaPhlAn3** generated with `metaphlan --install` or downloaded from links on the [MetaPhlAn3 wiki](https://github.com/biobakery/MetaPhlAn/wiki/MetaPhlAn-3.0#customizing-the-database). A directory containing: + - `mpa_v30_CHOCOPhlAn_201901.pkl` + - `mpa_v30_CHOCOPhlAn_201901.pkl` + - `mpa_v30_CHOCOPhlAn_201901.fasta` + - `mpa_v30_CHOCOPhlAn_201901.3.bt2` + - `mpa_v30_CHOCOPhlAn_201901.4.bt2` + - `mpa_v30_CHOCOPhlAn_201901.1.bt2` + - `mpa_v30_CHOCOPhlAn_201901.2.bt2` + - `mpa_v30_CHOCOPhlAn_201901.rev.1.bt2` + - `mpa_v30_CHOCOPhlAn_201901.rev.2.bt2` + - `mpa_latest` + ## Running the pipeline The typical command for running the pipeline is as follows: @@ -105,6 +144,66 @@ work # Directory containing the nextflow working files # Other nextflow hidden files, eg. history of pipeline runs and old logs. ``` +### Preprocessing Steps + +nf-core/taxprofiler offers four main preprocessing steps + +- Read processing: adapter clipping and pair-merging. +- Complexity filtering: removal of low-sequence complexity reads. +- Host read-removal: removal of reads aligning to reference genome(s) of a host. +- Run merging: concatenation of multiple FASTQ chunks/sequencing runs/libraries of a sample. + +#### Read Processing + +Raw sequencing read processing in the form of adapter clipping and paired-end read merging can be activated via the `--perform_shortread_clipmerge` or `--perform_longread_clip` flags. + +It is highly recommended to run this on raw reads to remove artefacts from sequencing that can cause false positive identification of taxa (e.g. contaminated reference genomes) and/or skews in taxonomic abundance profiles. + +There are currently two options for short-read preprocessing: `fastp` or `adapterremoval`. + +For adapter clipping, you can either rely on tool default adapter sequences, or supply your own adapters (`--shortread_clipmerge_adapter1` and `--shortread_clipmerge_adapter2`) +By default, paired-end merging is not activated and paired-end profiling is performed where supported otherwise pairs will be independently profiled. If paired-end merging is activated you can also specify whether to exclude unmerged reads in the reads sent for profiling (`--shortread_clipmerge_mergepairs` and `--shortread_clipmerge_excludeunmerged`). +You can also turn off clipping and only perform paired-end merging, if requested. This can be useful when processing data downloaded from the ENA, SRA, or DDBJ (--shortread_clipmerge_skipadaptertrim). +Both tools support length filtering of reads and can be tuned with `--shortread_clipmerge_minlength`. Performing length filtering can be useful to remove short (often low sequencing complexity) sequences that result in unspecific classification and therefore slow down runtime during profiling, with minimal gain. + +There is currently one option for long-read Oxford Nanopore processing: `porechop`. + +For both short-read and long-read preprocessing, you can optionally save the resulting processed reads with `--save_preprocessed_reads`. + +#### Complexity Filtering + +Complexity filtering can be activated via the `--perform_shortread_complexityfilter` flag. + +Complexity filtering is primarily a run-time optimisation step. It is not necessary for accurate taxonomic profiling, however it can speed up run-time of each tool by removing reads with low-diversity of nucleotides (e.g. with mono-nucleotide - `AAAAAAAA`, or di-nucleotide repeats `GAGAGAGAGAGAGAG`) that have a low-chance of giving an informatic taxonomic ID as they can be associated with many different taxa. Removing these reads therefore saves computational time and resources. + +There are currently two options for short-read complexity filtering: [`bbduk`](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/bb-tools-user-guide/bbduk-guide/) and [`prinseq++`](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/bb-tools-user-guide/bbduk-guide/). + +The tools offer different algorithms and parameters for removing low complexity reads. We therefore recommend reviewing the pipeline's [parameter documentation](https://nf-co.re/taxprofiler/parameters) and the documentation of both tools (see links above) to decide on optimal methods and parameters for your dataset. + +You can optionally save the FASTQ output of the run merging with the `--save_complexityfiltered_reads`. + +#### Host Removal + +Removal of possible-host reads from FASTQ files prior profiling can be activated with `--perform_shortread_hostremoval` + +Similarly to complexity filtering, host-removal can be useful for runtime optimisation and reduction in misclassified reads. It is not always necessary to report classification of reads from a host when you already know the host of the sample, therefore you can gain a run-time and computational advantage by removing these prior typically resource-heavy profiling with more efficient methods. Furthermore, particularly with human samples, you can reduce the number of false positives during profiling that occur due to host-sequence contamination in reference genomes on public databases. + +nf-core/taxprofiler currently offers host-removal via alignment against a reference genome with Bowtie2, and the use of the unaligned reads for downstream profiling. + +You can supply your reference genome in FASTA format with `--shortread_hostremoval_reference`. You can also optionally supply a directory containing pre-indexed Bowtie2 index files with `--shortread_hostremoval_index`, however nf-core/taxprofiler will generate this for you if necessary. + +> 💡 If you have multiple taxa or sequences you wish to remove (e.g., the host genome and then also PhiX - common quality-control reagent during sequencing) you can simply concatenate the FASTAs of each taxa or sequences into a single reference file. + +#### Run Merging + +For samples that may have been sequenced over multiple runs, or for FASTQ files split into multiple chunks, you can activate the ability to merge across all runs or chunks with `--perform_runmerging`. + +For more information how to set up your input samplesheet, see [Multiple runs of the same sample](#multiple-runs-of-the-same-sample). + +Activating this functionality will concatenate togther the FASTQ files with the same sample name _after_ the optional preprocessing steps and prior profiling. Note that libraries with runs of different pairment types will **not** the different types merged together, and output files will indicate with a `_se` or `_pe` suffix to the sample name accordingly. + +You can optionally save the FASTQ output of the run merging with the `--save_runmerged_reads`. + ### 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: From 457c13e8b74f4fef01f8631bbbdfb9583777760c Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 17 Apr 2022 17:55:23 +0200 Subject: [PATCH 159/306] Add additional tips for host removal --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 528aeef..919569c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -190,7 +190,7 @@ Similarly to complexity filtering, host-removal can be useful for runtime optimi nf-core/taxprofiler currently offers host-removal via alignment against a reference genome with Bowtie2, and the use of the unaligned reads for downstream profiling. -You can supply your reference genome in FASTA format with `--shortread_hostremoval_reference`. You can also optionally supply a directory containing pre-indexed Bowtie2 index files with `--shortread_hostremoval_index`, however nf-core/taxprofiler will generate this for you if necessary. +You can supply your reference genome in FASTA format with `--shortread_hostremoval_reference`. You can also optionally supply a directory containing pre-indexed Bowtie2 index files with `--shortread_hostremoval_index`, however nf-core/taxprofiler will generate this for you if necessary. Pre-supplying the directory of index files can greatly speed up the process, and these can be re-used. > 💡 If you have multiple taxa or sequences you wish to remove (e.g., the host genome and then also PhiX - common quality-control reagent during sequencing) you can simply concatenate the FASTAs of each taxa or sequences into a single reference file. From 2cfcdf2dd2bcc5141b0e7a6474dd27646fa6ee25 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 17 Apr 2022 22:27:04 +0200 Subject: [PATCH 160/306] Add finer controler over for hostremoval saving --- conf/modules.config | 20 ++++++++++++++++---- nextflow.config | 7 +++++-- nextflow_schema.json | 39 ++++++++++++++++++++++++++++++++------- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index ccd1748..55119ff 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -133,21 +133,33 @@ process { } withName: BOWTIE2_BUILD { - ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ path: { "${params.outdir}/bowtie2/build" }, mode: params.publish_dir_mode, - pattern: '*.bt2' + enabled: params.save_hostremoval_index, + pattern: 'bowtie2/*' ] } withName: BOWTIE2_ALIGN { ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [[ + path: { "${params.outdir}/bowtie2/align" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/bowtie2/align" }, + mode: params.publish_dir_mode, + enabled: params.save_hostremoval_mapped, + pattern: '*.bam' + ], publishDir = [ path: { "${params.outdir}/bowtie2/align" }, mode: params.publish_dir_mode, - pattern: '*.{fastq.gz,bam}' - ] + enabled: params.save_hostremoval_unmapped, + pattern: '*.fastq.gz' + ]] } withName: BBMAP_BBDUK { diff --git a/nextflow.config b/nextflow.config index bf3ca92..5616b0e 100644 --- a/nextflow.config +++ b/nextflow.config @@ -82,8 +82,11 @@ params { // Host Removal perform_shortread_hostremoval = false - shortread_hostremoval_reference = null - shortread_hostremoval_index = null + shortread_hostremoval_reference = null + shortread_hostremoval_index = null + save_hostremoval_index = false + save_hostremoval_mapped = false + save_hostremoval_unmapped = false // MALT run_malt = false diff --git a/nextflow_schema.json b/nextflow_schema.json index cf0edab..7890517 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +348,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -364,11 +380,20 @@ }, "shortread_hostremoval_reference": { "type": "string", - "default": null + "default": "None" }, "shortread_hostremoval_index": { "type": "string", - "default": null + "default": "None" + }, + "save_hostremoval_index": { + "type": "boolean" + }, + "save_hostremoval_mapped": { + "type": "boolean" + }, + "save_hostremoval_unmapped": { + "type": "boolean" } } -} +} \ No newline at end of file From b036ed5b7354df252bf819dfdd65771b8ebcfc98 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 17 Apr 2022 22:28:18 +0200 Subject: [PATCH 161/306] Prettier --- nextflow_schema.json | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 7890517..c0d3597 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -348,10 +335,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -396,4 +380,4 @@ "type": "boolean" } } -} \ No newline at end of file +} From 9880baa0b813a1d7721c9270224a37bb064a31fc Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 18 Apr 2022 06:37:23 +0200 Subject: [PATCH 162/306] Get build directory working --- conf/modules.config | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 55119ff..ba3c5c9 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -137,29 +137,31 @@ process { path: { "${params.outdir}/bowtie2/build" }, mode: params.publish_dir_mode, enabled: params.save_hostremoval_index, - pattern: 'bowtie2/*' + pattern: 'bowtie2' ] } withName: BOWTIE2_ALIGN { ext.prefix = { "${meta.id}_${meta.run_accession}" } - publishDir = [[ + publishDir = [ + [ path: { "${params.outdir}/bowtie2/align" }, mode: params.publish_dir_mode, pattern: '*.log' - ], - [ + ], + [ path: { "${params.outdir}/bowtie2/align" }, mode: params.publish_dir_mode, enabled: params.save_hostremoval_mapped, pattern: '*.bam' - ], - publishDir = [ + ], + [ path: { "${params.outdir}/bowtie2/align" }, mode: params.publish_dir_mode, enabled: params.save_hostremoval_unmapped, pattern: '*.fastq.gz' - ]] + ] + ] } withName: BBMAP_BBDUK { From 564474cafeb77ed1e39d127ed4a08667a4522878 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 18 Apr 2022 07:21:12 +0200 Subject: [PATCH 163/306] Adds filtering and warning for non-FASTA supporting tool. Standardised warning messages. --- subworkflows/local/input_check.nf | 2 ++ subworkflows/local/profiling.nf | 8 ++++++++ workflows/taxprofiler.nf | 11 ++++++----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index db46f2b..eb21b9d 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -45,6 +45,7 @@ def create_fastq_channel(LinkedHashMap row) { meta.run_accession = row.run_accession meta.instrument_platform = row.instrument_platform meta.single_end = row.single_end.toBoolean() + meta.is_fasta = false // add path(s) of the fastq file(s) to the meta map def fastq_meta = [] @@ -75,6 +76,7 @@ def create_fasta_channel(LinkedHashMap row) { meta.run_accession = row.run_accession meta.instrument_platform = row.instrument_platform meta.single_end = true + meta.is_fasta = true def array = [] if (!file(row.fasta).exists()) { diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 18de739..f2f7eb7 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -75,6 +75,10 @@ workflow PROFILING { } ch_input_for_centrifuge = ch_input_for_profiling.centrifuge + .filter{ + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge does not accept FASTA files as input. Skipping Centrifuge for sample " + it[0].id + !it[0].is_fasta + } .multiMap { it -> reads: [ it[0] + it[2], it[1] ] @@ -82,6 +86,10 @@ workflow PROFILING { } ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 + .filter{ + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] MetaPhlAn3 does not accept FASTA files as input. Skipping MetaPhlAn3 for sample " + it[0].id + !it[0].is_fasta + } .multiMap { it -> reads: [it[0] + it[2], it[1]] diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 9fe8cc8..e7ff78b 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -19,11 +19,11 @@ 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 samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } -if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] warning: MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." -if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "[nf-core/taxprofiler] error: cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" +if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler]: MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." +if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" -if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } -if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } +if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } +if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } if (params.shortread_hostremoval_index ) { ch_reference_index = file(params.shortread_hostremoval_index ) } else { ch_reference_index = [] } @@ -175,10 +175,11 @@ workflow TAXPROFILER { meta, reads -> [ meta, [ reads ].flatten() ] } - + .mix( INPUT_CHECK.out.fasta ) } else { ch_reads_runmerged = ch_shortreads_hostremoved .mix( ch_longreads_preprocessed ) + .mix( INPUT_CHECK.out.fasta ) } /* From 491c2d894967f76481b566758c40be369b448603 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 18 Apr 2022 07:26:15 +0200 Subject: [PATCH 164/306] Revert "Adds filtering and warning for non-FASTA supporting tool. Standardised warning messages." This reverts commit 564474cafeb77ed1e39d127ed4a08667a4522878. --- subworkflows/local/input_check.nf | 2 -- subworkflows/local/profiling.nf | 8 -------- workflows/taxprofiler.nf | 11 +++++------ 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index eb21b9d..db46f2b 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -45,7 +45,6 @@ def create_fastq_channel(LinkedHashMap row) { meta.run_accession = row.run_accession meta.instrument_platform = row.instrument_platform meta.single_end = row.single_end.toBoolean() - meta.is_fasta = false // add path(s) of the fastq file(s) to the meta map def fastq_meta = [] @@ -76,7 +75,6 @@ def create_fasta_channel(LinkedHashMap row) { meta.run_accession = row.run_accession meta.instrument_platform = row.instrument_platform meta.single_end = true - meta.is_fasta = true def array = [] if (!file(row.fasta).exists()) { diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index f2f7eb7..18de739 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -75,10 +75,6 @@ workflow PROFILING { } ch_input_for_centrifuge = ch_input_for_profiling.centrifuge - .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge does not accept FASTA files as input. Skipping Centrifuge for sample " + it[0].id - !it[0].is_fasta - } .multiMap { it -> reads: [ it[0] + it[2], it[1] ] @@ -86,10 +82,6 @@ workflow PROFILING { } ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 - .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] MetaPhlAn3 does not accept FASTA files as input. Skipping MetaPhlAn3 for sample " + it[0].id - !it[0].is_fasta - } .multiMap { it -> reads: [it[0] + it[2], it[1]] diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index e7ff78b..9fe8cc8 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -19,11 +19,11 @@ 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 samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } -if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler]: MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." -if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" +if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] warning: MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." +if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "[nf-core/taxprofiler] error: cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" -if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } -if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } +if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } +if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "[nf-core/taxprofiler] error: --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } if (params.shortread_hostremoval_index ) { ch_reference_index = file(params.shortread_hostremoval_index ) } else { ch_reference_index = [] } @@ -175,11 +175,10 @@ workflow TAXPROFILER { meta, reads -> [ meta, [ reads ].flatten() ] } - .mix( INPUT_CHECK.out.fasta ) + } else { ch_reads_runmerged = ch_shortreads_hostremoved .mix( ch_longreads_preprocessed ) - .mix( INPUT_CHECK.out.fasta ) } /* From 6307a9cf4342a05fdc6cb2a127527a66d3993155 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 18 Apr 2022 07:28:09 +0200 Subject: [PATCH 165/306] Typos --- subworkflows/local/profiling.nf | 2 +- workflows/taxprofiler.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index f2f7eb7..156259b 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -76,7 +76,7 @@ workflow PROFILING { ch_input_for_centrifuge = ch_input_for_profiling.centrifuge .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge does not accept FASTA files as input. Skipping Centrifuge for sample " + it[0].id + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample " + it[0].id !it[0].is_fasta } .multiMap { diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index e7ff78b..884ee46 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -19,7 +19,7 @@ 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 samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } -if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler]: MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." +if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } From 9f634805e402c99bd906c523fdbba3350f4086e4 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 18 Apr 2022 07:31:44 +0200 Subject: [PATCH 166/306] Update subworkflows/local/profiling.nf --- subworkflows/local/profiling.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 4e0a3a9..659430f 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -87,7 +87,7 @@ workflow PROFILING { ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample " + it[0].id + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] MetaPhlAn3 currently does not accept FASTA files as input. Skipping MetaPhlAn3 for sample " + it[0].id !it[0].is_fasta } .multiMap { From ecaff371aa8047b1fea25f8b42fa7df1fc58e93b Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Mon, 18 Apr 2022 07:35:19 +0200 Subject: [PATCH 167/306] Update input_check.nf --- subworkflows/local/input_check.nf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index db46f2b..eb21b9d 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -45,6 +45,7 @@ def create_fastq_channel(LinkedHashMap row) { meta.run_accession = row.run_accession meta.instrument_platform = row.instrument_platform meta.single_end = row.single_end.toBoolean() + meta.is_fasta = false // add path(s) of the fastq file(s) to the meta map def fastq_meta = [] @@ -75,6 +76,7 @@ def create_fasta_channel(LinkedHashMap row) { meta.run_accession = row.run_accession meta.instrument_platform = row.instrument_platform meta.single_end = true + meta.is_fasta = true def array = [] if (!file(row.fasta).exists()) { From 87777a428959bc89f4f6cfda956c43312d4b6d16 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 19 Apr 2022 12:54:57 +0200 Subject: [PATCH 168/306] Update subworkflows/local/profiling.nf Co-authored-by: Moritz E. Beber --- subworkflows/local/profiling.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index d7532b7..5f9c111 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -106,7 +106,7 @@ workflow PROFILING { // when no pair-merging def meta_new = meta.clone() meta_new['db_name'] = meta.id - meta_new['id'] = rma.name - ( '.' + rma.extension ) + meta_new['id'] = rma.baseName [ meta_new, rma ] } From 1b7ff2265d146577c840399fcb167d032f98c88d Mon Sep 17 00:00:00 2001 From: sofstam Date: Tue, 19 Apr 2022 13:04:58 +0200 Subject: [PATCH 169/306] Update usage.md, reformat test.config and modules.config --- conf/modules.config | 4 ++-- conf/test.config | 8 ++++---- docs/usage.md | 5 +++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index f85df30..d9e9125 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -226,7 +226,7 @@ process { pattern: '*.txt' ] ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } withName: CUSTOM_DUMPSOFTWAREVERSIONS { @@ -252,6 +252,6 @@ process { pattern: '*.tsv' ] ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } } diff --git a/conf/test.config b/conf/test.config index 884ff32..107beb5 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,14 +24,14 @@ params { // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - run_kraken2 = true - run_malt = true - run_metaphlan3 = true - run_centrifuge = true perform_shortread_clipmerge = true perform_longread_clip = false perform_shortread_complexityfilter = true perform_shortread_hostremoval = true shortread_hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' run_kaiju = true + run_kraken2 = true + run_malt = true + run_metaphlan3 = true + run_centrifuge = true } diff --git a/docs/usage.md b/docs/usage.md index 239a55d..2b6937b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -86,6 +86,11 @@ Column specifications are as follows: > 💡 You can also specify the same database directory/file twice (ensuring unique `db_name`s) and specify different parameters for each database to compare the effect of different parameters during profiling. +## Profilers + +- `kaiju` + - The file `nodes.dmp` must be inside the database directory that is specified to `--databases` file + ## Running the pipeline The typical command for running the pipeline is as follows: From 4e1b1269be42b3547678ac1ec2905401b8e47ab0 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Tue, 19 Apr 2022 13:11:43 +0200 Subject: [PATCH 170/306] Apply prettier --- nextflow_schema.json | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 5fe371d..12c0bac 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -348,10 +335,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", From b7d0557ee08ff0aac70fc84f4c36bb6c881ddcc9 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 20 Apr 2022 09:15:45 +0200 Subject: [PATCH 171/306] Update subworkflows/local/profiling.nf Co-authored-by: Moritz E. Beber --- subworkflows/local/profiling.nf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 659430f..1968741 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -76,7 +76,9 @@ workflow PROFILING { ch_input_for_centrifuge = ch_input_for_profiling.centrifuge .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample " + it[0].id + if (it[0].is_fasta) { + WorkflowTaxprofiler.logMessage("Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample ${it[0].id}.", log) + } !it[0].is_fasta } .multiMap { From ac714dd30f758a0f419ad4155b296cdba5e8a80a Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 20 Apr 2022 09:22:10 +0200 Subject: [PATCH 172/306] Apply suggestions from code review Co-authored-by: Moritz E. Beber --- docs/usage.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 919569c..f4f11fb 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -10,7 +10,7 @@ ## Samplesheet inputs -nf-core/profiler can accept as input raw or preprocessed single- or paired-end short-read (e.g. Illumina) FASTQ files, long-read FASTQ files (e.g. Oxford Nanopore), or FASTA sequences (when accepted by a profiler). +nf-core/taxprofiler can accept as input raw or preprocessed single- or paired-end short-read (e.g. Illumina) FASTQ files, long-read FASTQ files (e.g. Oxford Nanopore), or FASTA sequences (available for a subset of profilers). You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 6 columns, and a header row as shown in the examples below. Furthermother, nf-core/taxprofiler also requires a second comma-separated file of 3 columns with a header row as in the examples below. @@ -62,8 +62,8 @@ An [example samplesheet](../assets/samplesheet.csv) has been provided with the p ### Full database sheet nf-core/taxprofiler supports multiple databases being profiled in parallel for each tool. -Databases can be supplied either be in the form of a compressed `.tar.gz` archive of a folder containing all relevant database files or the path to an (uncompressed) directory. -The pipelines takes the locations these of databases as input, and specific parameters for each, via a 4 column comma-separated sheet. +Databases can be supplied either in the form of a compressed `.tar.gz` archive of a directory containing all relevant database files or the path to a directory on the filesystem. +The pipeline takes the locations and specific parameters of these databases as input via a four column comma-separated sheet. > ⚠️ nf-core/taxprofiler does not provide any databases by default, nor does it currently generate them for you. This must be performed manually by the user. See below for more information of the expected database files. @@ -163,7 +163,7 @@ There are currently two options for short-read preprocessing: `fastp` or `adapte For adapter clipping, you can either rely on tool default adapter sequences, or supply your own adapters (`--shortread_clipmerge_adapter1` and `--shortread_clipmerge_adapter2`) By default, paired-end merging is not activated and paired-end profiling is performed where supported otherwise pairs will be independently profiled. If paired-end merging is activated you can also specify whether to exclude unmerged reads in the reads sent for profiling (`--shortread_clipmerge_mergepairs` and `--shortread_clipmerge_excludeunmerged`). -You can also turn off clipping and only perform paired-end merging, if requested. This can be useful when processing data downloaded from the ENA, SRA, or DDBJ (--shortread_clipmerge_skipadaptertrim). +You can also turn off clipping and only perform paired-end merging, if requested. This can be useful when processing data downloaded from the ENA, SRA, or DDBJ (`--shortread_clipmerge_skipadaptertrim`). Both tools support length filtering of reads and can be tuned with `--shortread_clipmerge_minlength`. Performing length filtering can be useful to remove short (often low sequencing complexity) sequences that result in unspecific classification and therefore slow down runtime during profiling, with minimal gain. There is currently one option for long-read Oxford Nanopore processing: `porechop`. @@ -174,7 +174,7 @@ For both short-read and long-read preprocessing, you can optionally save the res Complexity filtering can be activated via the `--perform_shortread_complexityfilter` flag. -Complexity filtering is primarily a run-time optimisation step. It is not necessary for accurate taxonomic profiling, however it can speed up run-time of each tool by removing reads with low-diversity of nucleotides (e.g. with mono-nucleotide - `AAAAAAAA`, or di-nucleotide repeats `GAGAGAGAGAGAGAG`) that have a low-chance of giving an informatic taxonomic ID as they can be associated with many different taxa. Removing these reads therefore saves computational time and resources. +Complexity filtering is primarily a run-time optimisation step. It is not necessary for accurate taxonomic profiling, however it can speed up run-time of each tool by removing reads with low-diversity of nucleotides (e.g. with mono-nucleotide - `AAAAAAAA`, or di-nucleotide repeats `GAGAGAGAGAGAGAG`) that have a low-chance of giving an informative taxonomic ID as they can be associated with many different taxa. Removing these reads therefore saves computational time and resources. There are currently two options for short-read complexity filtering: [`bbduk`](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/bb-tools-user-guide/bbduk-guide/) and [`prinseq++`](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/bb-tools-user-guide/bbduk-guide/). @@ -200,7 +200,7 @@ For samples that may have been sequenced over multiple runs, or for FASTQ files For more information how to set up your input samplesheet, see [Multiple runs of the same sample](#multiple-runs-of-the-same-sample). -Activating this functionality will concatenate togther the FASTQ files with the same sample name _after_ the optional preprocessing steps and prior profiling. Note that libraries with runs of different pairment types will **not** the different types merged together, and output files will indicate with a `_se` or `_pe` suffix to the sample name accordingly. +Activating this functionality will concatenate the FASTQ files with the same sample name _after_ the optional preprocessing steps and _before_ profiling. Note that libraries with runs of different pairing types will **not** be merged and this will be indicated on output files with a `_se` or `_pe` suffix to the sample name accordingly. You can optionally save the FASTQ output of the run merging with the `--save_runmerged_reads`. From 60d232d62ce42f77df45ceaf71a04751960cbf7d Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 20 Apr 2022 09:23:38 +0200 Subject: [PATCH 173/306] Revert "Update subworkflows/local/profiling.nf" This reverts commit b7d0557ee08ff0aac70fc84f4c36bb6c881ddcc9. --- subworkflows/local/profiling.nf | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 1968741..659430f 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -76,9 +76,7 @@ workflow PROFILING { ch_input_for_centrifuge = ch_input_for_profiling.centrifuge .filter{ - if (it[0].is_fasta) { - WorkflowTaxprofiler.logMessage("Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample ${it[0].id}.", log) - } + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample " + it[0].id !it[0].is_fasta } .multiMap { From 9fd94b13892018963ed387e8f425e295af5e4d61 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 20 Apr 2022 09:25:54 +0200 Subject: [PATCH 174/306] Switch to string interpolation --- subworkflows/local/profiling.nf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 659430f..87bea1a 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -76,18 +76,18 @@ workflow PROFILING { ch_input_for_centrifuge = ch_input_for_profiling.centrifuge .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample " + it[0].id - !it[0].is_fasta + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample ${it[0].id}." + !it[0].is_fasta } .multiMap { it -> - reads: [ it[0] + it[2], it[1] ] - db: it[3] + reads: [ it[0] + it[2], it[1] ] + db: it[3] } ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] MetaPhlAn3 currently does not accept FASTA files as input. Skipping MetaPhlAn3 for sample " + it[0].id + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] MetaPhlAn3 currently does not accept FASTA files as input. Skipping MetaPhlAn3 for sample ${it[0].id}." !it[0].is_fasta } .multiMap { From d031322c16aca1c2a6dcd4103c17a097db3f136f Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 20 Apr 2022 13:41:01 +0200 Subject: [PATCH 175/306] Update test.config --- conf/test.config | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conf/test.config b/conf/test.config index 107beb5..0bd09ab 100644 --- a/conf/test.config +++ b/conf/test.config @@ -34,4 +34,10 @@ params { run_malt = true run_metaphlan3 = true run_centrifuge = true + } + +process { + withName: MALT_RUN { + maxForks = 1 + } } From d850333e11db1d1fb4807b15b94c9e8332a71cac Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Wed, 20 Apr 2022 13:42:26 +0200 Subject: [PATCH 176/306] Linting --- conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index 0bd09ab..ecf55bd 100644 --- a/conf/test.config +++ b/conf/test.config @@ -34,7 +34,7 @@ params { run_malt = true run_metaphlan3 = true run_centrifuge = true - } +} process { withName: MALT_RUN { From c00ca1c2b80a9032f66dd068c2f51cb42090a55d Mon Sep 17 00:00:00 2001 From: sofstam Date: Fri, 22 Apr 2022 15:24:10 +0200 Subject: [PATCH 177/306] Add centrifuge kreport in taxprofiler --- conf/modules.config | 10 +++++ modules.json | 11 +++-- .../modules/centrifuge/kreport/main.nf | 33 +++++++++++++++ .../modules/centrifuge/kreport/meta.yml | 41 +++++++++++++++++++ nextflow.config | 1 + nextflow_schema.json | 27 ++++++++++-- subworkflows/local/profiling.nf | 4 +- 7 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 modules/nf-core/modules/centrifuge/kreport/main.nf create mode 100644 modules/nf-core/modules/centrifuge/kreport/meta.yml diff --git a/conf/modules.config b/conf/modules.config index 5b9a815..19cfef7 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -254,6 +254,16 @@ process { ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } + withName: CENTRIFUGE_KREPORT { + ext.args = { "${meta.db_params}" } + ext.prefix = { "${meta.id}" } + publishDir = [ + path: { "${params.outdir}/centrifuge/${meta.db_name}" }, + mode: params.publish_dir_mode, + pattern: '*.{txt}' + ] + } + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ path: { "${params.outdir}/pipeline_info" }, diff --git a/modules.json b/modules.json index d6be2da..6fc4f51 100644 --- a/modules.json +++ b/modules.json @@ -21,6 +21,9 @@ "centrifuge/centrifuge": { "git_sha": "d2726fcf75063960f06b36d2229a4c0966614108" }, + "centrifuge/kreport": { + "git_sha": "be4ae28c3c95b3c4047a7d9fb4cb0ed749631cea" + }, "custom/dumpsoftwareversions": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, @@ -30,6 +33,9 @@ "fastqc": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, + "kaiju/kaiju": { + "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" + }, "kraken2/kraken2": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, @@ -53,10 +59,7 @@ }, "untar": { "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" - }, - "kaiju/kaiju": { - "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/centrifuge/kreport/main.nf b/modules/nf-core/modules/centrifuge/kreport/main.nf new file mode 100644 index 0000000..124cbdb --- /dev/null +++ b/modules/nf-core/modules/centrifuge/kreport/main.nf @@ -0,0 +1,33 @@ +process CENTRIFUGE_KREPORT { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::centrifuge=1.0.4_beta" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/centrifuge:1.0.4_beta--h9a82719_6': + 'quay.io/biocontainers/centrifuge:1.0.4_beta--h9a82719_6' }" + + input: + tuple val(meta), path(results) + path db + + output: + tuple val(meta), path('*.txt') , emit: kreport + 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}" + """ + db_name=`find -L ${db} -name "*.1.cf" -not -name "._*" | sed 's/.1.cf//'` + centrifuge-kreport -x \$db_name ${results} > ${prefix}.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + centrifuge: \$( centrifuge --version | sed -n 1p | sed 's/^.*centrifuge-class version //') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/centrifuge/kreport/meta.yml b/modules/nf-core/modules/centrifuge/kreport/meta.yml new file mode 100644 index 0000000..fbcae24 --- /dev/null +++ b/modules/nf-core/modules/centrifuge/kreport/meta.yml @@ -0,0 +1,41 @@ +name: "centrifuge_kreport" +description: Creates Kraken-style reports from centrifuge out files +keywords: + - metagenomics +tools: + - centrifuge: + description: Centrifuge is a classifier for metagenomic sequences. + homepage: https://ccb.jhu.edu/software/centrifuge/ + documentation: https://ccb.jhu.edu/software/centrifuge/manual.shtml + doi: 10.1101/gr.210641.116 + licence: ["GPL v3"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - results: + type: file + description: File containing the centrifuge classification results + pattern: "*.{txt}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - kreport: + type: file + description: | + File containing kraken-style report from centrifuge + out files. + pattern: "*.{txt}" +authors: + - "@sofstam" + - "@jfy133" diff --git a/nextflow.config b/nextflow.config index 43aaae8..61b4905 100644 --- a/nextflow.config +++ b/nextflow.config @@ -98,6 +98,7 @@ params { // centrifuge run_centrifuge = false + centrifuge_kreport = false centrifuge_save_unaligned = false centrifuge_save_aligned = false centrifuge_sam_format = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 2181cce..f5822db 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +348,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -384,6 +400,9 @@ }, "malt_generatemegansummary": { "type": "boolean" + }, + "centrifuge_kreport": { + "type": "boolean" } } } diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 8d19bfe..1369f4c 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -6,6 +6,7 @@ include { MALT_RUN } from '../../modules/nf-core/modules/malt include { MEGAN_RMA2INFO } from '../../modules/nf-core/modules/megan/rma2info/main' include { KRAKEN2_KRAKEN2 } from '../../modules/nf-core/modules/kraken2/kraken2/main' include { CENTRIFUGE_CENTRIFUGE } from '../../modules/nf-core/modules/centrifuge/centrifuge/main' +include { CENTRIFUGE_KREPORT } from '../../modules/nf-core/modules/centrifuge/kreport/main' include { METAPHLAN3 } from '../../modules/nf-core/modules/metaphlan3/main' include { KAIJU_KAIJU } from '../../modules/nf-core/modules/kaiju/kaiju/main' @@ -142,8 +143,9 @@ workflow PROFILING { if ( params.run_centrifuge ) { CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_unaligned, params.centrifuge_save_aligned, params.centrifuge_sam_format ) + CENTRIFUGE_KREPORT (CENTRIFUGE_CENTRIFUGE.out.results, ch_input_for_centrifuge.db) ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) - ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_CENTRIFUGE.out.report ) + ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_KREPORT.out.kreport ) } if ( params.run_metaphlan3 ) { From cda12749181128267b721e1931018e1c67bbf1a6 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 23 Apr 2022 06:47:59 +0200 Subject: [PATCH 178/306] Moves ordering of modules/subworkflow versions mixing to before custom_dumpsoftwareversions mix, otherwise not used in collectFile --- workflows/taxprofiler.nf | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 3ae1351..9981ca4 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -111,8 +111,8 @@ workflow TAXPROFILER { SUBWORKFLOW: PERFORM PREPROCESSING */ if ( params.perform_shortread_clipmerge ) { - ch_shortreads_preprocessed = SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ).reads + ch_versions = ch_versions.mix( SHORTREAD_PREPROCESSING.out.versions ) } else { ch_shortreads_preprocessed = INPUT_CHECK.out.fastq } @@ -120,7 +120,7 @@ workflow TAXPROFILER { if ( params.perform_longread_clip ) { ch_longreads_preprocessed = LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ).reads .map { it -> [ it[0], [it[1]] ] } - ch_versions = ch_versions.mix(LONGREAD_PREPROCESSING.out.versions.first()) + ch_versions = ch_versions.mix( LONGREAD_PREPROCESSING.out.versions ) } else { ch_longreads_preprocessed = INPUT_CHECK.out.nanopore } @@ -131,6 +131,7 @@ workflow TAXPROFILER { if ( params.perform_shortread_complexityfilter ) { ch_shortreads_filtered = SHORTREAD_COMPLEXITYFILTERING ( ch_shortreads_preprocessed ).reads + ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } else { ch_shortreads_filtered = ch_shortreads_preprocessed } @@ -141,7 +142,7 @@ workflow TAXPROFILER { if ( params.perform_shortread_hostremoval ) { ch_shortreads_hostremoved = SHORTREAD_HOSTREMOVAL ( ch_shortreads_filtered, ch_reference, ch_reference_index ).reads - ch_versions = ch_versions.mix(SHORTREAD_HOSTREMOVAL.out.versions.first()) + ch_versions = ch_versions.mix(SHORTREAD_HOSTREMOVAL.out.versions) } else { ch_shortreads_hostremoved = ch_shortreads_filtered } @@ -177,6 +178,8 @@ workflow TAXPROFILER { } .mix( INPUT_CHECK.out.fasta ) + ch_versions = ch_versions.mix(CAT_FASTQ.out.versions) + } else { ch_reads_runmerged = ch_shortreads_hostremoved .mix( ch_longreads_preprocessed, INPUT_CHECK.out.fasta ) @@ -193,8 +196,10 @@ workflow TAXPROFILER { MODULE: MultiQC */ + ch_versions = ch_versions.mix(MULTIQC.out.versions) + CUSTOM_DUMPSOFTWAREVERSIONS ( - ch_versions.unique().collectFile(name: 'collated_versions.yml') + ch_versions.dump(tag: "software_versions_beforeuniqe").unique().dump(tag: "software_versions").collectFile(name: 'collated_versions.yml') ) @@ -210,26 +215,18 @@ workflow TAXPROFILER { if (params.perform_shortread_clipmerge) { ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( SHORTREAD_PREPROCESSING.out.versions ) } if (params.perform_longread_clip) { ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( LONGREAD_PREPROCESSING.out.versions ) } if (params.perform_shortread_complexityfilter){ ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } if (params.perform_shortread_hostremoval) { ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_HOSTREMOVAL.out.mqc.collect{it[1]}.ifEmpty([])) - ch_versions = ch_versions.mix(SHORTREAD_HOSTREMOVAL.out.versions) - } - - if (params.perform_runmerging){ - ch_versions = ch_versions.mix(CAT_FASTQ.out.versions) } ch_multiqc_files = ch_multiqc_files.mix( PROFILING.out.mqc ) @@ -239,7 +236,6 @@ workflow TAXPROFILER { ch_multiqc_files.collect() ) multiqc_report = MULTIQC.out.report.toList() - ch_versions = ch_versions.mix(MULTIQC.out.versions) } /* From 06b38f3699edd49d1d4a25e02196e012d9221dba Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 23 Apr 2022 08:36:01 +0200 Subject: [PATCH 179/306] Fix crash, add missing versions --- subworkflows/local/db_check.nf | 2 +- subworkflows/local/profiling.nf | 2 +- workflows/taxprofiler.nf | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index a718fa9..f3464d5 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -34,7 +34,7 @@ workflow DB_CHECK { emit: dbs = ch_final_dbs // channel: [ val(meta), [ db ] ] - versions = DATABASE_CHECK.out.versions // channel: [ versions.yml ] + versions = DATABASE_CHECK.out.versions.mix(UNTAR.out.versions.first()) // channel: [ versions.yml ] } def create_db_channels(LinkedHashMap row) { diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 8d19bfe..a5afaaf 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -129,7 +129,7 @@ workflow PROFILING { MEGAN_RMA2INFO (ch_maltrun_for_megan, params.malt_generatemegansummary ) ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( MALT_RUN.out.versions.first() ) + ch_versions = ch_versions.mix( MALT_RUN.out.versions.first(), MEGAN_RMA2INFO.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 9981ca4..a0046b2 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -196,10 +196,8 @@ workflow TAXPROFILER { MODULE: MultiQC */ - ch_versions = ch_versions.mix(MULTIQC.out.versions) - CUSTOM_DUMPSOFTWAREVERSIONS ( - ch_versions.dump(tag: "software_versions_beforeuniqe").unique().dump(tag: "software_versions").collectFile(name: 'collated_versions.yml') + ch_versions.unique().collectFile(name: 'collated_versions.yml') ) @@ -236,6 +234,7 @@ workflow TAXPROFILER { ch_multiqc_files.collect() ) multiqc_report = MULTIQC.out.report.toList() + ch_versions = ch_versions.mix(MULTIQC.out.versions) } /* From e7e24c2d95c43d28c024817b0ebf67b2a9febf75 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Mon, 25 Apr 2022 09:21:29 +0200 Subject: [PATCH 180/306] Prettier and suggestions from review --- conf/modules.config | 2 +- nextflow.config | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 19cfef7..5c5de54 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -256,7 +256,7 @@ process { withName: CENTRIFUGE_KREPORT { ext.args = { "${meta.db_params}" } - ext.prefix = { "${meta.id}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/centrifuge/${meta.db_name}" }, mode: params.publish_dir_mode, diff --git a/nextflow.config b/nextflow.config index 61b4905..43aaae8 100644 --- a/nextflow.config +++ b/nextflow.config @@ -98,7 +98,6 @@ params { // centrifuge run_centrifuge = false - centrifuge_kreport = false centrifuge_save_unaligned = false centrifuge_save_aligned = false centrifuge_sam_format = false From ba2479b95bf0cb07423dfff9c1a75db09ac3abff Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Mon, 25 Apr 2022 09:26:49 +0200 Subject: [PATCH 181/306] Prettier --- modules.json | 2 +- nextflow_schema.json | 24 ++++-------------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/modules.json b/modules.json index 6fc4f51..e9c1310 100644 --- a/modules.json +++ b/modules.json @@ -62,4 +62,4 @@ } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index f5822db..fe66308 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -348,10 +335,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", From d8a5c793e23315557ae0772a1a82db895ac5d536 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Mon, 25 Apr 2022 09:29:43 +0200 Subject: [PATCH 182/306] Remove parameter from nextflow_schema --- nextflow_schema.json | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index fe66308..f53e036 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +348,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -384,9 +400,6 @@ }, "malt_generatemegansummary": { "type": "boolean" - }, - "centrifuge_kreport": { - "type": "boolean" } } -} +} \ No newline at end of file From e3a613de00416b470ca69e6d36fd6d46c5d1c491 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Mon, 25 Apr 2022 09:32:25 +0200 Subject: [PATCH 183/306] Prettier --- nextflow_schema.json | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index f53e036..2181cce 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -348,10 +335,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -402,4 +386,4 @@ "type": "boolean" } } -} \ No newline at end of file +} From d082d67bcb6e55dfcd46996b97a947819e86b3c7 Mon Sep 17 00:00:00 2001 From: sofstam Date: Mon, 25 Apr 2022 17:36:29 +0200 Subject: [PATCH 184/306] Add kaiju2table to taxprofiler --- conf/modules.config | 10 ++++ modules.json | 5 +- .../nf-core/modules/kaiju/kaiju2table/main.nf | 40 +++++++++++++++ .../modules/kaiju/kaiju2table/meta.yml | 50 +++++++++++++++++++ nextflow.config | 1 + nextflow_schema.json | 30 +++++++++-- subworkflows/local/profiling.nf | 6 ++- 7 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 modules/nf-core/modules/kaiju/kaiju2table/main.nf create mode 100644 modules/nf-core/modules/kaiju/kaiju2table/meta.yml diff --git a/conf/modules.config b/conf/modules.config index 5c5de54..a72561d 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -289,4 +289,14 @@ process { ext.args = { "${meta.db_params}" } ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } + + withName: KAIJU_KAIJU2TABLE { + ext.args = { "${meta.db_params}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + publishDir = [ + path: { "${params.outdir}/kaiju/${meta.db_name}" }, + mode: params.publish_dir_mode, + pattern: '*.{txt}' + ] + } } diff --git a/modules.json b/modules.json index e9c1310..9beb010 100644 --- a/modules.json +++ b/modules.json @@ -36,6 +36,9 @@ "kaiju/kaiju": { "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" }, + "kaiju/kaiju2table": { + "git_sha": "538dbac98ba9c8f799536cd5a617195501439457" + }, "kraken2/kraken2": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, @@ -62,4 +65,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/kaiju/kaiju2table/main.nf b/modules/nf-core/modules/kaiju/kaiju2table/main.nf new file mode 100644 index 0000000..00739d1 --- /dev/null +++ b/modules/nf-core/modules/kaiju/kaiju2table/main.nf @@ -0,0 +1,40 @@ +process KAIJU_KAIJU2TABLE { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::kaiju=1.8.2" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/kaiju:1.8.2--h5b5514e_1': + 'quay.io/biocontainers/kaiju:1.8.2--h2e03b76_0' }" + + input: + tuple val(meta), path(results) + path db + val taxon_rank + + output: + tuple val(meta), path('*.txt'), emit: summary + 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}" + """ + dbnodes=`find -L ${db} -name "*nodes.dmp"` + dbname=`find -L ${db} -name "*.fmi" -not -name "._*"` + kaiju2table $args \\ + -t \$dbnodes \\ + -n \$dbname \\ + -r ${taxon_rank} \\ + -o ${prefix}.txt \\ + ${results} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + kaiju: \$(echo \$( kaiju -h 2>&1 | sed -n 1p | sed 's/^.*Kaiju //' )) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/kaiju/kaiju2table/meta.yml b/modules/nf-core/modules/kaiju/kaiju2table/meta.yml new file mode 100644 index 0000000..bc3e85d --- /dev/null +++ b/modules/nf-core/modules/kaiju/kaiju2table/meta.yml @@ -0,0 +1,50 @@ +name: "kaiju_kaiju2table" +description: write your description here +keywords: + - classify + - metagenomics +tools: + - kaiju: + description: Fast and sensitive taxonomic classification for metagenomics + homepage: https://kaiju.binf.ku.dk/ + documentation: https://github.com/bioinformatics-centre/kaiju/blob/master/README.md + tool_dev_url: https://github.com/bioinformatics-centre/kaiju + doi: "10.1038/ncomms11257" + licence: ["GNU GPL v3"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - results: + type: file + description: File containing the kaiju classification results + pattern: "*.{txt}" + - taxon_rank: + type: string + description: | + Taxonomic rank to display in report + pattern: "phylum|class|order|family|genus|species" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - results: + type: file + description: | + Summary table for a given taxonomic rank + pattern: "*.{tsv}" + +authors: + - "@sofstam" + - "@talnor" + - "@jfy133" diff --git a/nextflow.config b/nextflow.config index 43aaae8..909da25 100644 --- a/nextflow.config +++ b/nextflow.config @@ -107,6 +107,7 @@ params { // kaiju run_kaiju = false + kaiju_taxon_name = 'species' } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index 2181cce..9516b6a 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +348,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -384,6 +400,10 @@ }, "malt_generatemegansummary": { "type": "boolean" + }, + "kaiju_taxon_name": { + "type": "string", + "default": "species" } } -} +} \ No newline at end of file diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index fcb2678..1f1d4da 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -9,6 +9,7 @@ include { CENTRIFUGE_CENTRIFUGE } from '../../modules/nf-core/modules/cent include { CENTRIFUGE_KREPORT } from '../../modules/nf-core/modules/centrifuge/kreport/main' include { METAPHLAN3 } from '../../modules/nf-core/modules/metaphlan3/main' include { KAIJU_KAIJU } from '../../modules/nf-core/modules/kaiju/kaiju/main' +include { KAIJU_KAIJU2TABLE } from '../../modules/nf-core/modules/kaiju/kaiju2table/main' workflow PROFILING { take: @@ -155,8 +156,11 @@ workflow PROFILING { } if ( params.run_kaiju ) { - KAIJU_KAIJU ( ch_input_for_kaiju.reads, ch_input_for_kaiju.db ) + KAIJU_KAIJU ( ch_input_for_kaiju.reads, ch_input_for_kaiju.db) + KAIJU_KAIJU2TABLE (KAIJU_KAIJU.out.results, ch_input_for_kaiju.db, params.kaiju_taxon_name) + ch_multiqc_files = ch_multiqc_files.mix( KAIJU_KAIJU2TABLE.out.summary.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( KAIJU_KAIJU.out.versions.first() ) + ch_raw_profiles = ch_raw_profiles.mix( KAIJU_KAIJU2TABLE.out.summary ) } emit: From f4480feb9bb5f76a7e12f84682d512d2932022d2 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Mon, 25 Apr 2022 17:45:26 +0200 Subject: [PATCH 185/306] Prettier --- modules.json | 2 +- nextflow_schema.json | 26 +++++--------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/modules.json b/modules.json index 9beb010..ffcde5d 100644 --- a/modules.json +++ b/modules.json @@ -65,4 +65,4 @@ } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index 9516b6a..93450d2 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -348,10 +335,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -406,4 +390,4 @@ "default": "species" } } -} \ No newline at end of file +} From 7afda265c5c99149739a62dfc0449aad20582184 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Tue, 26 Apr 2022 11:27:18 +0200 Subject: [PATCH 186/306] Enumerating taxon --- nextflow_schema.json | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 93450d2..2fda6e1 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +348,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -387,7 +403,15 @@ }, "kaiju_taxon_name": { "type": "string", - "default": "species" + "default": "species", + "enum": [ + "phylum", + "class", + "order", + "family", + "genus", + "species" + ] } } -} +} \ No newline at end of file From 194b89e66a32862d234ef39cc36aad46c97a2da7 Mon Sep 17 00:00:00 2001 From: Sofia Stamouli Date: Tue, 26 Apr 2022 12:53:39 +0200 Subject: [PATCH 187/306] Prettier --- nextflow_schema.json | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 2fda6e1..83793e8 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -348,10 +335,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -404,14 +388,7 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": [ - "phylum", - "class", - "order", - "family", - "genus", - "species" - ] + "enum": ["phylum", "class", "order", "family", "genus", "species"] } } -} \ No newline at end of file +} From 8126d16dee7e60f80d9cfb159db9199435bccd03 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 29 Apr 2022 21:59:42 +0200 Subject: [PATCH 188/306] Add draft version of DIAMOND --- CITATIONS.md | 4 ++ conf/modules.config | 50 ++++++++++------- conf/test.config | 5 ++ docs/usage.md | 3 ++ modules.json | 5 +- .../nf-core/modules/diamond/blastx/main.nf | 53 +++++++++++++++++++ .../nf-core/modules/diamond/blastx/meta.yml | 52 ++++++++++++++++++ nextflow.config | 4 ++ nextflow_schema.json | 51 +++++++++++++++--- subworkflows/local/profiling.nf | 16 ++++++ 10 files changed, 216 insertions(+), 27 deletions(-) create mode 100644 modules/nf-core/modules/diamond/blastx/main.nf create mode 100644 modules/nf-core/modules/diamond/blastx/meta.yml diff --git a/CITATIONS.md b/CITATIONS.md index 02621d9..fd8c52a 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -52,6 +52,10 @@ > Kim, Daehwan, Li Song, Florian P. Breitwieser, and Steven L. Salzberg. 2016. “Centrifuge: Rapid and Sensitive Classification of Metagenomic Sequences.” Genome Research 26 (12): 1721-29. doi: 10.1101/gr.210641.116. +- [DIAMOND](https://doi.org/10.1038/nmeth.3176) + +> Buchfink, Benjamin, Chao Xie, and Daniel H. Huson. 2015. “Fast and Sensitive Protein Alignment Using DIAMOND.” Nature Methods 12 (1): 59-60. doi: 10.1038/nmeth.3176. + ## Software packaging/containerisation tools - [Anaconda](https://anaconda.com) diff --git a/conf/modules.config b/conf/modules.config index a72561d..9b081b5 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -264,6 +264,36 @@ process { ] } + withName: KAIJU_KAIJU { + publishDir = [ + path: { "${params.outdir}/kaiju/${meta.db_name}" }, + mode: params.publish_dir_mode, + pattern: '*.tsv' + ] + ext.args = { "${meta.db_params}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + } + + withName: KAIJU_KAIJU2TABLE { + ext.args = { "${meta.db_params}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + publishDir = [ + path: { "${params.outdir}/kaiju/${meta.db_name}" }, + mode: params.publish_dir_mode, + pattern: '*.{txt}' + ] + } + + withName: DIAMOND_BLASTX { + ext.args = { "${meta.db_params}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + publishDir = [ + path: { "${params.outdir}/diamond/${meta.db_name}" }, + mode: params.publish_dir_mode, + pattern: '*.{blast,xml,txt,daa,sam,tsv,paf}' + ] + } + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ path: { "${params.outdir}/pipeline_info" }, @@ -279,24 +309,4 @@ process { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - - withName: KAIJU_KAIJU { - publishDir = [ - path: { "${params.outdir}/kaiju/${meta.db_name}" }, - mode: params.publish_dir_mode, - pattern: '*.tsv' - ] - ext.args = { "${meta.db_params}" } - ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } - } - - withName: KAIJU_KAIJU2TABLE { - ext.args = { "${meta.db_params}" } - ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } - publishDir = [ - path: { "${params.outdir}/kaiju/${meta.db_name}" }, - mode: params.publish_dir_mode, - pattern: '*.{txt}' - ] - } } diff --git a/conf/test.config b/conf/test.config index ecf55bd..35d3539 100644 --- a/conf/test.config +++ b/conf/test.config @@ -34,6 +34,11 @@ params { run_malt = true run_metaphlan3 = true run_centrifuge = true + run_diamond = true + // TODO: setting to txt here as does not require taxonomy in database. + // Should consider re-building our test database but with the required + // taxonomy files, but this may make large files (prot2access: 9GB) + diamond_output_format = 'txt' } process { diff --git a/docs/usage.md b/docs/usage.md index 5d3268b..002f4f2 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -128,6 +128,9 @@ Expected (uncompressed) database files for each tool are as follows: - `kaiju_db_*.fmi` - `nodes.dmp` - `names.dmp` +- **DIAMOND** output of `diamond makedb`. Note: requires building with taxonomy files + to generate taxonomic profile. See [DIAMOND documentation](https://github.com/bbuchfink/diamond/wiki/3.-Command-line-options#makedb-options). A file named: + - `.dmnd` ## Running the pipeline diff --git a/modules.json b/modules.json index ffcde5d..7b659c1 100644 --- a/modules.json +++ b/modules.json @@ -27,6 +27,9 @@ "custom/dumpsoftwareversions": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, + "diamond/blastx": { + "git_sha": "42564565b934eeb2449e35ec97ed13ff2a67f1de" + }, "fastp": { "git_sha": "d0a1cbb703a130c19f6796c3fce24fbe7dfce789" }, @@ -65,4 +68,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/diamond/blastx/main.nf b/modules/nf-core/modules/diamond/blastx/main.nf new file mode 100644 index 0000000..6703c1e --- /dev/null +++ b/modules/nf-core/modules/diamond/blastx/main.nf @@ -0,0 +1,53 @@ +process DIAMOND_BLASTX { + tag "$meta.id" + label 'process_medium' + + // Dimaond is limited to v2.0.9 because there is not a + // singularity version higher than this at the current time. + conda (params.enable_conda ? "bioconda::diamond=2.0.9" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/diamond:2.0.9--hdcc8f71_0' : + 'quay.io/biocontainers/diamond:2.0.9--hdcc8f71_0' }" + + input: + tuple val(meta), path(fasta) + path db + val outext + + output: + tuple val(meta), path('*.{blast,xml,txt,daa,sam,tsv,paf}'), emit: output + 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}" + switch ( outext ) { + case "blast": outfmt = 0; break + case "xml": outfmt = 5; break + case "txt": outfmt = 6; break + case "daa": outfmt = 100; break + case "sam": outfmt = 101; break + case "tsv": outfmt = 102; break + case "paf": outfmt = 103; break + } + """ + DB=`find -L ./ -name "*.dmnd" | sed 's/.dmnd//'` + + diamond \\ + blastx \\ + --threads $task.cpus \\ + --db \$DB \\ + --query $fasta \\ + --outfmt ${outfmt} \\ + $args \\ + --out ${prefix}.${outext} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + diamond: \$(diamond --version 2>&1 | tail -n 1 | sed 's/^diamond version //') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/diamond/blastx/meta.yml b/modules/nf-core/modules/diamond/blastx/meta.yml new file mode 100644 index 0000000..5ee2d55 --- /dev/null +++ b/modules/nf-core/modules/diamond/blastx/meta.yml @@ -0,0 +1,52 @@ +name: diamond_blastx +description: Queries a DIAMOND database using blastx mode +keywords: + - fasta + - diamond + - blastx + - DNA sequence +tools: + - diamond: + description: Accelerated BLAST compatible local sequence aligner + homepage: https://github.com/bbuchfink/diamond + documentation: https://github.com/bbuchfink/diamond/wiki + tool_dev_url: https://github.com/bbuchfink/diamond + doi: "doi:10.1038/s41592-021-01101-x" + licence: ["GPL v3.0"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - fasta: + type: file + description: Input fasta file containing query sequences + pattern: "*.{fa,fasta}" + - db: + type: directory + description: Directory containing the nucelotide blast database + pattern: "*" + - outext: + type: string + description: | + Specify the type of output file to be generated. `blast` corresponds to + BLAST pairwise format. `xml` corresponds to BLAST xml format. + `txt` corresponds to to BLAST tabular format. `tsv` corresponds to + taxonomic classification format. + pattern: "blast|xml|txt|daa|sam|tsv|paf" + +output: + - txt: + type: file + description: File containing blastx hits + pattern: "*.{blastx.txt}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@spficklin" + - "@jfy133" diff --git a/nextflow.config b/nextflow.config index 909da25..963a4a5 100644 --- a/nextflow.config +++ b/nextflow.config @@ -108,6 +108,10 @@ params { // kaiju run_kaiju = false kaiju_taxon_name = 'species' + + // diamond + run_diamond = false + diamond_output_format = 'tsv' } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index 83793e8..fc516d6 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +348,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -388,7 +404,30 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": ["phylum", "class", "order", "family", "genus", "species"] + "enum": [ + "phylum", + "class", + "order", + "family", + "genus", + "species" + ] + }, + "run_diamond": { + "type": "boolean" + }, + "diamond_output_format": { + "type": "string", + "default": "tsv", + "enum": [ + "blast", + "xml", + "txt", + "daa", + "sam", + "tsv", + "paf" + ] } } -} +} \ No newline at end of file diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 1f1d4da..9389e19 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -10,6 +10,8 @@ include { CENTRIFUGE_KREPORT } from '../../modules/nf-core/modules/cent include { METAPHLAN3 } from '../../modules/nf-core/modules/metaphlan3/main' include { KAIJU_KAIJU } from '../../modules/nf-core/modules/kaiju/kaiju/main' include { KAIJU_KAIJU2TABLE } from '../../modules/nf-core/modules/kaiju/kaiju2table/main' +include { DIAMOND_BLASTX } from '../../modules/nf-core/modules/diamond/blastx/main' + workflow PROFILING { take: @@ -41,6 +43,7 @@ workflow PROFILING { metaphlan3: it[2]['tool'] == 'metaphlan3' centrifuge: it[2]['tool'] == 'centrifuge' kaiju: it[2]['tool'] == 'kaiju' + diamond: it[2]['tool'] == 'diamond' unknown: true } @@ -109,6 +112,13 @@ workflow PROFILING { db: it[3] } + ch_input_for_diamond = ch_input_for_profiling.diamond + .multiMap { + it -> + reads: [it[0] + it[2], it[1]] + db: it[3] + } + /* RUN PROFILING */ @@ -163,6 +173,12 @@ workflow PROFILING { ch_raw_profiles = ch_raw_profiles.mix( KAIJU_KAIJU2TABLE.out.summary ) } + if ( params.run_diamond ) { + DIAMOND_BLASTX ( ch_input_for_diamond.reads, ch_input_for_diamond.db, params.diamond_output_format ) + ch_versions = ch_versions.mix( DIAMOND_BLASTX.out.versions.first() ) + ch_raw_profiles = ch_raw_profiles.mix( DIAMOND_BLASTX.out.output ) + } + emit: profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom versions = ch_versions // channel: [ versions.yml ] From a4a9b161d80914f7f1964b4a86c58229d4d884b3 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 29 Apr 2022 22:02:44 +0200 Subject: [PATCH 189/306] Lintin --- conf/modules.config | 4 ++-- docs/usage.md | 2 +- modules.json | 2 +- nextflow_schema.json | 45 +++++++------------------------------------- 4 files changed, 11 insertions(+), 42 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 9b081b5..d8fb382 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -274,7 +274,7 @@ process { ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } } - withName: KAIJU_KAIJU2TABLE { + withName: KAIJU_KAIJU2TABLE { ext.args = { "${meta.db_params}" } ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ @@ -284,7 +284,7 @@ process { ] } - withName: DIAMOND_BLASTX { + withName: DIAMOND_BLASTX { ext.args = { "${meta.db_params}" } ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ diff --git a/docs/usage.md b/docs/usage.md index 002f4f2..cee2bb6 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -129,7 +129,7 @@ Expected (uncompressed) database files for each tool are as follows: - `nodes.dmp` - `names.dmp` - **DIAMOND** output of `diamond makedb`. Note: requires building with taxonomy files - to generate taxonomic profile. See [DIAMOND documentation](https://github.com/bbuchfink/diamond/wiki/3.-Command-line-options#makedb-options). A file named: + to generate taxonomic profile. See [DIAMOND documentation](https://github.com/bbuchfink/diamond/wiki/3.-Command-line-options#makedb-options). A file named: - `.dmnd` ## Running the pipeline diff --git a/modules.json b/modules.json index 7b659c1..a65926c 100644 --- a/modules.json +++ b/modules.json @@ -68,4 +68,4 @@ } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index fc516d6..f429d1b 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -348,10 +335,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -404,14 +388,7 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": [ - "phylum", - "class", - "order", - "family", - "genus", - "species" - ] + "enum": ["phylum", "class", "order", "family", "genus", "species"] }, "run_diamond": { "type": "boolean" @@ -419,15 +396,7 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": [ - "blast", - "xml", - "txt", - "daa", - "sam", - "tsv", - "paf" - ] + "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] } } -} \ No newline at end of file +} From 0630fce3b5ddb4db1b1932b2405e11ba9bd321e2 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 30 Apr 2022 08:11:40 +0200 Subject: [PATCH 190/306] Tweak based on official DIAMOND test-data --- conf/test.config | 4 ---- nextflow.config | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/conf/test.config b/conf/test.config index 35d3539..a2464b2 100644 --- a/conf/test.config +++ b/conf/test.config @@ -35,10 +35,6 @@ params { run_metaphlan3 = true run_centrifuge = true run_diamond = true - // TODO: setting to txt here as does not require taxonomy in database. - // Should consider re-building our test database but with the required - // taxonomy files, but this may make large files (prot2access: 9GB) - diamond_output_format = 'txt' } process { diff --git a/nextflow.config b/nextflow.config index 963a4a5..5644786 100644 --- a/nextflow.config +++ b/nextflow.config @@ -111,7 +111,7 @@ params { // diamond run_diamond = false - diamond_output_format = 'tsv' + diamond_output_format = 'txt' } // Load base.config by default for all pipelines From 031cb45934cc3e66909d6cdd7b38527508353250 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 1 May 2022 07:18:14 +0200 Subject: [PATCH 191/306] Add initial longread QC filtering - requires filtlong module update --- .github/workflows/ci.yml | 18 +++---- CITATIONS.md | 2 + conf/modules.config | 48 ++++++++++++------ conf/test.config | 4 +- docs/usage.md | 10 ++-- modules.json | 3 ++ modules/nf-core/modules/filtlong/main.nf | 36 +++++++++++++ modules/nf-core/modules/filtlong/meta.yml | 50 +++++++++++++++++++ nextflow.config | 28 +++++++---- nextflow_schema.json | 36 +++++++++---- subworkflows/local/longread_preprocessing.nf | 45 +++++++++++++---- .../local/shortread_adapterremoval.nf | 4 +- subworkflows/local/shortread_fastp.nf | 4 +- subworkflows/local/shortread_preprocessing.nf | 4 +- workflows/taxprofiler.nf | 15 +++--- 15 files changed, 233 insertions(+), 74 deletions(-) create mode 100644 modules/nf-core/modules/filtlong/main.nf create mode 100644 modules/nf-core/modules/filtlong/meta.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1ece72..cb531b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,18 +29,18 @@ jobs: - NXF_VER: "" NXF_EDGE: "1" parameters: - - "--perform_longread_clip false" - - "--perform_shortread_clipmerge false" - - "--shortread_clipmerge_tool fastp" - - "--shortread_clipmerge_tool fastp --shortread_clipmerge_mergepairs --shortread_clipmerge_excludeunmerged" - - "--shortread_clipmerge_tool fastp --shortread_clipmerge_mergepairs" - - "--shortread_clipmerge_tool adapterremoval" - - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs --shortread_clipmerge_excludeunmerged" - - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs" + - "--perform_longread_qc false" + - "--perform_shortread_qc false" + - "--shortread_qc_tool fastp" + - "--shortread_qc_tool fastp --shortread_qc_mergepairs --shortread_qc_excludeunmerged" + - "--shortread_qc_tool fastp --shortread_qc_mergepairs" + - "--shortread_qc_tool adapterremoval" + - "--shortread_qc_tool adapterremoval --shortread_qc_mergepairs --shortread_qc_excludeunmerged" + - "--shortread_qc_tool adapterremoval --shortread_qc_mergepairs" - "--shortread_complexityfilter_tool bbduk" - "--shortread_complexityfilter_tool prinseq" - "--perform_runmerging" - - "--perform_runmerging --shortread_clipmerge_mergepairs" + - "--perform_runmerging --shortread_qc_mergepairs" - "--shortread_complexityfilter false --perform_shortread_hostremoval" steps: diff --git a/CITATIONS.md b/CITATIONS.md index fd8c52a..8044658 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -56,6 +56,8 @@ > Buchfink, Benjamin, Chao Xie, and Daniel H. Huson. 2015. “Fast and Sensitive Protein Alignment Using DIAMOND.” Nature Methods 12 (1): 59-60. doi: 10.1038/nmeth.3176. +- [FILTLONG](https://github.com/rrwick/Filtlong) + ## Software packaging/containerisation tools - [Anaconda](https://anaconda.com) diff --git a/conf/modules.config b/conf/modules.config index d8fb382..8481e7b 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -51,10 +51,10 @@ process { withName: FASTP_SINGLE { ext.args = [ // trimming options - params.shortread_clipmerge_skipadaptertrim ? "--disable_adapter_trimming" : "", - params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", + params.shortread_qc_skipadaptertrim ? "--disable_adapter_trimming" : "", + params.shortread_qc_adapter1 ? "--adapter_sequence ${params.shortread_qc_adapter1}" : "", // filtering options - "--length_required ${params.shortread_clipmerge_minlength}" + "--length_required ${params.shortread_qc_minlength}" ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -68,13 +68,13 @@ process { withName: FASTP_PAIRED { ext.args = [ // collapsing options - option to retain singletons - params.shortread_clipmerge_excludeunmerged ? '' : "--include_unmerged", + params.shortread_qc_excludeunmerged ? '' : "--include_unmerged", // trimming options - params.shortread_clipmerge_skipadaptertrim ? "--disable_adapter_trimming" : "", - params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", - params.shortread_clipmerge_adapter2 ? "--adapter_sequence_r2 ${params.shortread_clipmerge_adapter2}" : "--detect_adapter_for_pe", + params.shortread_qc_skipadaptertrim ? "--disable_adapter_trimming" : "", + params.shortread_qc_adapter1 ? "--adapter_sequence ${params.shortread_qc_adapter1}" : "", + params.shortread_qc_adapter2 ? "--adapter_sequence_r2 ${params.shortread_qc_adapter2}" : "--detect_adapter_for_pe", // filtering options - "--length_required ${params.shortread_clipmerge_minlength}" + "--length_required ${params.shortread_qc_minlength}" ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -88,10 +88,10 @@ process { withName: ADAPTERREMOVAL_SINGLE { ext.args = [ // trimming options - params.shortread_clipmerge_skipadaptertrim ? "--adapter1 '' --adapter2 ''" : "", - params.shortread_clipmerge_adapter1 ? "--adapter1 ${params.shortread_clipmerge_adapter1}" : "", + params.shortread_qc_skipadaptertrim ? "--adapter1 '' --adapter2 ''" : "", + params.shortread_qc_adapter1 ? "--adapter1 ${params.shortread_qc_adapter1}" : "", // filtering options - "--minlength ${params.shortread_clipmerge_minlength}" + "--minlength ${params.shortread_qc_minlength}" ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -105,13 +105,13 @@ process { withName: ADAPTERREMOVAL_PAIRED { ext.args = [ // collapsing options - params.shortread_clipmerge_mergepairs ? "--collapse" : "", + params.shortread_qc_mergepairs ? "--collapse" : "", // trimming options - params.shortread_clipmerge_skipadaptertrim ? "--adapter1 '' --adapter2 ''" : "", - params.shortread_clipmerge_adapter1 ? "--adapter1 ${params.shortread_clipmerge_adapter1}" : "", - params.shortread_clipmerge_adapter2 ? "--adapter2 ${params.shortread_clipmerge_adapter2}" : "", + params.shortread_qc_skipadaptertrim ? "--adapter1 '' --adapter2 ''" : "", + params.shortread_qc_adapter1 ? "--adapter1 ${params.shortread_qc_adapter1}" : "", + params.shortread_qc_adapter2 ? "--adapter2 ${params.shortread_qc_adapter2}" : "", // filtering options - "--minlength ${params.shortread_clipmerge_minlength}" + "--minlength ${params.shortread_qc_minlength}" ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -132,6 +132,22 @@ process { ] } + withName: FILTLONG { + ext.args = [ + "--min_length ${params.longread_qc_minlength}", + "--keep_percent ${params.longread_qc_keepbppercent}", + "--target_bases ${params.longread_qc_targetnbases}" + ] + .join(' ').trim() + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/porechop" }, + mode: params.publish_dir_mode, + pattern: '*.fastq.gz', + enabled: params.save_preprocessed_reads + ] + } + withName: BOWTIE2_BUILD { publishDir = [ path: { "${params.outdir}/bowtie2/build" }, diff --git a/conf/test.config b/conf/test.config index a2464b2..ac7b0d3 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,8 +24,8 @@ params { // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - perform_shortread_clipmerge = true - perform_longread_clip = false + perform_shortread_qc = true + perform_longread_qc = false perform_shortread_complexityfilter = true perform_shortread_hostremoval = true shortread_hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' diff --git a/docs/usage.md b/docs/usage.md index cee2bb6..34af0eb 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -162,16 +162,16 @@ nf-core/taxprofiler offers four main preprocessing steps #### Read Processing -Raw sequencing read processing in the form of adapter clipping and paired-end read merging can be activated via the `--perform_shortread_clipmerge` or `--perform_longread_clip` flags. +Raw sequencing read processing in the form of adapter clipping and paired-end read merging can be activated via the `--perform_shortread_qc` or `--perform_longread_qc` flags. It is highly recommended to run this on raw reads to remove artefacts from sequencing that can cause false positive identification of taxa (e.g. contaminated reference genomes) and/or skews in taxonomic abundance profiles. There are currently two options for short-read preprocessing: `fastp` or `adapterremoval`. -For adapter clipping, you can either rely on tool default adapter sequences, or supply your own adapters (`--shortread_clipmerge_adapter1` and `--shortread_clipmerge_adapter2`) -By default, paired-end merging is not activated and paired-end profiling is performed where supported otherwise pairs will be independently profiled. If paired-end merging is activated you can also specify whether to exclude unmerged reads in the reads sent for profiling (`--shortread_clipmerge_mergepairs` and `--shortread_clipmerge_excludeunmerged`). -You can also turn off clipping and only perform paired-end merging, if requested. This can be useful when processing data downloaded from the ENA, SRA, or DDBJ (`--shortread_clipmerge_skipadaptertrim`). -Both tools support length filtering of reads and can be tuned with `--shortread_clipmerge_minlength`. Performing length filtering can be useful to remove short (often low sequencing complexity) sequences that result in unspecific classification and therefore slow down runtime during profiling, with minimal gain. +For adapter clipping, you can either rely on tool default adapter sequences, or supply your own adapters (`--shortread_qc_adapter1` and `--shortread_qc_adapter2`) +By default, paired-end merging is not activated and paired-end profiling is performed where supported otherwise pairs will be independently profiled. If paired-end merging is activated you can also specify whether to exclude unmerged reads in the reads sent for profiling (`--shortread_qc_mergepairs` and `--shortread_qc_excludeunmerged`). +You can also turn off clipping and only perform paired-end merging, if requested. This can be useful when processing data downloaded from the ENA, SRA, or DDBJ (`--shortread_qc_skipadaptertrim`). +Both tools support length filtering of reads and can be tuned with `--shortread_qc_minlength`. Performing length filtering can be useful to remove short (often low sequencing complexity) sequences that result in unspecific classification and therefore slow down runtime during profiling, with minimal gain. There is currently one option for long-read Oxford Nanopore processing: `porechop`. diff --git a/modules.json b/modules.json index a65926c..21c6a89 100644 --- a/modules.json +++ b/modules.json @@ -36,6 +36,9 @@ "fastqc": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, + "filtlong": { + "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + }, "kaiju/kaiju": { "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" }, diff --git a/modules/nf-core/modules/filtlong/main.nf b/modules/nf-core/modules/filtlong/main.nf new file mode 100644 index 0000000..0e6fdd5 --- /dev/null +++ b/modules/nf-core/modules/filtlong/main.nf @@ -0,0 +1,36 @@ +process FILTLONG { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::filtlong=0.2.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/filtlong:0.2.1--h9a82719_0' : + 'quay.io/biocontainers/filtlong:0.2.1--h9a82719_0' }" + + input: + tuple val(meta), path(shortreads), path(longreads) + + output: + tuple val(meta), path("${meta.id}_lr_filtlong.fastq.gz"), emit: reads + 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}" + def short_reads = meta.single_end ? "-1 $shortreads" : "-1 ${shortreads[0]} -2 ${shortreads[1]}" + """ + filtlong \\ + $short_reads \\ + $args \\ + $longreads \\ + | gzip -n > ${prefix}_lr_filtlong.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + filtlong: \$( filtlong --version | sed -e "s/Filtlong v//g" ) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/filtlong/meta.yml b/modules/nf-core/modules/filtlong/meta.yml new file mode 100644 index 0000000..b3626e6 --- /dev/null +++ b/modules/nf-core/modules/filtlong/meta.yml @@ -0,0 +1,50 @@ +name: filtlong +description: Filtlong filters long reads based on quality measures or short read data. +keywords: + - nanopore + - quality control + - QC + - filtering + - long reads + - short reads +tools: + - filtlong: + description: Filtlong is a tool for filtering long reads. It can take a set of long reads and produce a smaller, better subset. It uses both read length (longer is better) and read identity (higher is better) when choosing which reads pass the filter. + homepage: https://anaconda.org/bioconda/filtlong + documentation: None + tool_dev_url: https://github.com/rrwick/Filtlong + doi: "" + licence: ["GPL v3"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - shortreads: + type: file + description: fastq file + pattern: "*.{fq,fastq,fq.gz,fastq.gz}" + - longreads: + type: file + description: fastq file + pattern: "*.{fq,fastq,fq.gz,fastq.gz}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - reads: + type: file + description: Filtered (compressed) fastq file + pattern: "*.fastq.gz" + +authors: + - "@d4straub" diff --git a/nextflow.config b/nextflow.config index 5644786..145e53b 100644 --- a/nextflow.config +++ b/nextflow.config @@ -55,16 +55,23 @@ params { databases = null // FASTQ preprocessing - perform_shortread_clipmerge = false - shortread_clipmerge_tool = 'fastp' - shortread_clipmerge_skipadaptertrim = false - shortread_clipmerge_mergepairs = false - shortread_clipmerge_excludeunmerged = false - shortread_clipmerge_adapter1 = null - shortread_clipmerge_adapter2 = null - shortread_clipmerge_minlength = 15 - perform_longread_clip = false - save_preprocessed_reads = false + perform_shortread_qc = false + shortread_qc_tool = 'fastp' + shortread_qc_skipadaptertrim = false + shortread_qc_mergepairs = false + shortread_qc_excludeunmerged = false + shortread_qc_adapter1 = null + shortread_qc_adapter2 = null + shortread_qc_minlength = 15 + + perform_longread_qc = false + longread_qc_run_clip = false + longread_qc_run_filter = false + longread_qc_minlength = 1000 + longread_qc_keepbppercent = 90 + longread_qc_targetnbases = 500000000 + + save_preprocessed_reads = false // Complexity filtering perform_shortread_complexityfilter = false @@ -185,6 +192,7 @@ profiles { } // Load igenomes.config if required + if (!params.igenomes_ignore) { includeConfig 'conf/igenomes.config' } else { diff --git a/nextflow_schema.json b/nextflow_schema.json index f429d1b..1a590bb 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -262,7 +262,7 @@ "type": "string", "default": "None" }, - "shortread_clipmerge_excludeunmerged": { + "shortread_qc_excludeunmerged": { "type": "boolean" }, "run_malt": { @@ -291,26 +291,26 @@ "type": "boolean", "description": "Enable MetaPhlAn for taxonomic profiling" }, - "shortread_clipmerge_tool": { + "shortread_qc_tool": { "type": "string", "default": "fastp", "enum": ["fastp", "adapterremoval"] }, - "shortread_clipmerge_skipadaptertrim": { + "shortread_qc_skipadaptertrim": { "type": "boolean" }, - "shortread_clipmerge_mergepairs": { + "shortread_qc_mergepairs": { "type": "boolean" }, - "shortread_clipmerge_adapter1": { + "shortread_qc_adapter1": { "type": "string", "default": "None" }, - "shortread_clipmerge_adapter2": { + "shortread_qc_adapter2": { "type": "string", "default": "None" }, - "shortread_clipmerge_minlength": { + "shortread_qc_minlength": { "type": "integer", "default": 15 }, @@ -347,10 +347,10 @@ "save_runmerged_reads": { "type": "boolean" }, - "perform_shortread_clipmerge": { + "perform_shortread_qc": { "type": "boolean" }, - "perform_longread_clip": { + "perform_longread_qc": { "type": "boolean" }, "perform_shortread_complexityfilter": { @@ -397,6 +397,24 @@ "type": "string", "default": "tsv", "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] + }, + "longread_qc_run_clip": { + "type": "boolean" + }, + "longread_qc_run_filter": { + "type": "boolean" + }, + "longread_qc_minlength": { + "type": "integer", + "default": 1000 + }, + "longread_qc_keepbppercent": { + "type": "integer", + "default": 90 + }, + "longread_qc_targetnbases": { + "type": "integer", + "default": 500000000 } } } diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 2fa5f3b..5ae5417 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -4,6 +4,7 @@ include { FASTQC as FASTQC_PROCESSED } from '../../modules/nf-core/modules/fastqc/main' include { PORECHOP } from '../../modules/nf-core/modules/porechop/main' +include { FILTLONG } from '../../modules/nf-core/modules/filtlong/main' workflow LONGREAD_PREPROCESSING { take: @@ -13,21 +14,43 @@ workflow LONGREAD_PREPROCESSING { ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() - PORECHOP ( reads ) + if ( params.longread_qc_run_clip && !params.longread_qc_run_filter ) { + PORECHOP ( reads ) - ch_processed_reads = PORECHOP.out.reads - .map { - meta, reads -> - def meta_new = meta.clone() - meta_new['single_end'] = 1 - [ meta_new, reads ] - } + ch_processed_reads = PORECHOP.out.reads + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] - FASTQC_PROCESSED ( PORECHOP.out.reads ) - ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) + ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) + } + } else if ( !params.longread_qc_run_clip && params.longread_qc_run_filter ) { + + ch_processed_reads = FILTLONG ( reads.map{ meta, reads -> [meta, [], reads ]} ) + ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) + + } else { + PORECHOP ( reads ) + ch_clipped_reads = PORECHOP.out.reads + .map { + meta, reads -> + def meta_new = meta.clone() + meta_new['single_end'] = 1 + [ meta_new, reads ] + } + + ch_processed_reads = FILTLONG ( ch_clipped_reads.map{ meta, reads -> [meta, [], reads ]} ).reads + + ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) + ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) + + } + + FASTQC_PROCESSED ( ch_processed_reads.dump(tag: "filtlong") ) ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) - emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] versions = ch_versions // channel: [ versions.yml ] diff --git a/subworkflows/local/shortread_adapterremoval.nf b/subworkflows/local/shortread_adapterremoval.nf index b573be9..e491423 100644 --- a/subworkflows/local/shortread_adapterremoval.nf +++ b/subworkflows/local/shortread_adapterremoval.nf @@ -29,7 +29,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { * has to be exported in a separate channel and we must manually recombine when necessary. */ - if ( params.shortread_clipmerge_mergepairs && !params.shortread_clipmerge_excludeunmerged ) { + if ( params.shortread_qc_mergepairs && !params.shortread_qc_excludeunmerged ) { ch_concat_fastq = Channel.empty() .mix( @@ -54,7 +54,7 @@ workflow SHORTREAD_ADAPTERREMOVAL { ch_adapterremoval_reads_prepped = CAT_FASTQ.out.reads .mix(ADAPTERREMOVAL_SINGLE.out.singles_truncated) - } else if ( params.shortread_clipmerge_mergepairs && params.shortread_clipmerge_excludeunmerged ) { + } else if ( params.shortread_qc_mergepairs && params.shortread_qc_excludeunmerged ) { ch_concat_fastq = Channel.empty() .mix( diff --git a/subworkflows/local/shortread_fastp.nf b/subworkflows/local/shortread_fastp.nf index 6fed2ae..05e0f3d 100644 --- a/subworkflows/local/shortread_fastp.nf +++ b/subworkflows/local/shortread_fastp.nf @@ -21,9 +21,9 @@ workflow SHORTREAD_FASTP { FASTP_SINGLE ( ch_input_for_fastp.single, false, false ) // Last parameter here turns on merging of PE data - FASTP_PAIRED ( ch_input_for_fastp.paired, false, params.shortread_clipmerge_mergepairs ) + FASTP_PAIRED ( ch_input_for_fastp.paired, false, params.shortread_qc_mergepairs ) - if ( params.shortread_clipmerge_mergepairs ) { + if ( params.shortread_qc_mergepairs ) { ch_fastp_reads_prepped_pe = FASTP_PAIRED.out.reads_merged .map { meta, reads -> diff --git a/subworkflows/local/shortread_preprocessing.nf b/subworkflows/local/shortread_preprocessing.nf index b0ac25e..977a317 100644 --- a/subworkflows/local/shortread_preprocessing.nf +++ b/subworkflows/local/shortread_preprocessing.nf @@ -15,11 +15,11 @@ workflow SHORTREAD_PREPROCESSING { ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() - if ( params.shortread_clipmerge_tool == "fastp" ) { + if ( params.shortread_qc_tool == "fastp" ) { ch_processed_reads = SHORTREAD_FASTP ( reads ).reads ch_versions = ch_versions.mix( SHORTREAD_FASTP.out.versions ) ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_FASTP.out.mqc ) - } else if ( params.shortread_clipmerge_tool == "adapterremoval" ) { + } else if ( params.shortread_qc_tool == "adapterremoval" ) { ch_processed_reads = SHORTREAD_ADAPTERREMOVAL ( reads ).reads ch_versions = ch_versions.mix( SHORTREAD_ADAPTERREMOVAL.out.versions ) ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_ADAPTERREMOVAL.out.mqc ) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index a0046b2..2279fd5 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -19,8 +19,11 @@ 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 samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } -if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." -if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" + +if (params.shortread_qc_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] MALT does not accept uncollapsed paired-short reads. Pairs will be profiled as separate files." +if (params.shortread_qc_excludeunmerged && !params.shortread_qc_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_qc_mergepairs" + +if ( (params.longread_qc_run_clip || params.longread_qc_run_filter) & !params.perform_longread_qc ) exit 1, "ERROR: [nf-core/taxprofiler] --longread_qc_run_clip or --longread_qc_run_filter requested but quality-control not turned on. Please specify --perform_long_qc" if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } @@ -110,14 +113,14 @@ workflow TAXPROFILER { /* SUBWORKFLOW: PERFORM PREPROCESSING */ - if ( params.perform_shortread_clipmerge ) { + if ( params.perform_shortread_qc ) { ch_shortreads_preprocessed = SHORTREAD_PREPROCESSING ( INPUT_CHECK.out.fastq ).reads ch_versions = ch_versions.mix( SHORTREAD_PREPROCESSING.out.versions ) } else { ch_shortreads_preprocessed = INPUT_CHECK.out.fastq } - if ( params.perform_longread_clip ) { + if ( params.perform_longread_qc ) { ch_longreads_preprocessed = LONGREAD_PREPROCESSING ( INPUT_CHECK.out.nanopore ).reads .map { it -> [ it[0], [it[1]] ] } ch_versions = ch_versions.mix( LONGREAD_PREPROCESSING.out.versions ) @@ -211,11 +214,11 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) - if (params.perform_shortread_clipmerge) { + if (params.perform_shortread_qc) { ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) } - if (params.perform_longread_clip) { + if (params.perform_longread_qc) { ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) } From 409470642141460daace239396d7fdc95b0580b5 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 1 May 2022 07:24:58 +0200 Subject: [PATCH 192/306] Only create profiler input channels when profiler activate --- subworkflows/local/profiling.nf | 142 +++++++++++++++++--------------- 1 file changed, 74 insertions(+), 68 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 9389e19..d8e9c84 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -48,7 +48,7 @@ workflow PROFILING { } /* - PREPARE PROFILER INPUT CHANNELS + PREPARE PROFILER INPUT CHANNELS & RUN PROFILING */ // Each tool as a slightly different input structure and generally separate @@ -56,74 +56,26 @@ workflow PROFILING { // for each tool and make liberal use of multiMap to keep reads/databases // channel element order in sync with each other - // MALT: We groupTuple to have all samples in one channel for MALT as database - // loading takes a long time, so we only want to run it once per database - // TODO document somewhere we only accept illumina short reads for MALT? - ch_input_for_malt = ch_input_for_profiling.malt - .filter { it[0]['instrument_platform'] == 'ILLUMINA' } - .map { - it -> - def temp_meta = [ id: it[2]['db_name']] + it[2] - def db = it[3] - [ temp_meta, it[1], db ] - } - .groupTuple(by: [0,2]) - .multiMap { - it -> - reads: [ it[0], it[1].flatten() ] - db: it[2] - } - - // All subsequent tools can easily run on a per-sample basis - - ch_input_for_kraken2 = ch_input_for_profiling.kraken2 - .multiMap { - it -> - reads: [ it[0] + it[2], it[1] ] - db: it[3] - } - - ch_input_for_centrifuge = ch_input_for_profiling.centrifuge - .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample ${it[0].id}." - !it[0].is_fasta - } - .multiMap { - it -> - reads: [ it[0] + it[2], it[1] ] - db: it[3] - } - - ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 - .filter{ - if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] MetaPhlAn3 currently does not accept FASTA files as input. Skipping MetaPhlAn3 for sample ${it[0].id}." - !it[0].is_fasta - } - .multiMap { - it -> - reads: [it[0] + it[2], it[1]] - db: it[3] - } - - ch_input_for_kaiju = ch_input_for_profiling.kaiju - .multiMap { - it -> - reads: [it[0] + it[2], it[1]] - db: it[3] - } - - ch_input_for_diamond = ch_input_for_profiling.diamond - .multiMap { - it -> - reads: [it[0] + it[2], it[1]] - db: it[3] - } - - /* - RUN PROFILING - */ - if ( params.run_malt ) { + + + // MALT: We groupTuple to have all samples in one channel for MALT as database + // loading takes a long time, so we only want to run it once per database + // TODO document somewhere we only accept illumina short reads for MALT? + ch_input_for_malt = ch_input_for_profiling.malt + .filter { it[0]['instrument_platform'] == 'ILLUMINA' } + .map { + it -> + def temp_meta = [ id: it[2]['db_name']] + it[2] + def db = it[3] + [ temp_meta, it[1], db ] + } + .groupTuple(by: [0,2]) + .multiMap { + it -> + reads: [ it[0], it[1].flatten() ] + db: it[2] + MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) ch_maltrun_for_megan = MALT_RUN.out.rma6 @@ -143,40 +95,94 @@ workflow PROFILING { ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( MALT_RUN.out.versions.first(), MEGAN_RMA2INFO.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) + } if ( params.run_kraken2 ) { + + ch_input_for_kraken2 = ch_input_for_profiling.kraken2 + .multiMap { + it -> + reads: [ it[0] + it[2], it[1] ] + db: it[3] + } + KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.txt ) + } if ( params.run_centrifuge ) { + + ch_input_for_centrifuge = ch_input_for_profiling.centrifuge + .filter{ + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] Centrifuge currently does not accept FASTA files as input. Skipping Centrifuge for sample ${it[0].id}." + !it[0].is_fasta + } + .multiMap { + it -> + reads: [ it[0] + it[2], it[1] ] + db: it[3] + } + CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_unaligned, params.centrifuge_save_aligned, params.centrifuge_sam_format ) CENTRIFUGE_KREPORT (CENTRIFUGE_CENTRIFUGE.out.results, ch_input_for_centrifuge.db) ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_KREPORT.out.kreport ) + } if ( params.run_metaphlan3 ) { + + ch_input_for_metaphlan3 = ch_input_for_profiling.metaphlan3 + .filter{ + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] MetaPhlAn3 currently does not accept FASTA files as input. Skipping MetaPhlAn3 for sample ${it[0].id}." + !it[0].is_fasta + } + .multiMap { + it -> + reads: [it[0] + it[2], it[1]] + db: it[3] + } + METAPHLAN3 ( ch_input_for_metaphlan3.reads, ch_input_for_metaphlan3.db ) ch_versions = ch_versions.mix( METAPHLAN3.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( METAPHLAN3.out.biom ) + } if ( params.run_kaiju ) { + + ch_input_for_kaiju = ch_input_for_profiling.kaiju + .multiMap { + it -> + reads: [it[0] + it[2], it[1]] + db: it[3] + } + KAIJU_KAIJU ( ch_input_for_kaiju.reads, ch_input_for_kaiju.db) KAIJU_KAIJU2TABLE (KAIJU_KAIJU.out.results, ch_input_for_kaiju.db, params.kaiju_taxon_name) ch_multiqc_files = ch_multiqc_files.mix( KAIJU_KAIJU2TABLE.out.summary.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( KAIJU_KAIJU.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( KAIJU_KAIJU2TABLE.out.summary ) + } if ( params.run_diamond ) { + + ch_input_for_diamond = ch_input_for_profiling.diamond + .multiMap { + it -> + reads: [it[0] + it[2], it[1]] + db: it[3] + } + DIAMOND_BLASTX ( ch_input_for_diamond.reads, ch_input_for_diamond.db, params.diamond_output_format ) ch_versions = ch_versions.mix( DIAMOND_BLASTX.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( DIAMOND_BLASTX.out.output ) + } emit: From d5049a34e49c2a093039b83b183e48a81c6e9d60 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sun, 1 May 2022 07:28:29 +0200 Subject: [PATCH 193/306] Add missing close bracket --- subworkflows/local/profiling.nf | 1 + 1 file changed, 1 insertion(+) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index d8e9c84..7fb3ce9 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -75,6 +75,7 @@ workflow PROFILING { it -> reads: [ it[0], it[1].flatten() ] db: it[2] + } MALT_RUN ( ch_input_for_malt.reads, params.malt_mode, ch_input_for_malt.db ) From a821f748ee89ade6e229edab28fa7f3399969596 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 09:28:46 +0200 Subject: [PATCH 194/306] Add modules --- conf/modules.config | 41 ++++++++++++ modules.json | 14 +++- modules/nf-core/modules/metaphlan3/main.nf | 2 +- .../nf-core/modules/minimap2/align/main.nf | 48 ++++++++++++++ .../nf-core/modules/minimap2/align/meta.yml | 65 +++++++++++++++++++ .../nf-core/modules/minimap2/index/main.nf | 33 ++++++++++ .../nf-core/modules/minimap2/index/meta.yml | 30 +++++++++ .../nf-core/modules/samtools/bam2fq/main.nf | 56 ++++++++++++++++ .../nf-core/modules/samtools/bam2fq/meta.yml | 55 ++++++++++++++++ modules/nf-core/modules/samtools/view/main.nf | 44 +++++++++++++ .../nf-core/modules/samtools/view/meta.yml | 57 ++++++++++++++++ nf-core/modules/samtools/bam2fq/main.nf | 56 ++++++++++++++++ nf-core/modules/samtools/bam2fq/meta.yml | 55 ++++++++++++++++ 13 files changed, 554 insertions(+), 2 deletions(-) create mode 100644 modules/nf-core/modules/minimap2/align/main.nf create mode 100644 modules/nf-core/modules/minimap2/align/meta.yml create mode 100644 modules/nf-core/modules/minimap2/index/main.nf create mode 100644 modules/nf-core/modules/minimap2/index/meta.yml create mode 100644 modules/nf-core/modules/samtools/bam2fq/main.nf create mode 100644 modules/nf-core/modules/samtools/bam2fq/meta.yml create mode 100644 modules/nf-core/modules/samtools/view/main.nf create mode 100644 modules/nf-core/modules/samtools/view/meta.yml create mode 100644 nf-core/modules/samtools/bam2fq/main.nf create mode 100644 nf-core/modules/samtools/bam2fq/meta.yml diff --git a/conf/modules.config b/conf/modules.config index d8fb382..b707954 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -164,6 +164,47 @@ process { ] } + withName: MINIMAP2_INDEX { + ext.args = '-x map-ont' + publishDir = [ + path: { "${params.outdir}/minimap2/index" }, + mode: params.publish_dir_mode, + enabled: params.save_minimap2_hostremoval_index, + pattern: 'minimap2' + ] + } + + withName: MINIMAP2_ALIGN { + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/minimap2/align" }, + mode: params.publish_dir_mode, + enabled: params.save_minimap2_hostremoval_mapped, + pattern: '*.bam' + ] + } + + withName: SAMTOOLS_VIEW { + ext.args = '-f 4' + ext.prefix = { "${meta.id}.mapped.sorted" } + publishDir = [ + path: { "${params.outdir}/samtools/view" }, + mode: params.publish_dir_mode, + enabled: params.save_samtools_unmapped_bam, + pattern: '*.bam' + ] + } + + withName: SAMTOOLS_BAM2FQ { + ext.prefix = { "${meta.id}_${meta.run_accession}" } + publishDir = [ + path: { "${params.outdir}/samtools/bam2fq" }, + mode: params.publish_dir_mode, + enabled: params.save_minimap2_unmapped_fq, + pattern: '*.fq.gz' + ] + } + withName: BBMAP_BBDUK { ext.args = [ "entropy=${params.shortread_complexityfilter_entropy}", diff --git a/modules.json b/modules.json index a65926c..071234d 100644 --- a/modules.json +++ b/modules.json @@ -52,6 +52,12 @@ "git_sha": "2d38566eca4cc15142b2ffa7c11837569b39aece" }, "metaphlan3": { + "git_sha": "ed4dd1a928ebf4308efb720de878045f7773f8e2" + }, + "minimap2/align": { + "git_sha": "1a5a9e7b4009dcf34e6867dd1a5a1d9a718b027b" + }, + "minimap2/index": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, "multiqc": { @@ -63,9 +69,15 @@ "prinseqplusplus": { "git_sha": "f1c5384c31e985591716afdd732cf8c2ae29d05b" }, + "samtools/bam2fq": { + "git_sha": "897c33d5da084b61109500ee44c01da2d3e4e773" + }, + "samtools/view": { + "git_sha": "12afb6b0faf3cabf769c9a2a7dd477e3f066eac0" + }, "untar": { "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/metaphlan3/main.nf b/modules/nf-core/modules/metaphlan3/main.nf index 3fc6b27..bff0eb9 100644 --- a/modules/nf-core/modules/metaphlan3/main.nf +++ b/modules/nf-core/modules/metaphlan3/main.nf @@ -23,7 +23,7 @@ process METAPHLAN3 { script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - def input_type = ("$input".endsWith(".fastq.gz")) ? "--input_type fastq" : ("$input".contains(".fasta")) ? "--input_type fasta" : ("$input".endsWith(".bowtie2out.txt")) ? "--input_type bowtie2out" : "--input_type sam" + def input_type = ("$input".endsWith(".fastq.gz") || "$input".endsWith(".fq.gz")) ? "--input_type fastq" : ("$input".contains(".fasta")) ? "--input_type fasta" : ("$input".endsWith(".bowtie2out.txt")) ? "--input_type bowtie2out" : "--input_type sam" def input_data = ("$input_type".contains("fastq")) && !meta.single_end ? "${input[0]},${input[1]}" : "$input" def bowtie2_out = "$input_type" == "--input_type bowtie2out" || "$input_type" == "--input_type sam" ? '' : "--bowtie2out ${prefix}.bowtie2out.txt" diff --git a/modules/nf-core/modules/minimap2/align/main.nf b/modules/nf-core/modules/minimap2/align/main.nf new file mode 100644 index 0000000..08ac6ee --- /dev/null +++ b/modules/nf-core/modules/minimap2/align/main.nf @@ -0,0 +1,48 @@ +process MINIMAP2_ALIGN { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? 'bioconda::minimap2=2.21 bioconda::samtools=1.12' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-66534bcbb7031a148b13e2ad42583020b9cd25c4:1679e915ddb9d6b4abda91880c4b48857d471bd8-0' : + 'quay.io/biocontainers/mulled-v2-66534bcbb7031a148b13e2ad42583020b9cd25c4:1679e915ddb9d6b4abda91880c4b48857d471bd8-0' }" + + input: + tuple val(meta), path(reads) + path reference + val bam_format + val cigar_paf_format + val cigar_bam + + output: + tuple val(meta), path("*.paf"), optional: true, emit: paf + tuple val(meta), path("*.bam"), optional: true, emit: bam + 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}" + def input_reads = meta.single_end ? "$reads" : "${reads[0]} ${reads[1]}" + def bam_output = bam_format ? "-a | samtools sort | samtools view -@ ${task.cpus} -b -h -o ${prefix}.bam" : "-o ${prefix}.paf" + def cigar_paf = cigar_paf_format && !bam_format ? "-c" : '' + def set_cigar_bam = cigar_bam && bam_format ? "-L" : '' + """ + minimap2 \\ + $args \\ + -t $task.cpus \\ + $reference \\ + $input_reads \\ + $cigar_paf \\ + $set_cigar_bam \\ + $bam_output + + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + minimap2: \$(minimap2 --version 2>&1) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/minimap2/align/meta.yml b/modules/nf-core/modules/minimap2/align/meta.yml new file mode 100644 index 0000000..991b39a --- /dev/null +++ b/modules/nf-core/modules/minimap2/align/meta.yml @@ -0,0 +1,65 @@ +name: minimap2_align +description: A versatile pairwise aligner for genomic and spliced nucleotide sequences +keywords: + - align + - fasta + - fastq + - genome + - paf + - reference +tools: + - minimap2: + description: | + A versatile pairwise aligner for genomic and spliced nucleotide sequences. + homepage: https://github.com/lh3/minimap2 + documentation: https://github.com/lh3/minimap2#uguide + licence: ["MIT"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FASTA or FASTQ files of size 1 and 2 for single-end + and paired-end data, respectively. + - reference: + type: file + description: | + Reference database in FASTA format. + - bam_format: + type: boolean + description: Specify that output should be in BAM format + - cigar_paf_format: + type: boolean + description: Specify that output CIGAR should be in PAF format + - cigar_bam: + type: boolean + description: | + Write CIGAR with >65535 ops at the CG tag. This is recommended when + doing XYZ (https://github.com/lh3/minimap2#working-with-65535-cigar-operations) +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - paf: + type: file + description: Alignment in PAF format + pattern: "*.paf" + - bam: + type: file + description: Alignment in BAM format + pattern: "*.bam" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@heuermh" + - "@sofstam" + - "@sateeshperi" + - "@jfy133" diff --git a/modules/nf-core/modules/minimap2/index/main.nf b/modules/nf-core/modules/minimap2/index/main.nf new file mode 100644 index 0000000..3dfeb86 --- /dev/null +++ b/modules/nf-core/modules/minimap2/index/main.nf @@ -0,0 +1,33 @@ +process MINIMAP2_INDEX { + label 'process_medium' + + conda (params.enable_conda ? 'bioconda::minimap2=2.21' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/minimap2:2.21--h5bf99c6_0' : + 'quay.io/biocontainers/minimap2:2.21--h5bf99c6_0' }" + + input: + path fasta + + output: + path "*.mmi" , emit: index + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + """ + minimap2 \\ + -t $task.cpus \\ + -d ${fasta.baseName}.mmi \\ + $args \\ + $fasta + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + minimap2: \$(minimap2 --version 2>&1) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/minimap2/index/meta.yml b/modules/nf-core/modules/minimap2/index/meta.yml new file mode 100644 index 0000000..3bf9f04 --- /dev/null +++ b/modules/nf-core/modules/minimap2/index/meta.yml @@ -0,0 +1,30 @@ +name: minimap2_index +description: Provides fasta index required by minimap2 alignment. +keywords: + - index + - fasta + - reference +tools: + - minimap2: + description: | + A versatile pairwise aligner for genomic and spliced nucleotide sequences. + homepage: https://github.com/lh3/minimap2 + documentation: https://github.com/lh3/minimap2#uguide + licence: ["MIT"] +input: + - fasta: + type: file + description: | + Reference database in FASTA format. +output: + - mmi: + type: file + description: Minimap2 fasta index. + pattern: "*.mmi" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@yuukiiwa" + - "@drpatelh" diff --git a/modules/nf-core/modules/samtools/bam2fq/main.nf b/modules/nf-core/modules/samtools/bam2fq/main.nf new file mode 100644 index 0000000..554af48 --- /dev/null +++ b/modules/nf-core/modules/samtools/bam2fq/main.nf @@ -0,0 +1,56 @@ +process SAMTOOLS_BAM2FQ { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::samtools=1.15.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.15.1--h1170115_0' : + 'quay.io/biocontainers/samtools:1.15.1--h1170115_0' }" + + input: + tuple val(meta), path(inputbam) + val split + + output: + tuple val(meta), path("*.fq.gz"), emit: reads + 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}" + + if (split){ + """ + samtools \\ + bam2fq \\ + $args \\ + -@ $task.cpus \\ + -1 ${prefix}_1.fq.gz \\ + -2 ${prefix}_2.fq.gz \\ + -0 ${prefix}_other.fq.gz \\ + -s ${prefix}_singleton.fq.gz \\ + $inputbam + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ + } else { + """ + samtools \\ + bam2fq \\ + $args \\ + -@ $task.cpus \\ + $inputbam | gzip > ${prefix}_interleaved.fq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ + } +} diff --git a/modules/nf-core/modules/samtools/bam2fq/meta.yml b/modules/nf-core/modules/samtools/bam2fq/meta.yml new file mode 100644 index 0000000..319a60c --- /dev/null +++ b/modules/nf-core/modules/samtools/bam2fq/meta.yml @@ -0,0 +1,55 @@ +name: samtools_bam2fq +description: | + The module uses bam2fq method from samtools to + convert a SAM, BAM or CRAM file to FASTQ format +keywords: + - bam2fq + - samtools + - fastq +tools: + - samtools: + description: Tools for dealing with SAM, BAM and CRAM files + homepage: None + documentation: http://www.htslib.org/doc/1.1/samtools.html + tool_dev_url: None + doi: "" + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - inputbam: + type: file + description: BAM/CRAM/SAM file + pattern: "*.{bam,cram,sam}" + - split: + type: boolean + description: | + TRUE/FALSE value to indicate if reads should be separated into + /1, /2 and if present other, or singleton. + Note: choosing TRUE will generate 4 different files. + Choosing FALSE will produce a single file, which will be interleaved in case + the input contains paired reads. + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - reads: + type: file + description: | + FASTQ files, which will be either a group of 4 files (read_1, read_2, other and singleton) + or a single interleaved .fq.gz file if the user chooses not to split the reads. + pattern: "*.fq.gz" + +authors: + - "@lescai" diff --git a/modules/nf-core/modules/samtools/view/main.nf b/modules/nf-core/modules/samtools/view/main.nf new file mode 100644 index 0000000..11cfb74 --- /dev/null +++ b/modules/nf-core/modules/samtools/view/main.nf @@ -0,0 +1,44 @@ +process SAMTOOLS_VIEW { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? "bioconda::samtools=1.15.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.15.1--h1170115_0' : + 'quay.io/biocontainers/samtools:1.15.1--h1170115_0' }" + + input: + tuple val(meta), path(input), path(index) + path fasta + + output: + tuple val(meta), path("*.bam") , emit: bam , optional: true + tuple val(meta), path("*.cram"), emit: cram, optional: true + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def reference = fasta ? "--reference ${fasta} -C" : "" + def file_type = input.getExtension() + if ("$input" == "${prefix}.${file_type}") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" + """ + samtools \\ + view \\ + --threads ${task.cpus-1} \\ + ${reference} \\ + $args \\ + $input \\ + $args2 \\ + > ${prefix}.${file_type} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/samtools/view/meta.yml b/modules/nf-core/modules/samtools/view/meta.yml new file mode 100644 index 0000000..a8b43ec --- /dev/null +++ b/modules/nf-core/modules/samtools/view/meta.yml @@ -0,0 +1,57 @@ +name: samtools_view +description: filter/convert SAM/BAM/CRAM file +keywords: + - view + - bam + - sam + - cram +tools: + - samtools: + description: | + SAMtools is a set of utilities for interacting with and post-processing + short DNA sequence read alignments in the SAM, BAM and CRAM formats, written by Heng Li. + These files are generated as output by short read aligners like BWA. + homepage: http://www.htslib.org/ + documentation: hhttp://www.htslib.org/doc/samtools.html + doi: 10.1093/bioinformatics/btp352 + licence: ["MIT"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - input: + type: file + description: BAM/CRAM/SAM file + pattern: "*.{bam,cram,sam}" + - index: + type: optional file + description: BAM.BAI/CRAM.CRAI file + pattern: "*.{.bai,.crai}" + - fasta: + type: optional file + description: Reference file the CRAM was created with + pattern: "*.{fasta,fa}" +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: filtered/converted BAM/SAM file + pattern: "*.{bam,sam}" + - cram: + type: file + description: filtered/converted CRAM file + pattern: "*.cram" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@drpatelh" + - "@joseespinosa" + - "@FriederikeHanssen" diff --git a/nf-core/modules/samtools/bam2fq/main.nf b/nf-core/modules/samtools/bam2fq/main.nf new file mode 100644 index 0000000..5d6aa79 --- /dev/null +++ b/nf-core/modules/samtools/bam2fq/main.nf @@ -0,0 +1,56 @@ +process SAMTOOLS_BAM2FQ { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::samtools=1.15.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.15.1--h1170115_0' : + 'quay.io/biocontainers/samtools:1.15.1--h1170115_0' }" + + input: + tuple val(meta), path(inputbam) + val split + + output: + tuple val(meta), path("*.fq.gz"), emit: reads + 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}" + + if (split){ + """ + samtools \\ + bam2fq \\ + $args \\ + -@ $task.cpus \\ + -1 ${prefix}_1.fq.gz \\ + -2 ${prefix}_2.fq.gz \\ + -0 ${prefix}_other.fq.gz \\ + -s ${prefix}_singleton.fq.gz \\ + $inputbam + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ + } else { + """ + samtools \\ + bam2fq \\ + $args \\ + -@ $task.cpus \\ + $inputbam >${prefix}_interleaved.fq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ + } +} diff --git a/nf-core/modules/samtools/bam2fq/meta.yml b/nf-core/modules/samtools/bam2fq/meta.yml new file mode 100644 index 0000000..319a60c --- /dev/null +++ b/nf-core/modules/samtools/bam2fq/meta.yml @@ -0,0 +1,55 @@ +name: samtools_bam2fq +description: | + The module uses bam2fq method from samtools to + convert a SAM, BAM or CRAM file to FASTQ format +keywords: + - bam2fq + - samtools + - fastq +tools: + - samtools: + description: Tools for dealing with SAM, BAM and CRAM files + homepage: None + documentation: http://www.htslib.org/doc/1.1/samtools.html + tool_dev_url: None + doi: "" + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - inputbam: + type: file + description: BAM/CRAM/SAM file + pattern: "*.{bam,cram,sam}" + - split: + type: boolean + description: | + TRUE/FALSE value to indicate if reads should be separated into + /1, /2 and if present other, or singleton. + Note: choosing TRUE will generate 4 different files. + Choosing FALSE will produce a single file, which will be interleaved in case + the input contains paired reads. + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - reads: + type: file + description: | + FASTQ files, which will be either a group of 4 files (read_1, read_2, other and singleton) + or a single interleaved .fq.gz file if the user chooses not to split the reads. + pattern: "*.fq.gz" + +authors: + - "@lescai" From 5e6be52fab87d6b6a7f14ce254f63b02ad682a63 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 09:34:27 +0200 Subject: [PATCH 195/306] Implement hostremoval --- nextflow.config | 18 ++++++--- subworkflows/local/longread_hostremoval.nf | 47 ++++++++++++++++++++++ workflows/taxprofiler.nf | 17 ++++++-- 3 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 subworkflows/local/longread_hostremoval.nf diff --git a/nextflow.config b/nextflow.config index 5644786..04273f1 100644 --- a/nextflow.config +++ b/nextflow.config @@ -81,12 +81,18 @@ params { save_runmerged_reads = false // Host Removal - perform_shortread_hostremoval = false - shortread_hostremoval_reference = null - shortread_hostremoval_index = null - save_hostremoval_index = false - save_hostremoval_mapped = false - save_hostremoval_unmapped = false + perform_shortread_hostremoval = false + shortread_hostremoval_reference = null + shortread_hostremoval_index = null + longread_hostremoval_index = null + save_hostremoval_index = false + save_hostremoval_mapped = false + save_hostremoval_unmapped = false + save_minimap2_hostremoval_index = false + save_minimap2_hostremoval_mapped = false + save_minimap2_hostremoval_unmapped = false + save_samtools_unmapped_bam = false + save_minimap2_unmapped_fq = false // MALT run_malt = false diff --git a/subworkflows/local/longread_hostremoval.nf b/subworkflows/local/longread_hostremoval.nf new file mode 100644 index 0000000..7db020b --- /dev/null +++ b/subworkflows/local/longread_hostremoval.nf @@ -0,0 +1,47 @@ +// +// Remove host reads via alignment and export off-target reads +// + +include { MINIMAP2_INDEX } from '../../modules/nf-core/modules/minimap2/index/main' +include { MINIMAP2_ALIGN } from '../../modules/nf-core/modules/minimap2/align/main' +include { SAMTOOLS_VIEW } from '../../modules/nf-core/modules/samtools/view/main' +include { SAMTOOLS_BAM2FQ } from '../../modules/nf-core/modules/samtools/bam2fq/main' + +workflow LONGREAD_HOSTREMOVAL { + take: + reads // [ [ meta ], [ reads ] ] + reference // /path/to/fasta + index // /path/to/index + + main: + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + if ( !params.longread_hostremoval_index ) { + ch_minimap2_index = MINIMAP2_INDEX ( reference ).index + ch_versions = ch_versions.mix( MINIMAP2_INDEX.out.versions ) + } else { + ch_minimap2_index = index + } + + MINIMAP2_ALIGN ( reads, ch_minimap2_index, true, false, false ) + ch_versions = ch_versions.mix( MINIMAP2_ALIGN.out.versions.first() ) + ch_minimap2_mapped = MINIMAP2_ALIGN.out.bam + .map { + meta, reads -> + [ meta, reads, [] ] + } + + + SAMTOOLS_VIEW ( ch_minimap2_mapped , [] ) + ch_versions = ch_versions.mix( SAMTOOLS_VIEW.out.versions.first() ) + + SAMTOOLS_BAM2FQ ( SAMTOOLS_VIEW.out.bam, false ) + ch_versions = ch_versions.mix( SAMTOOLS_BAM2FQ.out.versions.first() ) + + + emit: + reads = SAMTOOLS_BAM2FQ.out.reads // channel: [ val(meta), [ reads ] ] + versions = ch_versions // channel: [ versions.yml ] +} + diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index a0046b2..9eb53a3 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -26,7 +26,8 @@ if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_refere if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } -if (params.shortread_hostremoval_index ) { ch_reference_index = file(params.shortread_hostremoval_index ) } else { ch_reference_index = [] } +if (params.shortread_hostremoval_index ) { ch_shortread_reference_index = file(params.shortread_hostremoval_index ) } else { ch_shortread_reference_index = [] } +if (params.longread_hostremoval_index ) { ch_longread_reference_index = file(params.longread_hostremoval_index ) } else { ch_longread_reference_index = [] } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -52,6 +53,7 @@ include { DB_CHECK } from '../subworkflows/local/db_check' include { SHORTREAD_PREPROCESSING } from '../subworkflows/local/shortread_preprocessing' include { LONGREAD_PREPROCESSING } from '../subworkflows/local/longread_preprocessing' include { SHORTREAD_HOSTREMOVAL } from '../subworkflows/local/shortread_hostremoval' +include { LONGREAD_HOSTREMOVAL } from '../subworkflows/local/longread_hostremoval' include { SHORTREAD_COMPLEXITYFILTERING } from '../subworkflows/local/shortread_complexityfiltering' include { PROFILING } from '../subworkflows/local/profiling' @@ -141,16 +143,23 @@ workflow TAXPROFILER { */ if ( params.perform_shortread_hostremoval ) { - ch_shortreads_hostremoved = SHORTREAD_HOSTREMOVAL ( ch_shortreads_filtered, ch_reference, ch_reference_index ).reads + ch_shortreads_hostremoved = SHORTREAD_HOSTREMOVAL ( ch_shortreads_filtered, ch_reference, ch_shortread_reference_index ).reads ch_versions = ch_versions.mix(SHORTREAD_HOSTREMOVAL.out.versions) } else { ch_shortreads_hostremoved = ch_shortreads_filtered } + if ( params.perform_longread_hostremoval ) { + ch_longreads_hostremoved = LONGREAD_HOSTREMOVAL ( ch_longreads_preprocessed, ch_reference, ch_longread_reference_index ).reads + ch_versions = ch_versions.mix(LONGREAD_HOSTREMOVAL.out.versions) + } else { + ch_longreads_hostremoved = ch_longreads_preprocessed + } + if ( params.perform_runmerging ) { ch_reads_for_cat_branch = ch_shortreads_hostremoved - .mix( ch_longreads_preprocessed ) + .mix( ch_longreads_hostremoved ) .map { meta, reads -> def meta_new = meta.clone() @@ -182,7 +191,7 @@ workflow TAXPROFILER { } else { ch_reads_runmerged = ch_shortreads_hostremoved - .mix( ch_longreads_preprocessed, INPUT_CHECK.out.fasta ) + .mix( ch_longreads_hostremoved, INPUT_CHECK.out.fasta ) } /* From afdebaf09a41cfc6fd3f3d3455f3f69b1e1dc608 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 10:15:11 +0200 Subject: [PATCH 196/306] Remove own modification to a module --- modules/nf-core/modules/samtools/bam2fq/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nf-core/modules/samtools/bam2fq/main.nf b/modules/nf-core/modules/samtools/bam2fq/main.nf index 554af48..0dd1da9 100644 --- a/modules/nf-core/modules/samtools/bam2fq/main.nf +++ b/modules/nf-core/modules/samtools/bam2fq/main.nf @@ -45,7 +45,7 @@ process SAMTOOLS_BAM2FQ { bam2fq \\ $args \\ -@ $task.cpus \\ - $inputbam | gzip > ${prefix}_interleaved.fq.gz + $inputbam > ${prefix}_interleaved.fq.gz cat <<-END_VERSIONS > versions.yml "${task.process}": From fa1c13263573e5cda53fb2468e8c339ec0ad0ecf Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 11:03:25 +0200 Subject: [PATCH 197/306] Update nextflow_schema --- nextflow_schema.json | 67 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index f429d1b..d3878c1 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +348,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -388,7 +404,14 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": ["phylum", "class", "order", "family", "genus", "species"] + "enum": [ + "phylum", + "class", + "order", + "family", + "genus", + "species" + ] }, "run_diamond": { "type": "boolean" @@ -396,7 +419,39 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] + "enum": [ + "blast", + "xml", + "txt", + "daa", + "sam", + "tsv", + "paf" + ] + }, + "longread_hostremoval_index": { + "type": "string", + "default": null + }, + "save_minimap2_hostremoval_index": { + "type": "string", + "default": "false", + "description": "Flag for publishing minimap2 host removal index" + }, + "save_minimap2_hostremoval_mapped": { + "type": "string", + "default": "false", + "description": "Flag for publishinig bam file with all long reads mapped to a reference" + }, + "save_samtools_unmapped_bam": { + "type": "string", + "default": "false", + "description": "Flag for publishing bam for reads that did not map to the host reference" + }, + "save_minimap2_unmapped_fq": { + "type": "string", + "default": "false", + "description": "Flag for publishing fastq files for reads that did not map to the host reference" } } } From 4b2a3789cd7ba15ac41f32f6179bcc1665cb8e47 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 11:03:48 +0200 Subject: [PATCH 198/306] Update default values for host removal parameters --- nextflow.config | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/nextflow.config b/nextflow.config index 04273f1..95daba9 100644 --- a/nextflow.config +++ b/nextflow.config @@ -81,18 +81,18 @@ params { save_runmerged_reads = false // Host Removal - perform_shortread_hostremoval = false - shortread_hostremoval_reference = null - shortread_hostremoval_index = null - longread_hostremoval_index = null - save_hostremoval_index = false - save_hostremoval_mapped = false - save_hostremoval_unmapped = false - save_minimap2_hostremoval_index = false - save_minimap2_hostremoval_mapped = false - save_minimap2_hostremoval_unmapped = false - save_samtools_unmapped_bam = false - save_minimap2_unmapped_fq = false + perform_shortread_hostremoval = false + shortread_hostremoval_reference = null + shortread_hostremoval_index = null + save_hostremoval_index = false + save_hostremoval_mapped = false + save_hostremoval_unmapped = false + longread_hostremoval_index = null + save_minimap2_hostremoval_index = false + save_minimap2_hostremoval_mapped = false + save_samtools_unmapped_bam = false + save_minimap2_unmapped_fq = false + // MALT run_malt = false From 17039ebc6b143870331c4d2f8976a0fe63de0736 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 11:07:23 +0200 Subject: [PATCH 199/306] Fix type to boolean for flags --- nextflow_schema.json | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index d3878c1..257f5cb 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -431,27 +431,23 @@ }, "longread_hostremoval_index": { "type": "string", - "default": null + "default": "None" }, "save_minimap2_hostremoval_index": { - "type": "string", - "default": "false", + "type": "boolean", "description": "Flag for publishing minimap2 host removal index" }, "save_minimap2_hostremoval_mapped": { - "type": "string", - "default": "false", + "type": "boolean", "description": "Flag for publishinig bam file with all long reads mapped to a reference" }, "save_samtools_unmapped_bam": { - "type": "string", - "default": "false", + "type": "boolean", "description": "Flag for publishing bam for reads that did not map to the host reference" }, "save_minimap2_unmapped_fq": { - "type": "string", - "default": "false", + "type": "boolean", "description": "Flag for publishing fastq files for reads that did not map to the host reference" } } -} +} \ No newline at end of file From 58d4dec70be5ac5eb2356aac351593c27f77502a Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 11:09:54 +0200 Subject: [PATCH 200/306] Update samtools view --- modules.json | 2 +- modules/nf-core/modules/samtools/view/main.nf | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 071234d..b568d9c 100644 --- a/modules.json +++ b/modules.json @@ -73,7 +73,7 @@ "git_sha": "897c33d5da084b61109500ee44c01da2d3e4e773" }, "samtools/view": { - "git_sha": "12afb6b0faf3cabf769c9a2a7dd477e3f066eac0" + "git_sha": "6b64f9cb6c3dd3577931cc3cd032d6fb730000ce" }, "untar": { "git_sha": "e080f4c8acf5760039ed12ec1f206170f3f9a918" diff --git a/modules/nf-core/modules/samtools/view/main.nf b/modules/nf-core/modules/samtools/view/main.nf index 11cfb74..55194e8 100644 --- a/modules/nf-core/modules/samtools/view/main.nf +++ b/modules/nf-core/modules/samtools/view/main.nf @@ -41,4 +41,16 @@ process SAMTOOLS_VIEW { samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') END_VERSIONS """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.bam + touch ${prefix}.cram + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ } From c833d4fd9ae037e297eda9dea69045715fc4a064 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 11:42:05 +0200 Subject: [PATCH 201/306] Fix issues found with prettier --- modules.json | 2 +- nextflow_schema.json | 45 +++++++------------------------------------- 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/modules.json b/modules.json index b568d9c..ce1fbc5 100644 --- a/modules.json +++ b/modules.json @@ -80,4 +80,4 @@ } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index 257f5cb..1902e27 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -348,10 +335,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -404,14 +388,7 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": [ - "phylum", - "class", - "order", - "family", - "genus", - "species" - ] + "enum": ["phylum", "class", "order", "family", "genus", "species"] }, "run_diamond": { "type": "boolean" @@ -419,15 +396,7 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": [ - "blast", - "xml", - "txt", - "daa", - "sam", - "tsv", - "paf" - ] + "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] }, "longread_hostremoval_index": { "type": "string", @@ -450,4 +419,4 @@ "description": "Flag for publishing fastq files for reads that did not map to the host reference" } } -} \ No newline at end of file +} From f4abbe280ab6497a220fde116f606d588b49bf73 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Tue, 3 May 2022 12:56:49 +0200 Subject: [PATCH 202/306] Fix back to nf-core modules version --- .../nf-core/modules/samtools/bam2fq/main.nf | 2 +- nf-core/modules/samtools/bam2fq/main.nf | 56 ------------------- nf-core/modules/samtools/bam2fq/meta.yml | 55 ------------------ 3 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 nf-core/modules/samtools/bam2fq/main.nf delete mode 100644 nf-core/modules/samtools/bam2fq/meta.yml diff --git a/modules/nf-core/modules/samtools/bam2fq/main.nf b/modules/nf-core/modules/samtools/bam2fq/main.nf index 0dd1da9..5d6aa79 100644 --- a/modules/nf-core/modules/samtools/bam2fq/main.nf +++ b/modules/nf-core/modules/samtools/bam2fq/main.nf @@ -45,7 +45,7 @@ process SAMTOOLS_BAM2FQ { bam2fq \\ $args \\ -@ $task.cpus \\ - $inputbam > ${prefix}_interleaved.fq.gz + $inputbam >${prefix}_interleaved.fq.gz cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/nf-core/modules/samtools/bam2fq/main.nf b/nf-core/modules/samtools/bam2fq/main.nf deleted file mode 100644 index 5d6aa79..0000000 --- a/nf-core/modules/samtools/bam2fq/main.nf +++ /dev/null @@ -1,56 +0,0 @@ -process SAMTOOLS_BAM2FQ { - tag "$meta.id" - label 'process_low' - - conda (params.enable_conda ? "bioconda::samtools=1.15.1" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/samtools:1.15.1--h1170115_0' : - 'quay.io/biocontainers/samtools:1.15.1--h1170115_0' }" - - input: - tuple val(meta), path(inputbam) - val split - - output: - tuple val(meta), path("*.fq.gz"), emit: reads - 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}" - - if (split){ - """ - samtools \\ - bam2fq \\ - $args \\ - -@ $task.cpus \\ - -1 ${prefix}_1.fq.gz \\ - -2 ${prefix}_2.fq.gz \\ - -0 ${prefix}_other.fq.gz \\ - -s ${prefix}_singleton.fq.gz \\ - $inputbam - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') - END_VERSIONS - """ - } else { - """ - samtools \\ - bam2fq \\ - $args \\ - -@ $task.cpus \\ - $inputbam >${prefix}_interleaved.fq.gz - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') - END_VERSIONS - """ - } -} diff --git a/nf-core/modules/samtools/bam2fq/meta.yml b/nf-core/modules/samtools/bam2fq/meta.yml deleted file mode 100644 index 319a60c..0000000 --- a/nf-core/modules/samtools/bam2fq/meta.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: samtools_bam2fq -description: | - The module uses bam2fq method from samtools to - convert a SAM, BAM or CRAM file to FASTQ format -keywords: - - bam2fq - - samtools - - fastq -tools: - - samtools: - description: Tools for dealing with SAM, BAM and CRAM files - homepage: None - documentation: http://www.htslib.org/doc/1.1/samtools.html - tool_dev_url: None - doi: "" - licence: ["MIT"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - inputbam: - type: file - description: BAM/CRAM/SAM file - pattern: "*.{bam,cram,sam}" - - split: - type: boolean - description: | - TRUE/FALSE value to indicate if reads should be separated into - /1, /2 and if present other, or singleton. - Note: choosing TRUE will generate 4 different files. - Choosing FALSE will produce a single file, which will be interleaved in case - the input contains paired reads. - -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - - reads: - type: file - description: | - FASTQ files, which will be either a group of 4 files (read_1, read_2, other and singleton) - or a single interleaved .fq.gz file if the user chooses not to split the reads. - pattern: "*.fq.gz" - -authors: - - "@lescai" From 59c7f5a5b1b6a3e144a9b1c151e6f2a4c315342f Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Thu, 5 May 2022 08:38:41 +0200 Subject: [PATCH 203/306] Update samtools/bam2fq --- modules.json | 4 ++-- modules/nf-core/modules/samtools/bam2fq/main.nf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules.json b/modules.json index ce1fbc5..043e4ad 100644 --- a/modules.json +++ b/modules.json @@ -70,7 +70,7 @@ "git_sha": "f1c5384c31e985591716afdd732cf8c2ae29d05b" }, "samtools/bam2fq": { - "git_sha": "897c33d5da084b61109500ee44c01da2d3e4e773" + "git_sha": "5510ea39fe638594bc26ac34cadf4a84bf27d159" }, "samtools/view": { "git_sha": "6b64f9cb6c3dd3577931cc3cd032d6fb730000ce" @@ -80,4 +80,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/samtools/bam2fq/main.nf b/modules/nf-core/modules/samtools/bam2fq/main.nf index 5d6aa79..9301d1d 100644 --- a/modules/nf-core/modules/samtools/bam2fq/main.nf +++ b/modules/nf-core/modules/samtools/bam2fq/main.nf @@ -45,7 +45,7 @@ process SAMTOOLS_BAM2FQ { bam2fq \\ $args \\ -@ $task.cpus \\ - $inputbam >${prefix}_interleaved.fq.gz + $inputbam | gzip --no-name > ${prefix}_interleaved.fq.gz cat <<-END_VERSIONS > versions.yml "${task.process}": From 5b7b21415633c3df64cb2d88734d5863b4162a9c Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Thu, 5 May 2022 08:40:53 +0200 Subject: [PATCH 204/306] Fix formatting with prettier --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 043e4ad..a55c88b 100644 --- a/modules.json +++ b/modules.json @@ -80,4 +80,4 @@ } } } -} \ No newline at end of file +} From d94534e8acee01575b589e88549ed64ab4fc4411 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Thu, 5 May 2022 09:07:33 +0200 Subject: [PATCH 205/306] Simplify params which control host removal publish --- conf/modules.config | 8 ++++---- nextflow.config | 6 +----- nextflow_schema.json | 16 ---------------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index b707954..cd0fb04 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -169,7 +169,7 @@ process { publishDir = [ path: { "${params.outdir}/minimap2/index" }, mode: params.publish_dir_mode, - enabled: params.save_minimap2_hostremoval_index, + enabled: params.save_hostremoval_index, pattern: 'minimap2' ] } @@ -179,7 +179,7 @@ process { publishDir = [ path: { "${params.outdir}/minimap2/align" }, mode: params.publish_dir_mode, - enabled: params.save_minimap2_hostremoval_mapped, + enabled: params.save_hostremoval_mapped, pattern: '*.bam' ] } @@ -190,7 +190,7 @@ process { publishDir = [ path: { "${params.outdir}/samtools/view" }, mode: params.publish_dir_mode, - enabled: params.save_samtools_unmapped_bam, + enabled: params.save_hostremoval_unmapped, pattern: '*.bam' ] } @@ -200,7 +200,7 @@ process { publishDir = [ path: { "${params.outdir}/samtools/bam2fq" }, mode: params.publish_dir_mode, - enabled: params.save_minimap2_unmapped_fq, + enabled: params.save_hostremoval_unmapped, pattern: '*.fq.gz' ] } diff --git a/nextflow.config b/nextflow.config index 95daba9..8c99af2 100644 --- a/nextflow.config +++ b/nextflow.config @@ -84,14 +84,10 @@ params { perform_shortread_hostremoval = false shortread_hostremoval_reference = null shortread_hostremoval_index = null + longread_hostremoval_index = null save_hostremoval_index = false save_hostremoval_mapped = false save_hostremoval_unmapped = false - longread_hostremoval_index = null - save_minimap2_hostremoval_index = false - save_minimap2_hostremoval_mapped = false - save_samtools_unmapped_bam = false - save_minimap2_unmapped_fq = false // MALT diff --git a/nextflow_schema.json b/nextflow_schema.json index 1902e27..9e4cc6c 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -401,22 +401,6 @@ "longread_hostremoval_index": { "type": "string", "default": "None" - }, - "save_minimap2_hostremoval_index": { - "type": "boolean", - "description": "Flag for publishing minimap2 host removal index" - }, - "save_minimap2_hostremoval_mapped": { - "type": "boolean", - "description": "Flag for publishinig bam file with all long reads mapped to a reference" - }, - "save_samtools_unmapped_bam": { - "type": "boolean", - "description": "Flag for publishing bam for reads that did not map to the host reference" - }, - "save_minimap2_unmapped_fq": { - "type": "boolean", - "description": "Flag for publishing fastq files for reads that did not map to the host reference" } } } From 557d31dfd2fecbac2a415ddab780e6a3abc87168 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Thu, 5 May 2022 13:19:10 +0200 Subject: [PATCH 206/306] Add parameter for turning on longread host removal --- conf/test.config | 2 +- docs/usage.md | 2 +- nextflow.config | 2 +- nextflow_schema.json | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/conf/test.config b/conf/test.config index a2464b2..6d04f60 100644 --- a/conf/test.config +++ b/conf/test.config @@ -28,7 +28,7 @@ params { perform_longread_clip = false perform_shortread_complexityfilter = true perform_shortread_hostremoval = true - shortread_hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' + perform_longread_hostremoval = true run_kaiju = true run_kraken2 = true run_malt = true diff --git a/docs/usage.md b/docs/usage.md index cee2bb6..537b94a 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -191,7 +191,7 @@ You can optionally save the FASTQ output of the run merging with the `--save_com #### Host Removal -Removal of possible-host reads from FASTQ files prior profiling can be activated with `--perform_shortread_hostremoval` +Removal of possible-host reads from FASTQ files prior profiling can be activated with `--perform_shortread_hostremoval` or `--perform_longread_hostremoval`. Similarly to complexity filtering, host-removal can be useful for runtime optimisation and reduction in misclassified reads. It is not always necessary to report classification of reads from a host when you already know the host of the sample, therefore you can gain a run-time and computational advantage by removing these prior typically resource-heavy profiling with more efficient methods. Furthermore, particularly with human samples, you can reduce the number of false positives during profiling that occur due to host-sequence contamination in reference genomes on public databases. diff --git a/nextflow.config b/nextflow.config index 8c99af2..4ac0c44 100644 --- a/nextflow.config +++ b/nextflow.config @@ -82,7 +82,7 @@ params { // Host Removal perform_shortread_hostremoval = false - shortread_hostremoval_reference = null + perform_longread_hostremoval = false shortread_hostremoval_index = null longread_hostremoval_index = null save_hostremoval_index = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 9e4cc6c..d2eee95 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -362,7 +362,9 @@ "perform_shortread_hostremoval": { "type": "boolean" }, - "shortread_hostremoval_reference": { + "perform_longread_hostremoval": { + "type": "boolean" + }, "type": "string", "default": "None" }, From 6618e8cac65fe43d40d4952863698801f9b4507b Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Thu, 5 May 2022 13:20:34 +0200 Subject: [PATCH 207/306] Change param to a more generic name --- conf/test.config | 1 + docs/usage.md | 2 +- nextflow.config | 1 + nextflow_schema.json | 1 + workflows/taxprofiler.nf | 8 ++++---- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/conf/test.config b/conf/test.config index 6d04f60..a5244f9 100644 --- a/conf/test.config +++ b/conf/test.config @@ -29,6 +29,7 @@ params { perform_shortread_complexityfilter = true perform_shortread_hostremoval = true perform_longread_hostremoval = true + hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' run_kaiju = true run_kraken2 = true run_malt = true diff --git a/docs/usage.md b/docs/usage.md index 537b94a..1143da3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -197,7 +197,7 @@ Similarly to complexity filtering, host-removal can be useful for runtime optimi nf-core/taxprofiler currently offers host-removal via alignment against a reference genome with Bowtie2, and the use of the unaligned reads for downstream profiling. -You can supply your reference genome in FASTA format with `--shortread_hostremoval_reference`. You can also optionally supply a directory containing pre-indexed Bowtie2 index files with `--shortread_hostremoval_index`, however nf-core/taxprofiler will generate this for you if necessary. Pre-supplying the directory of index files can greatly speed up the process, and these can be re-used. +You can supply your reference genome in FASTA format with `--hostremoval_reference`. You can also optionally supply a directory containing pre-indexed Bowtie2 index files with `--shortread_hostremoval_index`, however nf-core/taxprofiler will generate this for you if necessary. Pre-supplying the directory of index files can greatly speed up the process, and these can be re-used. > 💡 If you have multiple taxa or sequences you wish to remove (e.g., the host genome and then also PhiX - common quality-control reagent during sequencing) you can simply concatenate the FASTAs of each taxa or sequences into a single reference file. diff --git a/nextflow.config b/nextflow.config index 4ac0c44..ca9e280 100644 --- a/nextflow.config +++ b/nextflow.config @@ -83,6 +83,7 @@ params { // Host Removal perform_shortread_hostremoval = false perform_longread_hostremoval = false + hostremoval_reference = null shortread_hostremoval_index = null longread_hostremoval_index = null save_hostremoval_index = false diff --git a/nextflow_schema.json b/nextflow_schema.json index d2eee95..ab2108e 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -365,6 +365,7 @@ "perform_longread_hostremoval": { "type": "boolean" }, + "hostremoval_reference": { "type": "string", "default": "None" }, diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 9eb53a3..81ea5d9 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -11,7 +11,7 @@ WorkflowTaxprofiler.initialise(params, log) // TODO nf-core: Add all file path parameters for the pipeline to the list below // Check input path parameters to see if they exist -def checkPathParamList = [ params.input, params.databases, params.shortread_hostremoval_reference, +def checkPathParamList = [ params.input, params.databases, params.hostremoval_reference, params.shortread_hostremoval_index, params.multiqc_config ] for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } @@ -22,10 +22,10 @@ if (params.databases) { ch_databases = file(params.databases) } else { exit 1, ' if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" -if (params.perform_shortread_hostremoval && !params.shortread_hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --shortread_hostremoval_reference FASTA supplied. Check input." } -if (!params.shortread_hostremoval_reference && params.shortread_hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --shortread_hostremoval_reference FASTA supplied. Check input." } +if (params.perform_shortread_hostremoval && !params.hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --hostremoval_reference FASTA supplied. Check input." } +if (!params.hostremoval_reference && params.hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --hostremoval_reference FASTA supplied. Check input." } -if (params.shortread_hostremoval_reference ) { ch_reference = file(params.shortread_hostremoval_reference) } +if (params.hostremoval_reference ) { ch_reference = file(params.hostremoval_reference) } if (params.shortread_hostremoval_index ) { ch_shortread_reference_index = file(params.shortread_hostremoval_index ) } else { ch_shortread_reference_index = [] } if (params.longread_hostremoval_index ) { ch_longread_reference_index = file(params.longread_hostremoval_index ) } else { ch_longread_reference_index = [] } From e3e55f57c26ba462ceb6795be55b312e615ca550 Mon Sep 17 00:00:00 2001 From: ljmesi <37740329+ljmesi@users.noreply.github.com> Date: Thu, 5 May 2022 14:02:24 +0200 Subject: [PATCH 208/306] Fix alignment Just made this change to see if the CI tests would fail again or if the failure was just a one of thing. --- workflows/taxprofiler.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 81ea5d9..1f2e9d1 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -47,7 +47,7 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { INPUT_CHECK } from '../subworkflows/local/input_check' include { DB_CHECK } from '../subworkflows/local/db_check' include { SHORTREAD_PREPROCESSING } from '../subworkflows/local/shortread_preprocessing' From b0faeedc60c654fe445909ceed569ee010c84837 Mon Sep 17 00:00:00 2001 From: Lauri Mesilaakso Date: Fri, 6 May 2022 08:20:31 +0200 Subject: [PATCH 209/306] Update workflows/taxprofiler.nf Co-authored-by: James A. Fellows Yates --- workflows/taxprofiler.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 1f2e9d1..7a6cd09 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -25,7 +25,7 @@ if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_me if (params.perform_shortread_hostremoval && !params.hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --hostremoval_reference FASTA supplied. Check input." } if (!params.hostremoval_reference && params.hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --hostremoval_reference FASTA supplied. Check input." } -if (params.hostremoval_reference ) { ch_reference = file(params.hostremoval_reference) } +if (params.hostremoval_reference ) { ch_reference = file(params.hostremoval_reference) } if (params.shortread_hostremoval_index ) { ch_shortread_reference_index = file(params.shortread_hostremoval_index ) } else { ch_shortread_reference_index = [] } if (params.longread_hostremoval_index ) { ch_longread_reference_index = file(params.longread_hostremoval_index ) } else { ch_longread_reference_index = [] } From 02517733aa2227d1bc6f4decdb607918de047b2d Mon Sep 17 00:00:00 2001 From: Lauri Mesilaakso Date: Fri, 6 May 2022 10:40:59 +0200 Subject: [PATCH 210/306] Update docs/usage.md Co-authored-by: James A. Fellows Yates --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 1143da3..4aa1d09 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -197,7 +197,7 @@ Similarly to complexity filtering, host-removal can be useful for runtime optimi nf-core/taxprofiler currently offers host-removal via alignment against a reference genome with Bowtie2, and the use of the unaligned reads for downstream profiling. -You can supply your reference genome in FASTA format with `--hostremoval_reference`. You can also optionally supply a directory containing pre-indexed Bowtie2 index files with `--shortread_hostremoval_index`, however nf-core/taxprofiler will generate this for you if necessary. Pre-supplying the directory of index files can greatly speed up the process, and these can be re-used. +You can supply your reference genome in FASTA format with `--hostremoval_reference`. You can also optionally supply a directory containing pre-indexed Bowtie2 index files with `--shortread_hostremoval_index` or `--longread_hostremoval_index`, however nf-core/taxprofiler will generate this for you if necessary. Pre-supplying the directory of index files can greatly speed up the process, and these can be re-used. > 💡 If you have multiple taxa or sequences you wish to remove (e.g., the host genome and then also PhiX - common quality-control reagent during sequencing) you can simply concatenate the FASTAs of each taxa or sequences into a single reference file. From 87a1d8051979040f7dd1b6f976b2bd0224a7bf24 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 7 May 2022 05:22:35 +0200 Subject: [PATCH 211/306] Add working output for Kraken2/Centrifuge/DIAMOND --- conf/modules.config | 4 +- conf/test.config | 4 ++ modules.json | 6 +- .../nf-core/modules/diamond/blastx/main.nf | 33 ++++++--- .../nf-core/modules/diamond/blastx/meta.yml | 30 +++++++- .../nf-core/modules/kraken2/kraken2/main.nf | 23 ++++-- .../nf-core/modules/kraken2/kraken2/meta.yml | 25 +++++-- nextflow.config | 14 ++-- nextflow_schema.json | 71 ++++++++++++++----- subworkflows/local/profiling.nf | 34 ++++++--- workflows/taxprofiler.nf | 2 + 11 files changed, 181 insertions(+), 65 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index cd0fb04..31d0fca 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -271,7 +271,7 @@ process { publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: params.publish_dir_mode, - pattern: '*.{txt}' + pattern: '*.{txt,report,fastq.gz}' ] } @@ -289,7 +289,7 @@ process { publishDir = [ path: { "${params.outdir}/centrifuge/${meta.db_name}" }, mode: params.publish_dir_mode, - pattern: '*.txt' + pattern: '*.{txt,sam,gz}' ] ext.args = { "${meta.db_params}" } ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } diff --git a/conf/test.config b/conf/test.config index a5244f9..573db42 100644 --- a/conf/test.config +++ b/conf/test.config @@ -36,6 +36,10 @@ params { run_metaphlan3 = true run_centrifuge = true run_diamond = true + malt_save_reads = true + kraken2_save_reads = true + centrifuge_save_reads = true + diamond_save_reads = true } process { diff --git a/modules.json b/modules.json index a55c88b..5cad32e 100644 --- a/modules.json +++ b/modules.json @@ -28,7 +28,7 @@ "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, "diamond/blastx": { - "git_sha": "42564565b934eeb2449e35ec97ed13ff2a67f1de" + "git_sha": "bd3bfe0817246082525ab93707976676b1fe208b" }, "fastp": { "git_sha": "d0a1cbb703a130c19f6796c3fce24fbe7dfce789" @@ -43,7 +43,7 @@ "git_sha": "538dbac98ba9c8f799536cd5a617195501439457" }, "kraken2/kraken2": { - "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + "git_sha": "abe025677cdd805cc93032341ab19885473c1a07" }, "malt/run": { "git_sha": "72b96f4e504eef673f2b5c13560a9d90b669129b" @@ -80,4 +80,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/diamond/blastx/main.nf b/modules/nf-core/modules/diamond/blastx/main.nf index 6703c1e..d327227 100644 --- a/modules/nf-core/modules/diamond/blastx/main.nf +++ b/modules/nf-core/modules/diamond/blastx/main.nf @@ -2,21 +2,26 @@ process DIAMOND_BLASTX { tag "$meta.id" label 'process_medium' - // Dimaond is limited to v2.0.9 because there is not a - // singularity version higher than this at the current time. - conda (params.enable_conda ? "bioconda::diamond=2.0.9" : null) + conda (params.enable_conda ? "bioconda::diamond=2.0.15" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/diamond:2.0.9--hdcc8f71_0' : - 'quay.io/biocontainers/diamond:2.0.9--hdcc8f71_0' }" + 'https://depot.galaxyproject.org/singularity/diamond:2.0.15--hb97b32f_0' : + 'quay.io/biocontainers/diamond:2.0.15--hb97b32f_0' }" input: tuple val(meta), path(fasta) path db - val outext + val out_ext + val blast_columns output: - tuple val(meta), path('*.{blast,xml,txt,daa,sam,tsv,paf}'), emit: output - path "versions.yml" , emit: versions + tuple val(meta), path('*.blast'), optional: true, emit: blast + tuple val(meta), path('*.xml') , optional: true, emit: xml + tuple val(meta), path('*.txt') , optional: true, emit: txt + tuple val(meta), path('*.daa') , optional: true, emit: daa + tuple val(meta), path('*.sam') , optional: true, emit: sam + tuple val(meta), path('*.tsv') , optional: true, emit: tsv + tuple val(meta), path('*.paf') , optional: true, emit: paf + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -24,7 +29,8 @@ process DIAMOND_BLASTX { script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - switch ( outext ) { + def columns = blast_columns ? "${blast_columns}" : '' + switch ( out_ext ) { case "blast": outfmt = 0; break case "xml": outfmt = 5; break case "txt": outfmt = 6; break @@ -32,6 +38,11 @@ process DIAMOND_BLASTX { case "sam": outfmt = 101; break case "tsv": outfmt = 102; break case "paf": outfmt = 103; break + default: + outfmt = '6'; + out_ext = 'txt'; + log.warn("Unknown output file format provided (${out_ext}): selecting DIAMOND default of tabular BLAST output (txt)"); + break } """ DB=`find -L ./ -name "*.dmnd" | sed 's/.dmnd//'` @@ -41,9 +52,9 @@ process DIAMOND_BLASTX { --threads $task.cpus \\ --db \$DB \\ --query $fasta \\ - --outfmt ${outfmt} \\ + --outfmt ${outfmt} ${columns} \\ $args \\ - --out ${prefix}.${outext} + --out ${prefix}.${out_ext} cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/diamond/blastx/meta.yml b/modules/nf-core/modules/diamond/blastx/meta.yml index 5ee2d55..2dcd7bc 100644 --- a/modules/nf-core/modules/diamond/blastx/meta.yml +++ b/modules/nf-core/modules/diamond/blastx/meta.yml @@ -28,7 +28,7 @@ input: type: directory description: Directory containing the nucelotide blast database pattern: "*" - - outext: + - out_ext: type: string description: | Specify the type of output file to be generated. `blast` corresponds to @@ -38,10 +38,34 @@ input: pattern: "blast|xml|txt|daa|sam|tsv|paf" output: + - blast: + type: file + description: File containing blastp hits + pattern: "*.{blast}" + - xml: + type: file + description: File containing blastp hits + pattern: "*.{xml}" - txt: type: file - description: File containing blastx hits - pattern: "*.{blastx.txt}" + description: File containing hits in tabular BLAST format. + pattern: "*.{txt}" + - daa: + type: file + description: File containing hits DAA format + pattern: "*.{daa}" + - sam: + type: file + description: File containing aligned reads in SAM format + pattern: "*.{sam}" + - tsv: + type: file + description: Tab separated file containing taxonomic classification of hits + pattern: "*.{tsv}" + - paf: + type: file + description: File containing aligned reads in pairwise mapping format format + pattern: "*.{paf}" - versions: type: file description: File containing software versions diff --git a/modules/nf-core/modules/kraken2/kraken2/main.nf b/modules/nf-core/modules/kraken2/kraken2/main.nf index 3ec5df5..d400023 100644 --- a/modules/nf-core/modules/kraken2/kraken2/main.nf +++ b/modules/nf-core/modules/kraken2/kraken2/main.nf @@ -10,12 +10,15 @@ process KRAKEN2_KRAKEN2 { input: tuple val(meta), path(reads) path db + val save_output_fastqs + val save_reads_assignment output: - tuple val(meta), path('*classified*') , emit: classified - tuple val(meta), path('*unclassified*'), emit: unclassified - tuple val(meta), path('*report.txt') , emit: txt - path "versions.yml" , emit: versions + tuple val(meta), path('*classified*') , optional:true, emit: classified_reads_fastq + tuple val(meta), path('*unclassified*') , optional:true, emit: unclassified_reads_fastq + tuple val(meta), path('*classifiedreads*'), optional:true, emit: classified_reads_assignment + tuple val(meta), path('*report.txt') , emit: report + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -26,19 +29,25 @@ process KRAKEN2_KRAKEN2 { def paired = meta.single_end ? "" : "--paired" def classified = meta.single_end ? "${prefix}.classified.fastq" : "${prefix}.classified#.fastq" def unclassified = meta.single_end ? "${prefix}.unclassified.fastq" : "${prefix}.unclassified#.fastq" + def classified_command = save_output_fastqs ? "--classified-out ${classified}" : "" + def unclassified_command = save_output_fastqs ? "--unclassified-out ${unclassified}" : "" + def readclassification_command = save_reads_assignment ? "--output ${prefix}.kraken2.classifiedreads.txt" : "" + def compress_reads_command = save_output_fastqs ? "pigz -p $task.cpus *.fastq" : "" + """ kraken2 \\ --db $db \\ --threads $task.cpus \\ - --unclassified-out $unclassified \\ - --classified-out $classified \\ --report ${prefix}.kraken2.report.txt \\ --gzip-compressed \\ + $unclassified_command \\ + $classified_command \\ + $readclassification_command \\ $paired \\ $args \\ $reads - pigz -p $task.cpus *.fastq + $compress_reads_command cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/kraken2/kraken2/meta.yml b/modules/nf-core/modules/kraken2/kraken2/meta.yml index 9d6a385..7129fe3 100644 --- a/modules/nf-core/modules/kraken2/kraken2/meta.yml +++ b/modules/nf-core/modules/kraken2/kraken2/meta.yml @@ -27,25 +27,40 @@ input: - db: type: directory description: Kraken2 database + - save_output_fastqs: + type: boolean + description: | + If true, optional commands are added to save classified and unclassified reads + as fastq files + - save_reads_assignment: + type: boolean + description: | + If true, an optional command is added to save a file reporting the taxonomic + classification of each input read output: - meta: type: map description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - classified: + - classified_reads_fastq: type: file description: | - Reads classified to belong to any of the taxa + Reads classified as belonging to any of the taxa on the Kraken2 database. pattern: "*{fastq.gz}" - - unclassified: + - unclassified_reads_fastq: type: file description: | - Reads not classified to belong to any of the taxa + Reads not classified to any of the taxa on the Kraken2 database. pattern: "*{fastq.gz}" - - txt: + - classified_reads_assignment: + type: file + description: | + Kraken2 output file indicating the taxonomic assignment of + each input read + - report: type: file description: | Kraken2 report containing stats about classified diff --git a/nextflow.config b/nextflow.config index ca9e280..3f76d53 100644 --- a/nextflow.config +++ b/nextflow.config @@ -94,16 +94,17 @@ params { // MALT run_malt = false malt_mode = 'BlastN' - malt_generatemegansummary = false + malt_generate_megansummary = false + malt_save_reads = false // kraken2 - run_kraken2 = false + run_kraken2 = false + kraken2_save_reads = false + kraken2_save_readclassification = false // centrifuge run_centrifuge = false - centrifuge_save_unaligned = false - centrifuge_save_aligned = false - centrifuge_sam_format = false + centrifuge_save_reads = false // metaphlan3 run_metaphlan3 = false @@ -114,7 +115,8 @@ params { // diamond run_diamond = false - diamond_output_format = 'txt' + diamond_output_format = 'tsv' // TSV is only format with taxonomic information apparently + diamond_save_reads = false // this will override diamound output format so no taxonomic profile is generated! } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index ab2108e..bb4b759 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -278,15 +288,6 @@ "run_centrifuge": { "type": "boolean" }, - "centrifuge_save_unaligned": { - "type": "boolean" - }, - "centrifuge_save_aligned": { - "type": "boolean" - }, - "centrifuge_sam_format": { - "type": "boolean" - }, "run_metaphlan3": { "type": "boolean", "description": "Enable MetaPhlAn for taxonomic profiling" @@ -294,7 +295,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -335,7 +339,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -385,13 +392,20 @@ "run_kaiju": { "type": "boolean" }, - "malt_generatemegansummary": { + "malt_generate_megansummary": { "type": "boolean" }, "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": ["phylum", "class", "order", "family", "genus", "species"] + "enum": [ + "phylum", + "class", + "order", + "family", + "genus", + "species" + ] }, "run_diamond": { "type": "boolean" @@ -399,11 +413,34 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] + "enum": [ + "blast", + "xml", + "txt", + "daa", + "sam", + "tsv", + "paf" + ] }, "longread_hostremoval_index": { "type": "string", "default": "None" + }, + "malt_save_reads": { + "type": "boolean" + }, + "kraken2_save_reads": { + "type": "boolean" + }, + "kraken2_save_readclassification": { + "type": "boolean" + }, + "centrifuge_save_reads": { + "type": "boolean" + }, + "diamond_save_reads": { + "type": "boolean" } } -} +} \ No newline at end of file diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 7fb3ce9..18ea7fa 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -65,10 +65,18 @@ workflow PROFILING { ch_input_for_malt = ch_input_for_profiling.malt .filter { it[0]['instrument_platform'] == 'ILLUMINA' } .map { - it -> - def temp_meta = [ id: it[2]['db_name']] + it[2] - def db = it[3] - [ temp_meta, it[1], db ] + meta, reads, db_meta, db -> + def sam_format = params.malt_save_reads ? ' --alignments' : "" + // TODO No MALT SAM? + // TODO check all aligned reads published + // TODO try turning on/off aligned reads + // TODO wut? [9a/a441d6] Submitted process > NFCORE_TAXPROFILER:TAXPROFILER:PROFILING:MALT_RUN (null) + def temp_meta = [ id: meta['db_name'] ] + def new_db_meta = db_meta.clone() + new_db_meta['db_params'] = db_meta['db_params'] + sam_format + def new_meta = temp_meta + new_db_meta + + [ new_meta, reads, db ] } .groupTuple(by: [0,2]) .multiMap { @@ -92,7 +100,7 @@ workflow PROFILING { [ meta_new, rma ] } - MEGAN_RMA2INFO (ch_maltrun_for_megan, params.malt_generatemegansummary ) + MEGAN_RMA2INFO (ch_maltrun_for_megan, params.malt_generate_megansummary ) ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( MALT_RUN.out.versions.first(), MEGAN_RMA2INFO.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) @@ -108,10 +116,10 @@ workflow PROFILING { db: it[3] } - KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db ) - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.txt.collect{it[1]}.ifEmpty([]) ) + KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db, params.kraken2_save_reads, params.kraken2_save_readclassification ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) - ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.txt ) + ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.report ) } @@ -128,7 +136,7 @@ workflow PROFILING { db: it[3] } - CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_unaligned, params.centrifuge_save_aligned, params.centrifuge_sam_format ) + CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_reads, params.centrifuge_save_reads, params.centrifuge_save_reads ) CENTRIFUGE_KREPORT (CENTRIFUGE_CENTRIFUGE.out.results, ch_input_for_centrifuge.db) ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_KREPORT.out.kreport ) @@ -180,9 +188,13 @@ workflow PROFILING { db: it[3] } - DIAMOND_BLASTX ( ch_input_for_diamond.reads, ch_input_for_diamond.db, params.diamond_output_format ) + // diamond only accepts single output file specification, therefore + // this will replace output file! + ch_diamond_reads_format = params.diamond_save_reads ? 'sam' : params.diamond_output_format + + DIAMOND_BLASTX ( ch_input_for_diamond.reads, ch_input_for_diamond.db, params.diamond_output_format, [] ) ch_versions = ch_versions.mix( DIAMOND_BLASTX.out.versions.first() ) - ch_raw_profiles = ch_raw_profiles.mix( DIAMOND_BLASTX.out.output ) + ch_raw_profiles = ch_raw_profiles.mix( DIAMOND_BLASTX.out.tsv ) } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 7a6cd09..c319296 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -29,6 +29,8 @@ if (params.hostremoval_reference ) { ch_reference = file(params.hostre if (params.shortread_hostremoval_index ) { ch_shortread_reference_index = file(params.shortread_hostremoval_index ) } else { ch_shortread_reference_index = [] } if (params.longread_hostremoval_index ) { ch_longread_reference_index = file(params.longread_hostremoval_index ) } else { ch_longread_reference_index = [] } +if (params.diamond_save_reads ) log.warn "[nf-core/taxprofiler] DIAMOND only allows output of a single format. Only aligned reads in SAM format will be produced, no taxonomic profiles will be available." + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIG FILES From 47a5ae0cff4060c12451beba47502dae5dbb9d17 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 7 May 2022 06:09:05 +0200 Subject: [PATCH 212/306] Add FASTP complexity option --- conf/modules.config | 6 ++- conf/test.config | 1 + conf/test_nopreprocessing.config | 46 ++++++++++++++++ conf/test_noprofiling.config | 46 ++++++++++++++++ docs/usage.md | 4 +- nextflow.config | 3 ++ nextflow_schema.json | 52 ++++++++++++++++--- .../local/shortread_complexityfiltering.nf | 1 + workflows/taxprofiler.nf | 8 ++- 9 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 conf/test_nopreprocessing.config create mode 100644 conf/test_noprofiling.config diff --git a/conf/modules.config b/conf/modules.config index cd0fb04..c834f4e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -54,7 +54,8 @@ process { params.shortread_clipmerge_skipadaptertrim ? "--disable_adapter_trimming" : "", params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", // filtering options - "--length_required ${params.shortread_clipmerge_minlength}" + "--length_required ${params.shortread_clipmerge_minlength}", + params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp' ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -74,7 +75,8 @@ process { params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", params.shortread_clipmerge_adapter2 ? "--adapter_sequence_r2 ${params.shortread_clipmerge_adapter2}" : "--detect_adapter_for_pe", // filtering options - "--length_required ${params.shortread_clipmerge_minlength}" + "--length_required ${params.shortread_clipmerge_minlength}", + params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp' ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ diff --git a/conf/test.config b/conf/test.config index a5244f9..c687a86 100644 --- a/conf/test.config +++ b/conf/test.config @@ -29,6 +29,7 @@ params { perform_shortread_complexityfilter = true perform_shortread_hostremoval = true perform_longread_hostremoval = true + perform_runmerging = true hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' run_kaiju = true run_kraken2 = true diff --git a/conf/test_nopreprocessing.config b/conf/test_nopreprocessing.config new file mode 100644 index 0000000..e8d4ed9 --- /dev/null +++ b/conf/test_nopreprocessing.config @@ -0,0 +1,46 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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/taxprofiler -profile test, --outdir + +---------------------------------------------------------------------------------------- +*/ + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset skipping all preprocessing 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 + // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets + // TODO nf-core: Give any required params for the test so that command line flags are not needed + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' + perform_shortread_clipmerge = false + perform_longread_clip = false + perform_shortread_complexityfilter = false + perform_shortread_hostremoval = false + perform_longread_hostremoval = false + perform_runmerging = false + hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' + run_kaiju = true + run_kraken2 = true + run_malt = true + run_metaphlan3 = true + run_centrifuge = true + run_diamond = true +} + +process { + withName: MALT_RUN { + maxForks = 1 + } +} diff --git a/conf/test_noprofiling.config b/conf/test_noprofiling.config new file mode 100644 index 0000000..f908651 --- /dev/null +++ b/conf/test_noprofiling.config @@ -0,0 +1,46 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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/taxprofiler -profile test, --outdir + +---------------------------------------------------------------------------------------- +*/ + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset without performing any profiling 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 + // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets + // TODO nf-core: Give any required params for the test so that command line flags are not needed + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' + perform_shortread_clipmerge = true + perform_longread_clip = true + perform_shortread_complexityfilter = true + perform_shortread_hostremoval = true + perform_longread_hostremoval = true + perform_runmerging = true + hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' + run_kaiju = false + run_kraken2 = false + run_malt = false + run_metaphlan3 = false + run_centrifuge = false + run_diamond = false +} + +process { + withName: MALT_RUN { + maxForks = 1 + } +} diff --git a/docs/usage.md b/docs/usage.md index 4aa1d09..47ac952 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -183,11 +183,11 @@ Complexity filtering can be activated via the `--perform_shortread_complexityfil Complexity filtering is primarily a run-time optimisation step. It is not necessary for accurate taxonomic profiling, however it can speed up run-time of each tool by removing reads with low-diversity of nucleotides (e.g. with mono-nucleotide - `AAAAAAAA`, or di-nucleotide repeats `GAGAGAGAGAGAGAG`) that have a low-chance of giving an informative taxonomic ID as they can be associated with many different taxa. Removing these reads therefore saves computational time and resources. -There are currently two options for short-read complexity filtering: [`bbduk`](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/bb-tools-user-guide/bbduk-guide/) and [`prinseq++`](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/bb-tools-user-guide/bbduk-guide/). +There are currently three options for short-read complexity filtering: [`bbduk`](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/bb-tools-user-guide/bbduk-guide/), [`prinseq++`](https://github.com/Adrian-Cantu/PRINSEQ-plus-plus), and [`fastp`](https://github.com/OpenGene/fastp#low-complexity-filter). The tools offer different algorithms and parameters for removing low complexity reads. We therefore recommend reviewing the pipeline's [parameter documentation](https://nf-co.re/taxprofiler/parameters) and the documentation of both tools (see links above) to decide on optimal methods and parameters for your dataset. -You can optionally save the FASTQ output of the run merging with the `--save_complexityfiltered_reads`. +You can optionally save the FASTQ output of the run merging with the `--save_complexityfiltered_reads`. If running with `fastp`, complexity filtering happens inclusively within the earlier shortread preprocessing step. Therefore there will not be an independent pipeline step for complexity filtering, and no independent FASTQ file (i.e. `--save_complexityfiltered_reads` will be ignored) - your complexity filtered reads will also be in the `fastp/` folder in the same file(s) as the preprocessed read. #### Host Removal diff --git a/nextflow.config b/nextflow.config index ca9e280..411e7a6 100644 --- a/nextflow.config +++ b/nextflow.config @@ -74,6 +74,7 @@ params { shortread_complexityfilter_bbduk_mask = false shortread_complexityfilter_prinseqplusplus_mode = 'entropy' shortread_complexityfilter_prinseqplusplus_dustscore = 0.5 + shortread_complexityfilter_fastp_threshold = 30 save_complexityfiltered_reads = false // run merging @@ -185,6 +186,8 @@ profiles { } test { includeConfig 'conf/test.config' } test_full { includeConfig 'conf/test_full.config' } + test_noprofiling { includeConfig 'conf/test_noprofiling.config' } + test_nopreprocessing { includeConfig 'conf/test_preprocessing.config' } } // Load igenomes.config if required diff --git a/nextflow_schema.json b/nextflow_schema.json index ab2108e..a0a830c 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -173,7 +176,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -294,7 +304,10 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -319,7 +332,12 @@ }, "shortread_complexityfilter_tool": { "type": "string", - "default": "bbduk" + "default": "bbduk", + "enum": [ + "bbduk", + "prinseqplusplus", + "fastp" + ] }, "shortread_complexityfilter_bbduk_windowsize": { "type": "integer", @@ -335,7 +353,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -391,7 +412,14 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": ["phylum", "class", "order", "family", "genus", "species"] + "enum": [ + "phylum", + "class", + "order", + "family", + "genus", + "species" + ] }, "run_diamond": { "type": "boolean" @@ -399,11 +427,19 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] + "enum": [ + "blast", + "xml", + "txt", + "daa", + "sam", + "tsv", + "paf" + ] }, "longread_hostremoval_index": { "type": "string", "default": "None" } } -} +} \ No newline at end of file diff --git a/subworkflows/local/shortread_complexityfiltering.nf b/subworkflows/local/shortread_complexityfiltering.nf index 12686d7..a34440d 100644 --- a/subworkflows/local/shortread_complexityfiltering.nf +++ b/subworkflows/local/shortread_complexityfiltering.nf @@ -13,6 +13,7 @@ workflow SHORTREAD_COMPLEXITYFILTERING { ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() + // fastp complexity filtering is activated via modules.conf in shortread_preprocessing if ( params.shortread_complexityfilter_tool == 'bbduk' ) { ch_filtered_reads = BBMAP_BBDUK ( reads, [] ).reads ch_versions = ch_versions.mix( BBMAP_BBDUK.out.versions.first() ) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 7a6cd09..b8b953b 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -19,9 +19,12 @@ 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 samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } + if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" +if (params.shortread_complexityfilter_tool == 'fastp' && ( params.perform_shortread_clipmerge == false || params.shortread_clipmerge_tool != 'fastp' )) exit 1, "ERROR: [nf-core/taxprofiler] cannot use fastp complexity filtering if preprocessing not turned on and/or tool is not fastp. Please specify --perform_shortread_clipmerge and/or --shortread_clipmerge_tool 'fastp'" + if (params.perform_shortread_hostremoval && !params.hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --hostremoval_reference FASTA supplied. Check input." } if (!params.hostremoval_reference && params.hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --hostremoval_reference FASTA supplied. Check input." } @@ -131,7 +134,8 @@ workflow TAXPROFILER { SUBWORKFLOW: COMPLEXITY FILTERING */ - if ( params.perform_shortread_complexityfilter ) { + // fastp complexity filtering is activated via modules.conf in shortread_preprocessing + if ( params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool != 'fastp' ) { ch_shortreads_filtered = SHORTREAD_COMPLEXITYFILTERING ( ch_shortreads_preprocessed ).reads ch_versions = ch_versions.mix( SHORTREAD_COMPLEXITYFILTERING.out.versions ) } else { @@ -228,7 +232,7 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix( LONGREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) } - if (params.perform_shortread_complexityfilter){ + if (params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool != 'fastp'){ ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_COMPLEXITYFILTERING.out.mqc.collect{it[1]}.ifEmpty([]) ) } From 0f651873dd711c2223a8f844a091deb1316f6979 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 7 May 2022 06:12:24 +0200 Subject: [PATCH 213/306] Linting --- conf/modules.config | 4 ++-- nextflow_schema.json | 55 ++++++++++---------------------------------- 2 files changed, 14 insertions(+), 45 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index c834f4e..0abff92 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -55,7 +55,7 @@ process { params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", // filtering options "--length_required ${params.shortread_clipmerge_minlength}", - params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp' ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' + params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp' ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ @@ -76,7 +76,7 @@ process { params.shortread_clipmerge_adapter2 ? "--adapter_sequence_r2 ${params.shortread_clipmerge_adapter2}" : "--detect_adapter_for_pe", // filtering options "--length_required ${params.shortread_clipmerge_minlength}", - params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp' ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' + params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp' ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ diff --git a/nextflow_schema.json b/nextflow_schema.json index a0a830c..74fab27 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -304,10 +294,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -333,11 +320,7 @@ "shortread_complexityfilter_tool": { "type": "string", "default": "bbduk", - "enum": [ - "bbduk", - "prinseqplusplus", - "fastp" - ] + "enum": ["bbduk", "prinseqplusplus", "fastp"] }, "shortread_complexityfilter_bbduk_windowsize": { "type": "integer", @@ -353,10 +336,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -412,14 +392,7 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": [ - "phylum", - "class", - "order", - "family", - "genus", - "species" - ] + "enum": ["phylum", "class", "order", "family", "genus", "species"] }, "run_diamond": { "type": "boolean" @@ -427,19 +400,15 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": [ - "blast", - "xml", - "txt", - "daa", - "sam", - "tsv", - "paf" - ] + "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] }, "longread_hostremoval_index": { "type": "string", "default": "None" + }, + "shortread_complexityfilter_fastp_threshold": { + "type": "integer", + "default": 30 } } -} \ No newline at end of file +} From 3fa2181f498f5e1c994efb06be3a63f459dcf9cc Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Sat, 7 May 2022 06:15:15 +0200 Subject: [PATCH 214/306] Fix prinseq tool selection --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1ece72..7bb2076 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs --shortread_clipmerge_excludeunmerged" - "--shortread_clipmerge_tool adapterremoval --shortread_clipmerge_mergepairs" - "--shortread_complexityfilter_tool bbduk" - - "--shortread_complexityfilter_tool prinseq" + - "--shortread_complexityfilter_tool prinseqplusplus" - "--perform_runmerging" - "--perform_runmerging --shortread_clipmerge_mergepairs" - "--shortread_complexityfilter false --perform_shortread_hostremoval" From d67543503b927f6af5bce9574ad8b428c53c0c46 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Sat, 7 May 2022 13:15:21 +0200 Subject: [PATCH 215/306] Apply suggestions from code review Co-authored-by: Moritz E. Beber --- conf/modules.config | 2 +- docs/usage.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 0abff92..5d8398e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -55,7 +55,7 @@ process { params.shortread_clipmerge_adapter1 ? "--adapter_sequence ${params.shortread_clipmerge_adapter1}" : "", // filtering options "--length_required ${params.shortread_clipmerge_minlength}", - params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp' ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' + (params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp') ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } publishDir = [ diff --git a/docs/usage.md b/docs/usage.md index 47ac952..54ffce0 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -185,7 +185,7 @@ Complexity filtering is primarily a run-time optimisation step. It is not necess There are currently three options for short-read complexity filtering: [`bbduk`](https://jgi.doe.gov/data-and-tools/software-tools/bbtools/bb-tools-user-guide/bbduk-guide/), [`prinseq++`](https://github.com/Adrian-Cantu/PRINSEQ-plus-plus), and [`fastp`](https://github.com/OpenGene/fastp#low-complexity-filter). -The tools offer different algorithms and parameters for removing low complexity reads. We therefore recommend reviewing the pipeline's [parameter documentation](https://nf-co.re/taxprofiler/parameters) and the documentation of both tools (see links above) to decide on optimal methods and parameters for your dataset. +The tools offer different algorithms and parameters for removing low complexity reads. We therefore recommend reviewing the pipeline's [parameter documentation](https://nf-co.re/taxprofiler/parameters) and the documentation of the tools (see links above) to decide on optimal methods and parameters for your dataset. You can optionally save the FASTQ output of the run merging with the `--save_complexityfiltered_reads`. If running with `fastp`, complexity filtering happens inclusively within the earlier shortread preprocessing step. Therefore there will not be an independent pipeline step for complexity filtering, and no independent FASTQ file (i.e. `--save_complexityfiltered_reads` will be ignored) - your complexity filtered reads will also be in the `fastp/` folder in the same file(s) as the preprocessed read. From aca7bc439dac189b8f4b33e7014859f7528d80bf Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Thu, 12 May 2022 08:08:14 +0100 Subject: [PATCH 216/306] Update nextflow.config --- nextflow.config | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nextflow.config b/nextflow.config index 411e7a6..4bcb1b1 100644 --- a/nextflow.config +++ b/nextflow.config @@ -130,11 +130,11 @@ try { // Load nf-core/taxprofiler custom profiles from different institutions. // Warning: Uncomment only if a pipeline-specific instititutional config already exists on nf-core/configs! -// try { -// includeConfig "${params.custom_config_base}/pipeline/taxprofiler.config" -// } catch (Exception e) { -// System.err.println("WARNING: Could not load nf-core/config/taxprofiler profiles: ${params.custom_config_base}/pipeline/taxprofiler.config") -// } +try { + includeConfig "${params.custom_config_base}/pipeline/taxprofiler.config" +} catch (Exception e) { + System.err.println("WARNING: Could not load nf-core/config/taxprofiler profiles: ${params.custom_config_base}/pipeline/taxprofiler.config") +} profiles { From 68d0cc00faaa50c45ec77a25158f3749d22d4f95 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Mon, 23 May 2022 08:05:06 -0400 Subject: [PATCH 217/306] add motus profile. --- conf/test.config | 1 + conf/test_nopreprocessing.config | 1 + conf/test_noprofiling.config | 1 + modules.json | 8 ++- .../nf-core/modules/motus/downloaddb/main.nf | 39 ++++++++++++ .../nf-core/modules/motus/downloaddb/meta.yml | 39 ++++++++++++ modules/nf-core/modules/motus/profile/main.nf | 54 ++++++++++++++++ .../nf-core/modules/motus/profile/meta.yml | 61 +++++++++++++++++++ nextflow.config | 5 ++ nextflow_schema.json | 11 ++++ .../execution_trace_2022-05-21_11-05-12.txt | 1 + subworkflows/local/db_check.nf | 21 ++++++- subworkflows/local/profiling.nf | 19 +++++- 13 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 modules/nf-core/modules/motus/downloaddb/main.nf create mode 100644 modules/nf-core/modules/motus/downloaddb/meta.yml create mode 100644 modules/nf-core/modules/motus/profile/main.nf create mode 100644 modules/nf-core/modules/motus/profile/meta.yml create mode 100644 null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt diff --git a/conf/test.config b/conf/test.config index c687a86..cf983ab 100644 --- a/conf/test.config +++ b/conf/test.config @@ -37,6 +37,7 @@ params { run_metaphlan3 = true run_centrifuge = true run_diamond = true + run_motus = true } process { diff --git a/conf/test_nopreprocessing.config b/conf/test_nopreprocessing.config index e8d4ed9..7658a2d 100644 --- a/conf/test_nopreprocessing.config +++ b/conf/test_nopreprocessing.config @@ -37,6 +37,7 @@ params { run_metaphlan3 = true run_centrifuge = true run_diamond = true + run_motus = true } process { diff --git a/conf/test_noprofiling.config b/conf/test_noprofiling.config index f908651..dffb44e 100644 --- a/conf/test_noprofiling.config +++ b/conf/test_noprofiling.config @@ -37,6 +37,7 @@ params { run_metaphlan3 = false run_centrifuge = false run_diamond = false + run_motus = false } process { diff --git a/modules.json b/modules.json index a55c88b..2fc7203 100644 --- a/modules.json +++ b/modules.json @@ -60,6 +60,12 @@ "minimap2/index": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, + "motus/downloaddb": { + "git_sha": "6393a085c5fcea11963774c041808df169907487" + }, + "motus/profile": { + "git_sha": "6b960f0e75bbb4d5bd301cd3875fa078d0eab4d1" + }, "multiqc": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, @@ -80,4 +86,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/modules/motus/downloaddb/main.nf b/modules/nf-core/modules/motus/downloaddb/main.nf new file mode 100644 index 0000000..317624b --- /dev/null +++ b/modules/nf-core/modules/motus/downloaddb/main.nf @@ -0,0 +1,39 @@ +process MOTUS_DOWNLOADDB { + label 'process_low' + + conda (params.enable_conda ? "bioconda::motus=3.0.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/motus:3.0.1--pyhdfd78af_0': + 'quay.io/biocontainers/motus:3.0.1--pyhdfd78af_0' }" + + input: + path motus_downloaddb_script + + output: + path "db_mOTU/" , emit: db + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def software = "${motus_downloaddb_script.simpleName}_copy.py" + """ + ## must copy script file to working directory, + ## otherwise the reference_db will be download to bin folder + ## other than current directory + cp $motus_downloaddb_script ${software} + python ${software} \\ + $args \\ + -t $task.cpus + + ## mOTUs version number is not available from command line. + ## mOTUs save the version number in index database folder. + ## mOTUs will check the database version is same version as exec version. + cat <<-END_VERSIONS > versions.yml + "${task.process}": + mOTUs: \$(grep motus db_mOTU/db_mOTU_versions | sed 's/motus\\t//g') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/motus/downloaddb/meta.yml b/modules/nf-core/modules/motus/downloaddb/meta.yml new file mode 100644 index 0000000..64df5ee --- /dev/null +++ b/modules/nf-core/modules/motus/downloaddb/meta.yml @@ -0,0 +1,39 @@ +name: "motus_downloaddb" +description: Download the mOTUs database +keywords: + - classify + - metagenomics + - fastq + - taxonomic profiling + - database + - download +tools: + - "motus": + description: "The mOTU profiler is a computational tool that estimates relative taxonomic abundance of known and currently unknown microbial community members using metagenomic shotgun sequencing data." + homepage: "None" + documentation: "https://github.com/motu-tool/mOTUs/wiki" + tool_dev_url: "https://github.com/motu-tool/mOTUs" + doi: "10.1038/s41467-019-08844-4" + licence: "['GPL v3']" + +input: + - motus_downloaddb: + type: directory + description: | + The mOTUs downloadDB script source file. + It is the source file installed or + remote source in github such as https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py + pattern: "downloadDB.py" + +output: + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - db: + type: directory + description: The mOTUs database directory + pattern: "db_mOTU" + +authors: + - "@jianhong" diff --git a/modules/nf-core/modules/motus/profile/main.nf b/modules/nf-core/modules/motus/profile/main.nf new file mode 100644 index 0000000..6a1acd3 --- /dev/null +++ b/modules/nf-core/modules/motus/profile/main.nf @@ -0,0 +1,54 @@ +process MOTUS_PROFILE { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? "bioconda::motus=3.0.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/motus:3.0.1--pyhdfd78af_0': + 'quay.io/biocontainers/motus:3.0.1--pyhdfd78af_0' }" + + input: + tuple val(meta), path(reads) + path db + + output: + tuple val(meta), path("*.out"), emit: out + tuple val(meta), path("*.bam"), optional: true, emit: bam + tuple val(meta), path("*.mgc"), optional: true, emit: mgc + 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}" + def inputs = reads[0].getExtension() == 'bam' ? + "-i ${reads}" : + reads[0].getExtension() == 'mgc' ? "-m $reads" : + meta.single_end ? + "-s $reads" : "-f ${reads[0]} -r ${reads[1]}" + def refdb = db ? "-db ${db}" : "" + """ + motus profile \\ + $args \\ + $inputs \\ + $refdb \\ + -t $task.cpus \\ + -n $prefix \\ + -o ${prefix}.out + + ## mOTUs version number is not available from command line. + ## mOTUs save the version number in index database folder. + ## mOTUs will check the database version is same version as exec version. + if [ "$db" == "" ]; then + VERSION=\$(echo \$(motus -h 2>&1) | sed 's/^.*Version: //; s/References.*\$//') + else + VERSION=\$(grep motus $db/db_mOTU_versions | sed 's/motus\\t//g') + fi + cat <<-END_VERSIONS > versions.yml + "${task.process}": + mOTUs: \$VERSION + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/motus/profile/meta.yml b/modules/nf-core/modules/motus/profile/meta.yml new file mode 100644 index 0000000..19803bd --- /dev/null +++ b/modules/nf-core/modules/motus/profile/meta.yml @@ -0,0 +1,61 @@ +name: "motus_profile" +description: Taxonomic meta-omics profiling using universal marker genes +keywords: + - classify + - metagenomics + - fastq + - taxonomic profiling +tools: + - "motus": + description: "Marker gene-based OTU (mOTU) profiling" + homepage: "https://motu-tool.org/" + documentation: "https://github.com/motu-tool/mOTUs/wiki" + tool_dev_url: "https://github.com/motu-tool/mOTUs" + doi: "10.1038/s41467-019-08844-4" + licence: "['GPL v3']" + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input fastq/fasta files of size 1 and 2 for single-end and paired-end data, + respectively. + Or the intermediate bam file mapped by bwa to the mOTUs database or + the output bam file from motus profile. + Or the intermediate mgc read counts table. + pattern: "*.{fastq,fq,fasta,fa,fastq.gz,fq.gz,fasta.gz,fa.gz,.bam,.mgc}" + - db: + type: directory + description: | + mOTUs database downloaded by `motus downloadDB` + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - out: + type: file + description: Results with taxonomic classification of each read + pattern: "*.out" + - bam: + type: file + description: Optional intermediate sorted BAM file from BWA + pattern: "*.{bam}" + - mgc: + type: file + description: Optional intermediate mgc read count table file saved with `-M`. + pattern: "*.{mgc}" + +authors: + - "@jianhong" diff --git a/nextflow.config b/nextflow.config index 411e7a6..6c39ccd 100644 --- a/nextflow.config +++ b/nextflow.config @@ -116,6 +116,11 @@ params { // diamond run_diamond = false diamond_output_format = 'txt' + + // mOTUs + run_motus = false + download_motus_db = true + motus_downloaddb_script = 'https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py' } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index 74fab27..198a937 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -409,6 +409,17 @@ "shortread_complexityfilter_fastp_threshold": { "type": "integer", "default": 30 + }, + "run_motus": { + "type": "boolean" + }, + "download_motus_db": { + "type": "boolean" + }, + "motus_downloaddb_script": { + "type": "string", + "default": "https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py", + "description": "mOTUs database download script path." } } } diff --git a/null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt b/null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt new file mode 100644 index 0000000..6b739ac --- /dev/null +++ b/null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt @@ -0,0 +1 @@ +task_id hash native_id name status exit submit duration realtime %cpu peak_rss peak_vmem rchar wchar diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index f3464d5..95eeefc 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -2,8 +2,9 @@ // Check input samplesheet and get read channels // -include { DATABASE_CHECK } from '../../modules/local/database_check' -include { UNTAR } from '../../modules/nf-core/modules/untar/main' +include { DATABASE_CHECK } from '../../modules/local/database_check' +include { UNTAR } from '../../modules/nf-core/modules/untar/main' +include { MOTUS_DOWNLOADDB } from '../../modules/nf-core/modules/motus/downloaddb/main' workflow DB_CHECK { take: @@ -20,6 +21,22 @@ workflow DB_CHECK { .splitCsv ( header:true, sep:',' ) .map { create_db_channels(it) } + // Download database for mOTUs + if( params.run_motus ){ + check_motus_db = + parsed_samplesheet.filter{ it[0].tool == "motus" } + .ifEmpty{[]} + if( params.download_motus_db ){ + MOTUS_DOWNLOADDB( params.motus_downloaddb_script ) + check_motus_db = MOTUS_DOWNLOADDB.out.db + .map{[ + [tool: "motus", db_name: "db_mOTU", db_params: ''], + it + ]} + } + parsed_samplesheet = parsed_samplesheet.mix(check_motus_db) + } + ch_dbs_for_untar = parsed_samplesheet .branch { untar: it[1].toString().endsWith(".tar.gz") diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 7fb3ce9..109598d 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -11,7 +11,7 @@ include { METAPHLAN3 } from '../../modules/nf-core/modules/meta include { KAIJU_KAIJU } from '../../modules/nf-core/modules/kaiju/kaiju/main' include { KAIJU_KAIJU2TABLE } from '../../modules/nf-core/modules/kaiju/kaiju2table/main' include { DIAMOND_BLASTX } from '../../modules/nf-core/modules/diamond/blastx/main' - +include { MOTUS_PROFILE } from '../../modules/nf-core/modules/motus/profile/main' workflow PROFILING { take: @@ -44,6 +44,7 @@ workflow PROFILING { centrifuge: it[2]['tool'] == 'centrifuge' kaiju: it[2]['tool'] == 'kaiju' diamond: it[2]['tool'] == 'diamond' + motus: it[2]['tool'] == 'motus' unknown: true } @@ -186,9 +187,23 @@ workflow PROFILING { } + if ( params.run_motus ) { + + ch_input_for_motus = ch_input_for_profiling.motus + .multiMap { + it -> + reads: [it[0] + it[2], it[1]] + db: it[3] + } + + MOTUS_PROFILE ( ch_input_for_motus.reads, ch_input_for_motus.db ) + ch_versions = ch_versions.mix( MOTUS_PROFILE.out.versions.first() ) + ch_raw_profiles = ch_raw_profiles.mix( MOTUS_PROFILE.out.out ) + + } + emit: profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom versions = ch_versions // channel: [ versions.yml ] mqc = ch_multiqc_files } - From 4b3216c1cb494c8110c361df25524f7515cc531e Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Tue, 24 May 2022 08:42:18 -0400 Subject: [PATCH 218/306] filter fasta inputs for mOTUs; fix the typo for nopreprocessing in nextflow.config. --- nextflow.config | 2 +- subworkflows/local/profiling.nf | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 6c39ccd..a8240d1 100644 --- a/nextflow.config +++ b/nextflow.config @@ -192,7 +192,7 @@ profiles { test { includeConfig 'conf/test.config' } test_full { includeConfig 'conf/test_full.config' } test_noprofiling { includeConfig 'conf/test_noprofiling.config' } - test_nopreprocessing { includeConfig 'conf/test_preprocessing.config' } + test_nopreprocessing { includeConfig 'conf/test_nopreprocessing.config' } } // Load igenomes.config if required diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 109598d..2813bc7 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -190,6 +190,10 @@ workflow PROFILING { if ( params.run_motus ) { ch_input_for_motus = ch_input_for_profiling.motus + .filter{ + if (it[0].is_fasta) log.warn "[nf-core/taxprofiler] mOTUs currently does not accept FASTA files as input. Skipping mOTUs for sample ${it[0].id}." + !it[0].is_fasta + } .multiMap { it -> reads: [it[0] + it[2], it[1]] From f14141574e15b83165507c2223e17fd927d70018 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Wed, 25 May 2022 08:03:23 -0400 Subject: [PATCH 219/306] fix the end line issue for modules.json --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 2fc7203..d4172ae 100644 --- a/modules.json +++ b/modules.json @@ -86,4 +86,4 @@ } } } -} \ No newline at end of file +} From 74b48f091947e10de18d5f2565e9d0ac50be665e Mon Sep 17 00:00:00 2001 From: ZandraFagernas <47026226+ZandraFagernas@users.noreply.github.com> Date: Sat, 28 May 2022 13:29:24 +0200 Subject: [PATCH 220/306] Add files via upload --- docs/images/taxprofiler_logo.svg | 3223 ++++++++++++++++++++++++++++++ 1 file changed, 3223 insertions(+) create mode 100644 docs/images/taxprofiler_logo.svg diff --git a/docs/images/taxprofiler_logo.svg b/docs/images/taxprofiler_logo.svg new file mode 100644 index 0000000..0cb901d --- /dev/null +++ b/docs/images/taxprofiler_logo.svg @@ -0,0 +1,3223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + taxfinder + + + + + + + + + + + + taxprofiler + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + taxfinder + + + + + + + + + + + + taxprofiler + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 77da358e87818381006f6f009f3a8c9036295f8a Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 31 May 2022 12:45:38 +0200 Subject: [PATCH 221/306] Separate dark and light mode logos, add to README and add to MQC report --- README.md | 2 +- assets/multiqc_config.yml | 4 + docs/images/nf-core-taxprofiler_icon.png | Bin 0 -> 134012 bytes docs/images/nf-core-taxprofiler_icon.svg | 444 ++++ .../nf-core-taxprofiler_logo_custom_dark.png | Bin 0 -> 398775 bytes .../nf-core-taxprofiler_logo_custom_dark.svg | 2302 ++++++++++++++++ .../nf-core-taxprofiler_logo_custom_light.png | Bin 0 -> 443259 bytes .../nf-core-taxprofiler_logo_custom_light.svg | 2305 +++++++++++++++++ workflows/taxprofiler.nf | 3 + 9 files changed, 5059 insertions(+), 1 deletion(-) create mode 100644 docs/images/nf-core-taxprofiler_icon.png create mode 100644 docs/images/nf-core-taxprofiler_icon.svg create mode 100644 docs/images/nf-core-taxprofiler_logo_custom_dark.png create mode 100644 docs/images/nf-core-taxprofiler_logo_custom_dark.svg create mode 100644 docs/images/nf-core-taxprofiler_logo_custom_light.png create mode 100644 docs/images/nf-core-taxprofiler_logo_custom_light.svg diff --git a/README.md b/README.md index 722cccb..7f3932c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ![nf-core/taxprofiler](docs/images/nf-core-taxprofiler_logo_light.png#gh-light-mode-only) ![nf-core/taxprofiler](docs/images/nf-core-taxprofiler_logo_dark.png#gh-dark-mode-only) +# ![nf-core/taxprofiler](docs/images/nf-core-taxprofiler_logo_custom_light.png#gh-light-mode-only) ![nf-core/taxprofiler](docs/images/nf-core-taxprofiler_logo_custom_dark.png#gh-dark-mode-only) [![GitHub Actions CI Status](https://github.com/nf-core/taxprofiler/workflows/nf-core%20CI/badge.svg)](https://github.com/nf-core/taxprofiler/actions?query=workflow%3A%22nf-core+CI%22) [![GitHub Actions Linting Status](https://github.com/nf-core/taxprofiler/workflows/nf-core%20linting/badge.svg)](https://github.com/nf-core/taxprofiler/actions?query=workflow%3A%22nf-core+linting%22) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index b0a0b1c..438fb42 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -9,3 +9,7 @@ report_section_order: order: -1001 export_plots: true + +custom_logo: "nf-core-taxprofiler_logo_custom_light.png" +custom_logo_url: https://nf-co.re/taxprofiler +custom_logo_title: "nf-core/taxprofiler" diff --git a/docs/images/nf-core-taxprofiler_icon.png b/docs/images/nf-core-taxprofiler_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c639fb67f5d7b820fdd1ba26c38643abbfc820c4 GIT binary patch literal 134012 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 z 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 Za*+tl0NEe58M~A+ykT;kc-86ge*ko{Q8EAk literal 0 HcmV?d00001 diff --git a/docs/images/nf-core-taxprofiler_icon.svg b/docs/images/nf-core-taxprofiler_icon.svg new file mode 100644 index 0000000..24e615f --- /dev/null +++ b/docs/images/nf-core-taxprofiler_icon.svg @@ -0,0 +1,444 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/nf-core-taxprofiler_logo_custom_dark.png b/docs/images/nf-core-taxprofiler_logo_custom_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..6b089fc1392656a66025920f4b46ef87d8fe8d85 GIT binary patch literal 398775 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�IhigKWuY$;y__m5GLWP!q{etL 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 literal 0 HcmV?d00001 diff --git a/docs/images/nf-core-taxprofiler_logo_custom_dark.svg b/docs/images/nf-core-taxprofiler_logo_custom_dark.svg new file mode 100644 index 0000000..3d47b4c --- /dev/null +++ b/docs/images/nf-core-taxprofiler_logo_custom_dark.svg @@ -0,0 +1,2302 @@ + + + +taxfindertaxprofiler/ diff --git a/docs/images/nf-core-taxprofiler_logo_custom_light.png b/docs/images/nf-core-taxprofiler_logo_custom_light.png new file mode 100644 index 0000000000000000000000000000000000000000..2dc85b81a3d78ef077357222c27ca8f7d620e382 GIT binary patch literal 443259 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≶e)tFeODVv)@xsX=SvF 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 z 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ு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�o~o*j8y01BFOz|SCV(9otv4s`r3@* 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

DYKQ!rL|L|~+@2kiu=|KJ)w4`+ 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 + + +taxfindertaxprofiler/ diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index b8b953b..2b78573 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -87,6 +87,7 @@ def multiqc_report = [] workflow TAXPROFILER { ch_versions = Channel.empty() + ch_taxprofiler_logo = Channel.fromPath("$projectDir/docs/images/nf-core-taxprofiler_logo_custom_light.png") /* SUBWORKFLOW: Read in samplesheet, validate and stage input files @@ -224,6 +225,8 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix(ch_taxprofiler_logo.ifEmpty([])) + if (params.perform_shortread_clipmerge) { ch_multiqc_files = ch_multiqc_files.mix( SHORTREAD_PREPROCESSING.out.mqc.collect{it[1]}.ifEmpty([]) ) } From 792e2d019b94eb0d1f43da355350f8a066ecf0a2 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 31 May 2022 13:06:48 +0200 Subject: [PATCH 222/306] Fix merge error in schema --- nextflow_schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/nextflow_schema.json b/nextflow_schema.json index 179daea..c38e5d1 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -427,5 +427,6 @@ "longread_qc_targetnbases": { "type": "integer", "default": 500000000 + } } } From e916c91de3306518fab7f5b3d1773e7cbe86aea8 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Tue, 31 May 2022 07:57:15 -0400 Subject: [PATCH 223/306] Delete null/pipeline_info directory --- null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt diff --git a/null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt b/null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt deleted file mode 100644 index 6b739ac..0000000 --- a/null/pipeline_info/execution_trace_2022-05-21_11-05-12.txt +++ /dev/null @@ -1 +0,0 @@ -task_id hash native_id name status exit submit duration realtime %cpu peak_rss peak_vmem rchar wchar From 4d77b47e8d3400c6f348afcf8a2c8c653d1fd8e4 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Tue, 31 May 2022 08:47:38 -0400 Subject: [PATCH 224/306] create a new test profile config for mOTUs. --- .github/workflows/ci.yml | 4 +++- conf/test.config | 2 +- conf/test_motus.config | 41 ++++++++++++++++++++++++++++++++ conf/test_nopreprocessing.config | 2 +- docs/usage.md | 9 +++++++ nextflow.config | 1 + 6 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 conf/test_motus.config diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 689a193..01c5535 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,8 @@ jobs: - "--perform_runmerging" - "--perform_runmerging --shortread_clipmerge_mergepairs" - "--shortread_complexityfilter false --perform_shortread_hostremoval" + # Test different profiles + profile: ["test", "test_motus"] steps: - name: Check out pipeline code @@ -70,4 +72,4 @@ jobs: # For example: adding multiple test runs with different parameters # Remember that you can parallelise this by using strategy.matrix run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results ${{ matrix.parameters }} + nextflow run ${GITHUB_WORKSPACE} -profile ${{ matrix.profile }},docker --outdir ./results ${{ matrix.parameters }} diff --git a/conf/test.config b/conf/test.config index cf983ab..3a6d265 100644 --- a/conf/test.config +++ b/conf/test.config @@ -37,7 +37,7 @@ params { run_metaphlan3 = true run_centrifuge = true run_diamond = true - run_motus = true + run_motus = false } process { diff --git a/conf/test_motus.config b/conf/test_motus.config new file mode 100644 index 0000000..c645154 --- /dev/null +++ b/conf/test_motus.config @@ -0,0 +1,41 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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/taxprofiler -profile test, --outdir + +---------------------------------------------------------------------------------------- +*/ + +params { + config_profile_name = 'mOTUs Test profile' + config_profile_description = 'Minimal test to check mOTUs function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data + // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets + // TODO nf-core: Give any required params for the test so that command line flags are not needed + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' + perform_shortread_clipmerge = false + perform_longread_clip = false + perform_shortread_complexityfilter = false + perform_shortread_hostremoval = false + perform_longread_hostremoval = false + perform_runmerging = false + hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' + run_kaiju = false + run_kraken2 = false + run_malt = false + run_metaphlan3 = false + run_centrifuge = false + run_diamond = false + run_motus = true +} diff --git a/conf/test_nopreprocessing.config b/conf/test_nopreprocessing.config index 7658a2d..e52319f 100644 --- a/conf/test_nopreprocessing.config +++ b/conf/test_nopreprocessing.config @@ -37,7 +37,7 @@ params { run_metaphlan3 = true run_centrifuge = true run_diamond = true - run_motus = true + run_motus = false } process { diff --git a/docs/usage.md b/docs/usage.md index 54ffce0..3172b30 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -77,6 +77,7 @@ kraken2,db1,,///kraken2/testdb-kraken2.tar.gz kraken2,db2,--quick,///kraken2/testdb-kraken2.tar.gz centrifuge,db1,,///centrifuge/minigut_cf.tar.gz metaphlan3,db1,,///metaphlan3/metaphlan_database/ +motus,db_mOTU,,///motus/motus_database/ ``` Column specifications are as follows: @@ -131,6 +132,14 @@ Expected (uncompressed) database files for each tool are as follows: - **DIAMOND** output of `diamond makedb`. Note: requires building with taxonomy files to generate taxonomic profile. See [DIAMOND documentation](https://github.com/bbuchfink/diamond/wiki/3.-Command-line-options#makedb-options). A file named: - `.dmnd` +- **mOTUs** is composed of code and database together. The mOTUs tools + [`downloadDB`](https://github.com/motu-tool/mOTUs/blob/master/motus/downloadDB.py) + is used to prepare the mOTUs database and create a file with the version information. + The database download step can be time consuming and the database will be consisting + with same release version of the mOTUs tools. The database for same version tools + can be thus reused for multiple runs. Users can download the database once and + assign the database with the table. User can also set the parameter + `download_motus_db` and let the pipeline download the database automatically. ## Running the pipeline diff --git a/nextflow.config b/nextflow.config index 524c249..98e6cc5 100644 --- a/nextflow.config +++ b/nextflow.config @@ -193,6 +193,7 @@ profiles { test_full { includeConfig 'conf/test_full.config' } test_noprofiling { includeConfig 'conf/test_noprofiling.config' } test_nopreprocessing { includeConfig 'conf/test_nopreprocessing.config' } + test_motus { includeConfig 'conf/test_motus.config' } } // Load igenomes.config if required From f0eb88cbe265e8f4a83577bcba10b614659605f8 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Tue, 31 May 2022 08:57:00 -0400 Subject: [PATCH 225/306] add mOTUs to modules.config --- conf/modules.config | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/conf/modules.config b/conf/modules.config index 5d8398e..c2a3d85 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -337,6 +337,20 @@ process { ] } + withName: MOTUS_DOWNLOADDB { + publishDir = [ + path: { "${params.outdir}/motus/db/${meta.db_name}" }, + mode: params.publish_dir_mode + ] + } + + withName: MOTUS_PROFILE { + publishDir = [ + path: { "${params.outdir}/motus/${meta.db_name}" }, + mode: params.publish_dir_mode + ] + } + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ path: { "${params.outdir}/pipeline_info" }, From 16ec5bf74af50fbdcf675463d6ab030ae00d56b7 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 31 May 2022 20:11:19 +0200 Subject: [PATCH 226/306] Update parameter names --- conf/modules.config | 8 ++++---- conf/test_nopreprocessing.config | 4 ++-- conf/test_noprofiling.config | 4 ++-- modules.json | 2 +- modules/nf-core/modules/filtlong/main.nf | 7 ++++--- workflows/taxprofiler.nf | 6 +++--- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 797a209..164fe91 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -54,7 +54,7 @@ process { params.shortread_qc_skipadaptertrim ? "--disable_adapter_trimming" : "", params.shortread_qc_adapter1 ? "--adapter_sequence ${params.shortread_qc_adapter1}" : "", // filtering options - "--length_required ${params.shortread_clipmerge_minlength}", + "--length_required ${params.shortread_qc_minlength}", (params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp') ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } @@ -75,7 +75,7 @@ process { params.shortread_qc_adapter1 ? "--adapter_sequence ${params.shortread_qc_adapter1}" : "", params.shortread_qc_adapter2 ? "--adapter_sequence_r2 ${params.shortread_qc_adapter2}" : "--detect_adapter_for_pe", // filtering options - "--length_required ${params.shortread_clipmerge_minlength}", + "--length_required ${params.shortread_qc_minlength}", params.perform_shortread_complexityfilter && params.shortread_complexityfilter_tool == 'fastp' ? "--low_complexity_filter --complexity_threshold ${params.shortread_complexityfilter_fastp_threshold}" : '' ].join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}" } @@ -141,9 +141,9 @@ process { "--target_bases ${params.longread_qc_targetnbases}" ] .join(' ').trim() - ext.prefix = { "${meta.id}_${meta.run_accession}" } + ext.prefix = { "${meta.id}_${meta.run_accession}_filtered" } publishDir = [ - path: { "${params.outdir}/porechop" }, + path: { "${params.outdir}/filtlong" }, mode: params.publish_dir_mode, pattern: '*.fastq.gz', enabled: params.save_preprocessed_reads diff --git a/conf/test_nopreprocessing.config b/conf/test_nopreprocessing.config index e8d4ed9..60cdde8 100644 --- a/conf/test_nopreprocessing.config +++ b/conf/test_nopreprocessing.config @@ -24,8 +24,8 @@ params { // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - perform_shortread_clipmerge = false - perform_longread_clip = false + perform_shortread_qc = false + perform_longread_qc = false perform_shortread_complexityfilter = false perform_shortread_hostremoval = false perform_longread_hostremoval = false diff --git a/conf/test_noprofiling.config b/conf/test_noprofiling.config index f908651..379aaae 100644 --- a/conf/test_noprofiling.config +++ b/conf/test_noprofiling.config @@ -24,8 +24,8 @@ params { // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - perform_shortread_clipmerge = true - perform_longread_clip = true + perform_shortread_qc = true + perform_longread_qc = true perform_shortread_complexityfilter = true perform_shortread_hostremoval = true perform_longread_hostremoval = true diff --git a/modules.json b/modules.json index 9520707..b27939b 100644 --- a/modules.json +++ b/modules.json @@ -37,7 +37,7 @@ "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, "filtlong": { - "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" + "git_sha": "723852bf3d3b12059b2f53da8bc055206f3019d7" }, "kaiju/kaiju": { "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" diff --git a/modules/nf-core/modules/filtlong/main.nf b/modules/nf-core/modules/filtlong/main.nf index 0e6fdd5..9dbf05b 100644 --- a/modules/nf-core/modules/filtlong/main.nf +++ b/modules/nf-core/modules/filtlong/main.nf @@ -11,7 +11,7 @@ process FILTLONG { tuple val(meta), path(shortreads), path(longreads) output: - tuple val(meta), path("${meta.id}_lr_filtlong.fastq.gz"), emit: reads + tuple val(meta), path("*.fastq.gz"), emit: reads path "versions.yml" , emit: versions when: @@ -20,13 +20,14 @@ process FILTLONG { script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - def short_reads = meta.single_end ? "-1 $shortreads" : "-1 ${shortreads[0]} -2 ${shortreads[1]}" + def short_reads = !shortreads ? "" : meta.single_end ? "-1 $shortreads" : "-1 ${shortreads[0]} -2 ${shortreads[1]}" + if ("$longreads" == "${prefix}.fastq.gz") error "Longread FASTQ input and output names are the same, set prefix in module configuration to disambiguate!" """ filtlong \\ $short_reads \\ $args \\ $longreads \\ - | gzip -n > ${prefix}_lr_filtlong.fastq.gz + | gzip -n > ${prefix}.fastq.gz cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index a6ddaa3..1c48fd6 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -20,11 +20,11 @@ for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true if (params.input ) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet not specified!' } if (params.databases) { ch_databases = file(params.databases) } else { exit 1, 'Input database sheet not specified!' } -if (params.shortread_clipmerge_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." -if (params.shortread_clipmerge_excludeunmerged && !params.shortread_clipmerge_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_clipmerge_mergepairs" +if (params.shortread_qc_mergepairs && params.run_malt ) log.warn "[nf-core/taxprofiler] MALT does not accept uncollapsed paired-reads. Pairs will be profiled as separate files." +if (params.shortread_qc_excludeunmerged && !params.shortread_qc_mergepairs) exit 1, "ERROR: [nf-core/taxprofiler] cannot include unmerged reads when merging not turned on. Please specify --shortread_qc_mergepairs" if ( (params.longread_qc_run_clip || params.longread_qc_run_filter) & !params.perform_longread_qc ) exit 1, "ERROR: [nf-core/taxprofiler] --longread_qc_run_clip or --longread_qc_run_filter requested but quality-control not turned on. Please specify --perform_long_qc" -if (params.shortread_complexityfilter_tool == 'fastp' && ( params.perform_shortread_clipmerge == false || params.shortread_clipmerge_tool != 'fastp' )) exit 1, "ERROR: [nf-core/taxprofiler] cannot use fastp complexity filtering if preprocessing not turned on and/or tool is not fastp. Please specify --perform_shortread_clipmerge and/or --shortread_clipmerge_tool 'fastp'" +if (params.shortread_complexityfilter_tool == 'fastp' && ( params.perform_shortread_qc == false || params.shortread_qc_tool != 'fastp' )) exit 1, "ERROR: [nf-core/taxprofiler] cannot use fastp complexity filtering if preprocessing not turned on and/or tool is not fastp. Please specify --perform_shortread_qc and/or --shortread_qc_tool 'fastp'" if (params.perform_shortread_hostremoval && !params.hostremoval_reference) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval requested but no --hostremoval_reference FASTA supplied. Check input." } if (!params.hostremoval_reference && params.hostremoval_reference_index) { exit 1, "ERROR: [nf-core/taxprofiler] --shortread_hostremoval_index provided but no --hostremoval_reference FASTA supplied. Check input." } From fa70a413cb85cc10c63b47195fbdb5b3c4e49192 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 31 May 2022 21:12:09 +0200 Subject: [PATCH 227/306] Use finally working filtlong module --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index b27939b..fe25bed 100644 --- a/modules.json +++ b/modules.json @@ -37,7 +37,7 @@ "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, "filtlong": { - "git_sha": "723852bf3d3b12059b2f53da8bc055206f3019d7" + "git_sha": "089f761f0bf79c4a486f1df9b6205f650196a2c1" }, "kaiju/kaiju": { "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" From d3d28da1b18d60fa406e9c175f25d49396245084 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 2 Jun 2022 11:53:53 +0200 Subject: [PATCH 228/306] Update after review --- conf/modules.config | 4 ++-- conf/test.config | 4 ++-- nextflow.config | 4 ++-- nextflow_schema.json | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 164fe91..09b2ed9 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -137,8 +137,8 @@ process { withName: FILTLONG { ext.args = [ "--min_length ${params.longread_qc_minlength}", - "--keep_percent ${params.longread_qc_keepbppercent}", - "--target_bases ${params.longread_qc_targetnbases}" + "--keep_percent ${params.longread_qc_keep_percent}", + "--target_bases ${params.longread_qc_target_bases}" ] .join(' ').trim() ext.prefix = { "${meta.id}_${meta.run_accession}_filtered" } diff --git a/conf/test.config b/conf/test.config index 6af771c..d6fc7fe 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,8 +24,8 @@ params { // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' - perform_shortread_qc = true - perform_longread_qc = false + perform_shortread_qc = true + perform_longread_qc = true perform_shortread_complexityfilter = true perform_shortread_hostremoval = true perform_longread_hostremoval = true diff --git a/nextflow.config b/nextflow.config index 5e9b385..d8f80ab 100644 --- a/nextflow.config +++ b/nextflow.config @@ -68,8 +68,8 @@ params { longread_qc_run_clip = false longread_qc_run_filter = false longread_qc_minlength = 1000 - longread_qc_keepbppercent = 90 - longread_qc_targetnbases = 500000000 + longread_qc_keep_percent = 90 + longread_qc_target_bases = 500000000 save_preprocessed_reads = false diff --git a/nextflow_schema.json b/nextflow_schema.json index c38e5d1..e3cc558 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -420,11 +420,11 @@ "type": "integer", "default": 1000 }, - "longread_qc_keepbppercent": { + "longread_qc_keep_percent": { "type": "integer", "default": 90 }, - "longread_qc_targetnbases": { + "longread_qc_target_bases": { "type": "integer", "default": 500000000 } From 2838c136bda47ad24d36b5ebb174998d58c57348 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 2 Jun 2022 12:51:39 +0200 Subject: [PATCH 229/306] Prettier --- modules.json | 2 +- nextflow_schema.json | 45 +++++++------------------------------------- 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/modules.json b/modules.json index 5cad32e..9d918b3 100644 --- a/modules.json +++ b/modules.json @@ -80,4 +80,4 @@ } } } -} \ No newline at end of file +} diff --git a/nextflow_schema.json b/nextflow_schema.json index e7b495d..f0093b1 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -176,14 +173,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -295,10 +285,7 @@ "shortread_clipmerge_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_clipmerge_skipadaptertrim": { "type": "boolean" @@ -340,10 +327,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -399,14 +383,7 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": [ - "phylum", - "class", - "order", - "family", - "genus", - "species" - ] + "enum": ["phylum", "class", "order", "family", "genus", "species"] }, "run_diamond": { "type": "boolean" @@ -414,15 +391,7 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": [ - "blast", - "xml", - "txt", - "daa", - "sam", - "tsv", - "paf" - ] + "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] }, "longread_hostremoval_index": { "type": "string", @@ -448,4 +417,4 @@ "type": "boolean" } } -} \ No newline at end of file +} From 3313b90453ed0e4bf2571a28d1436427a05b90f7 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 3 Jun 2022 13:50:48 +0200 Subject: [PATCH 230/306] Fix JSOn schema --- nextflow_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index a82ff9e..2c00348 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -418,7 +418,7 @@ "longread_qc_target_bases": { "type": "integer", "default": 500000000 - } + }, "malt_save_reads": { "type": "boolean" }, From d48b3be5a780d87b30c8f9ce189a062676cc601a Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 3 Jun 2022 13:55:17 +0200 Subject: [PATCH 231/306] Tweak DIAMOND save_reads message --- nextflow.config | 2 +- workflows/taxprofiler.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index 11cbfc0..e5b8a1d 100644 --- a/nextflow.config +++ b/nextflow.config @@ -124,7 +124,7 @@ params { // diamond run_diamond = false diamond_output_format = 'tsv' // TSV is only format with taxonomic information apparently - diamond_save_reads = false // this will override diamound output format so no taxonomic profile is generated! + diamond_save_reads = false // this will override default diamond output format so no taxonomic profile is generated! } // Load base.config by default for all pipelines diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 6a05dbe..f29a366 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -33,7 +33,7 @@ if (params.hostremoval_reference ) { ch_reference = file(params.hostre if (params.shortread_hostremoval_index ) { ch_shortread_reference_index = file(params.shortread_hostremoval_index ) } else { ch_shortread_reference_index = [] } if (params.longread_hostremoval_index ) { ch_longread_reference_index = file(params.longread_hostremoval_index ) } else { ch_longread_reference_index = [] } -if (params.diamond_save_reads ) log.warn "[nf-core/taxprofiler] DIAMOND only allows output of a single format. Only aligned reads in SAM format will be produced, no taxonomic profiles will be available." +if (params.diamond_save_reads ) log.warn "[nf-core/taxprofiler] DIAMOND only allows output of a single format. As --diamond_save_reads supplied, only aligned reads in SAM format will be produced, no taxonomic profiles will be available." /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From c8a59adbceb8790c02f49a31e95a89347ed2fc04 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Fri, 3 Jun 2022 14:13:58 -0400 Subject: [PATCH 232/306] remove the database auto-download for mOTUs. --- .github/workflows/ci.yml | 54 +++++++++++++++++-- conf/modules.config | 7 --- conf/test_motus.config | 2 +- docs/usage.md | 3 +- modules.json | 3 -- .../nf-core/modules/motus/downloaddb/main.nf | 39 -------------- .../nf-core/modules/motus/downloaddb/meta.yml | 39 -------------- nextflow.config | 2 - nextflow_schema.json | 8 --- subworkflows/local/db_check.nf | 17 ------ 10 files changed, 53 insertions(+), 121 deletions(-) delete mode 100644 modules/nf-core/modules/motus/downloaddb/main.nf delete mode 100644 modules/nf-core/modules/motus/downloaddb/meta.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16a39a6..0088181 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,8 +42,6 @@ jobs: - "--perform_runmerging" - "--perform_runmerging --shortread_qc_mergepairs" - "--shortread_complexityfilter false --perform_shortread_hostremoval" - # Test different profiles - profile: ["test", "test_motus"] steps: - name: Check out pipeline code @@ -72,4 +70,54 @@ jobs: # For example: adding multiple test runs with different parameters # Remember that you can parallelise this by using strategy.matrix run: | - nextflow run ${GITHUB_WORKSPACE} -profile ${{ matrix.profile }},docker --outdir ./results ${{ matrix.parameters }} + nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results ${{ matrix.parameters }} + + motus: + name: Test mOTUs with workflow parameters + if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/taxprofiler') }} + runs-on: ubuntu-latest + env: + NXF_VER: ${{ matrix.nxf_ver }} + NXF_ANSI_LOG: false + strategy: + matrix: + # Nextflow versions + include: + # Test pipeline minimum Nextflow version + - NXF_VER: "21.10.3" + NXF_EDGE: "" + # Test latest edge release of Nextflow + - NXF_VER: "" + NXF_EDGE: "1" + + steps: + - name: Check out pipeline code + uses: actions/checkout@v2 + + - name: Install Nextflow + env: + NXF_VER: ${{ matrix.NXF_VER }} + # Uncomment only if the edge release is more recent than the latest stable release + # See https://github.com/nextflow-io/nextflow/issues/2467 + # NXF_EDGE: ${{ matrix.NXF_EDGE }} + run: | + wget -qO- get.nextflow.io | bash + sudo mv nextflow /usr/local/bin/ + + - name: Show current locale + run: locale + + - name: Set UTF-8 enabled locale + run: | + sudo locale-gen en_US.UTF-8 + sudo update-locale LANG=en_US.UTF-8 + + - name: Prepare the database + run: | + wget https://github.com/motu-tool/mOTUs/blob/master/motus/downloadDB.py + python downloadDB.py + echo 'tool,db_name,db_params,db_path\nmotus,db_mOTU,,db_mOTU' > 'database_motus.csv' + + - name: Run pipeline with test data + run: | + nextflow run ${GITHUB_WORKSPACE} -profile test_motus,docker --outdir ./results --databases ./database_motus.csv diff --git a/conf/modules.config b/conf/modules.config index 1e7c260..66967e2 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -353,13 +353,6 @@ process { ] } - withName: MOTUS_DOWNLOADDB { - publishDir = [ - path: { "${params.outdir}/motus/db/${meta.db_name}" }, - mode: params.publish_dir_mode - ] - } - withName: MOTUS_PROFILE { publishDir = [ path: { "${params.outdir}/motus/${meta.db_name}" }, diff --git a/conf/test_motus.config b/conf/test_motus.config index c645154..9d39ad4 100644 --- a/conf/test_motus.config +++ b/conf/test_motus.config @@ -23,7 +23,7 @@ params { // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' - databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' + databases = 'database_motus.csv' perform_shortread_clipmerge = false perform_longread_clip = false perform_shortread_complexityfilter = false diff --git a/docs/usage.md b/docs/usage.md index b43ae12..ea21e54 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -138,8 +138,7 @@ Expected (uncompressed) database files for each tool are as follows: The database download step can be time consuming and the database will be consisting with same release version of the mOTUs tools. The database for same version tools can be thus reused for multiple runs. Users can download the database once and - assign the database with the table. User can also set the parameter - `download_motus_db` and let the pipeline download the database automatically. + assign the database with the table. ## Running the pipeline diff --git a/modules.json b/modules.json index 3e4dd9b..758ac22 100644 --- a/modules.json +++ b/modules.json @@ -63,9 +63,6 @@ "minimap2/index": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, - "motus/downloaddb": { - "git_sha": "6393a085c5fcea11963774c041808df169907487" - }, "motus/profile": { "git_sha": "6b960f0e75bbb4d5bd301cd3875fa078d0eab4d1" }, diff --git a/modules/nf-core/modules/motus/downloaddb/main.nf b/modules/nf-core/modules/motus/downloaddb/main.nf deleted file mode 100644 index 317624b..0000000 --- a/modules/nf-core/modules/motus/downloaddb/main.nf +++ /dev/null @@ -1,39 +0,0 @@ -process MOTUS_DOWNLOADDB { - label 'process_low' - - conda (params.enable_conda ? "bioconda::motus=3.0.1" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/motus:3.0.1--pyhdfd78af_0': - 'quay.io/biocontainers/motus:3.0.1--pyhdfd78af_0' }" - - input: - path motus_downloaddb_script - - output: - path "db_mOTU/" , emit: db - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - def software = "${motus_downloaddb_script.simpleName}_copy.py" - """ - ## must copy script file to working directory, - ## otherwise the reference_db will be download to bin folder - ## other than current directory - cp $motus_downloaddb_script ${software} - python ${software} \\ - $args \\ - -t $task.cpus - - ## mOTUs version number is not available from command line. - ## mOTUs save the version number in index database folder. - ## mOTUs will check the database version is same version as exec version. - cat <<-END_VERSIONS > versions.yml - "${task.process}": - mOTUs: \$(grep motus db_mOTU/db_mOTU_versions | sed 's/motus\\t//g') - END_VERSIONS - """ -} diff --git a/modules/nf-core/modules/motus/downloaddb/meta.yml b/modules/nf-core/modules/motus/downloaddb/meta.yml deleted file mode 100644 index 64df5ee..0000000 --- a/modules/nf-core/modules/motus/downloaddb/meta.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: "motus_downloaddb" -description: Download the mOTUs database -keywords: - - classify - - metagenomics - - fastq - - taxonomic profiling - - database - - download -tools: - - "motus": - description: "The mOTU profiler is a computational tool that estimates relative taxonomic abundance of known and currently unknown microbial community members using metagenomic shotgun sequencing data." - homepage: "None" - documentation: "https://github.com/motu-tool/mOTUs/wiki" - tool_dev_url: "https://github.com/motu-tool/mOTUs" - doi: "10.1038/s41467-019-08844-4" - licence: "['GPL v3']" - -input: - - motus_downloaddb: - type: directory - description: | - The mOTUs downloadDB script source file. - It is the source file installed or - remote source in github such as https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py - pattern: "downloadDB.py" - -output: - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - - db: - type: directory - description: The mOTUs database directory - pattern: "db_mOTU" - -authors: - - "@jianhong" diff --git a/nextflow.config b/nextflow.config index 0ddd5c1..7f9f4c1 100644 --- a/nextflow.config +++ b/nextflow.config @@ -126,8 +126,6 @@ params { // mOTUs run_motus = false - download_motus_db = true - motus_downloaddb_script = 'https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py' } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index fc0d9e3..b380b84 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -430,14 +430,6 @@ }, "run_motus": { "type": "boolean" - }, - "download_motus_db": { - "type": "boolean" - }, - "motus_downloaddb_script": { - "type": "string", - "default": "https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py", - "description": "mOTUs database download script path." } } } diff --git a/subworkflows/local/db_check.nf b/subworkflows/local/db_check.nf index 95eeefc..7b440c6 100644 --- a/subworkflows/local/db_check.nf +++ b/subworkflows/local/db_check.nf @@ -4,7 +4,6 @@ include { DATABASE_CHECK } from '../../modules/local/database_check' include { UNTAR } from '../../modules/nf-core/modules/untar/main' -include { MOTUS_DOWNLOADDB } from '../../modules/nf-core/modules/motus/downloaddb/main' workflow DB_CHECK { take: @@ -21,22 +20,6 @@ workflow DB_CHECK { .splitCsv ( header:true, sep:',' ) .map { create_db_channels(it) } - // Download database for mOTUs - if( params.run_motus ){ - check_motus_db = - parsed_samplesheet.filter{ it[0].tool == "motus" } - .ifEmpty{[]} - if( params.download_motus_db ){ - MOTUS_DOWNLOADDB( params.motus_downloaddb_script ) - check_motus_db = MOTUS_DOWNLOADDB.out.db - .map{[ - [tool: "motus", db_name: "db_mOTU", db_params: ''], - it - ]} - } - parsed_samplesheet = parsed_samplesheet.mix(check_motus_db) - } - ch_dbs_for_untar = parsed_samplesheet .branch { untar: it[1].toString().endsWith(".tar.gz") From 22f65eedc7054363d84f029851c17efa78567a15 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Fri, 3 Jun 2022 14:16:25 -0400 Subject: [PATCH 233/306] fix the download address for downloadDB for mOTUs. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0088181..61911a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,7 +114,7 @@ jobs: - name: Prepare the database run: | - wget https://github.com/motu-tool/mOTUs/blob/master/motus/downloadDB.py + wget https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py python downloadDB.py echo 'tool,db_name,db_params,db_path\nmotus,db_mOTU,,db_mOTU' > 'database_motus.csv' From 3cd3d2dad365558df2a71d7afc19820304e8c2f4 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Fri, 3 Jun 2022 14:38:59 -0400 Subject: [PATCH 234/306] prettier write . --- .github/workflows/ci.yml | 84 ++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61911a8..c018947 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,51 +73,51 @@ jobs: nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results ${{ matrix.parameters }} motus: - name: Test mOTUs with workflow parameters - if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/taxprofiler') }} - runs-on: ubuntu-latest - env: - NXF_VER: ${{ matrix.nxf_ver }} - NXF_ANSI_LOG: false - strategy: - matrix: - # Nextflow versions - include: - # Test pipeline minimum Nextflow version - - NXF_VER: "21.10.3" - NXF_EDGE: "" - # Test latest edge release of Nextflow - - NXF_VER: "" - NXF_EDGE: "1" + name: Test mOTUs with workflow parameters + if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/taxprofiler') }} + runs-on: ubuntu-latest + env: + NXF_VER: ${{ matrix.nxf_ver }} + NXF_ANSI_LOG: false + strategy: + matrix: + # Nextflow versions + include: + # Test pipeline minimum Nextflow version + - NXF_VER: "21.10.3" + NXF_EDGE: "" + # Test latest edge release of Nextflow + - NXF_VER: "" + NXF_EDGE: "1" - steps: - - name: Check out pipeline code - uses: actions/checkout@v2 + steps: + - name: Check out pipeline code + uses: actions/checkout@v2 - - name: Install Nextflow - env: - NXF_VER: ${{ matrix.NXF_VER }} - # Uncomment only if the edge release is more recent than the latest stable release - # See https://github.com/nextflow-io/nextflow/issues/2467 - # NXF_EDGE: ${{ matrix.NXF_EDGE }} - run: | - wget -qO- get.nextflow.io | bash - sudo mv nextflow /usr/local/bin/ + - name: Install Nextflow + env: + NXF_VER: ${{ matrix.NXF_VER }} + # Uncomment only if the edge release is more recent than the latest stable release + # See https://github.com/nextflow-io/nextflow/issues/2467 + # NXF_EDGE: ${{ matrix.NXF_EDGE }} + run: | + wget -qO- get.nextflow.io | bash + sudo mv nextflow /usr/local/bin/ - - name: Show current locale - run: locale + - name: Show current locale + run: locale - - name: Set UTF-8 enabled locale - run: | - sudo locale-gen en_US.UTF-8 - sudo update-locale LANG=en_US.UTF-8 + - name: Set UTF-8 enabled locale + run: | + sudo locale-gen en_US.UTF-8 + sudo update-locale LANG=en_US.UTF-8 - - name: Prepare the database - run: | - wget https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py - python downloadDB.py - echo 'tool,db_name,db_params,db_path\nmotus,db_mOTU,,db_mOTU' > 'database_motus.csv' + - name: Prepare the database + run: | + wget https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py + python downloadDB.py > download_db_log.txt + echo 'tool,db_name,db_params,db_path\nmotus,db_mOTU,,db_mOTU' > 'database_motus.csv' - - name: Run pipeline with test data - run: | - nextflow run ${GITHUB_WORKSPACE} -profile test_motus,docker --outdir ./results --databases ./database_motus.csv + - name: Run pipeline with test data + run: | + nextflow run ${GITHUB_WORKSPACE} -profile test_motus,docker --outdir ./results --databases ./database_motus.csv From 9fa6ecad56a11e85c02b547998445e3311618298 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Fri, 3 Jun 2022 14:50:03 -0400 Subject: [PATCH 235/306] debug for database csv file --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c018947..96f507c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,7 +116,11 @@ jobs: run: | wget https://raw.githubusercontent.com/motu-tool/mOTUs/master/motus/downloadDB.py python downloadDB.py > download_db_log.txt - echo 'tool,db_name,db_params,db_path\nmotus,db_mOTU,,db_mOTU' > 'database_motus.csv' + echo 'tool,db_name,db_params,db_path' > 'database_motus.csv' + echo 'motus,db_mOTU,,db_mOTU' >> 'database_motus.csv' + cat database_motus.csv + echo $PWD + ls $PWD - name: Run pipeline with test data run: | From 713a341e097cb7313627b95af88bfc05b03fd759 Mon Sep 17 00:00:00 2001 From: JIANHONG OU Date: Fri, 3 Jun 2022 15:22:33 -0400 Subject: [PATCH 236/306] clean up ci.yml --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96f507c..9bdd7fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,9 +118,6 @@ jobs: python downloadDB.py > download_db_log.txt echo 'tool,db_name,db_params,db_path' > 'database_motus.csv' echo 'motus,db_mOTU,,db_mOTU' >> 'database_motus.csv' - cat database_motus.csv - echo $PWD - ls $PWD - name: Run pipeline with test data run: | From 9462032d00824f16c55fe4a313404282eddab91d Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 3 Jun 2022 22:29:04 +0200 Subject: [PATCH 237/306] Fix MALT save_alignemnts reads and some clean up --- conf/test.config | 3 +++ nextflow.config | 2 +- subworkflows/local/longread_preprocessing.nf | 2 +- subworkflows/local/profiling.nf | 10 ++++------ 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/conf/test.config b/conf/test.config index 996d5de..e9fa62e 100644 --- a/conf/test.config +++ b/conf/test.config @@ -47,4 +47,7 @@ process { withName: MALT_RUN { maxForks = 1 } + withName: MEGAN_RMA2INFO { + maxForks = 1 + } } diff --git a/nextflow.config b/nextflow.config index e5b8a1d..73fd0b3 100644 --- a/nextflow.config +++ b/nextflow.config @@ -196,7 +196,7 @@ profiles { test { includeConfig 'conf/test.config' } test_full { includeConfig 'conf/test_full.config' } test_noprofiling { includeConfig 'conf/test_noprofiling.config' } - test_nopreprocessing { includeConfig 'conf/test_preprocessing.config' } + test_nopreprocessing { includeConfig 'conf/test_nopreprocessing.config' } } // Load igenomes.config if required diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 5ae5417..6a23b0e 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -48,7 +48,7 @@ workflow LONGREAD_PREPROCESSING { } - FASTQC_PROCESSED ( ch_processed_reads.dump(tag: "filtlong") ) + FASTQC_PROCESSED ( ch_processed_reads ) ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) emit: diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 18ea7fa..b83c78f 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -66,19 +66,17 @@ workflow PROFILING { .filter { it[0]['instrument_platform'] == 'ILLUMINA' } .map { meta, reads, db_meta, db -> - def sam_format = params.malt_save_reads ? ' --alignments' : "" - // TODO No MALT SAM? - // TODO check all aligned reads published - // TODO try turning on/off aligned reads + def sam_format = params.malt_save_reads ? ' --alignments ./ -za false' : "" // TODO wut? [9a/a441d6] Submitted process > NFCORE_TAXPROFILER:TAXPROFILER:PROFILING:MALT_RUN (null) def temp_meta = [ id: meta['db_name'] ] def new_db_meta = db_meta.clone() new_db_meta['db_params'] = db_meta['db_params'] + sam_format def new_meta = temp_meta + new_db_meta - + new_meta['id'] = new_meta['db_name'] [ new_meta, reads, db ] } .groupTuple(by: [0,2]) + .dump(tag: "into_malt") .multiMap { it -> reads: [ it[0], it[1].flatten() ] @@ -192,7 +190,7 @@ workflow PROFILING { // this will replace output file! ch_diamond_reads_format = params.diamond_save_reads ? 'sam' : params.diamond_output_format - DIAMOND_BLASTX ( ch_input_for_diamond.reads, ch_input_for_diamond.db, params.diamond_output_format, [] ) + DIAMOND_BLASTX ( ch_input_for_diamond.reads, ch_input_for_diamond.db, ch_diamond_reads_format , [] ) ch_versions = ch_versions.mix( DIAMOND_BLASTX.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( DIAMOND_BLASTX.out.tsv ) From ebdd5683b248d2664fbd41fdc32cb5bf06e2e4ab Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Fri, 3 Jun 2022 22:33:48 +0200 Subject: [PATCH 238/306] Apply suggestions from code review --- subworkflows/local/profiling.nf | 2 -- 1 file changed, 2 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index b83c78f..9bae127 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -67,7 +67,6 @@ workflow PROFILING { .map { meta, reads, db_meta, db -> def sam_format = params.malt_save_reads ? ' --alignments ./ -za false' : "" - // TODO wut? [9a/a441d6] Submitted process > NFCORE_TAXPROFILER:TAXPROFILER:PROFILING:MALT_RUN (null) def temp_meta = [ id: meta['db_name'] ] def new_db_meta = db_meta.clone() new_db_meta['db_params'] = db_meta['db_params'] + sam_format @@ -76,7 +75,6 @@ workflow PROFILING { [ new_meta, reads, db ] } .groupTuple(by: [0,2]) - .dump(tag: "into_malt") .multiMap { it -> reads: [ it[0], it[1].flatten() ] From dc48935651e5a32fabbd7e095cd034cdfec625db Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 7 Jun 2022 20:19:45 +0200 Subject: [PATCH 239/306] Apply suggestions from code review --- docs/usage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index ea21e54..4090cf2 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -137,8 +137,8 @@ Expected (uncompressed) database files for each tool are as follows: is used to prepare the mOTUs database and create a file with the version information. The database download step can be time consuming and the database will be consisting with same release version of the mOTUs tools. The database for same version tools - can be thus reused for multiple runs. Users can download the database once and - assign the database with the table. + can be thus reused for multiple runs. Users can download the database once using the script above and + specify the path the database to the TSV table provided to `--databases`. ## Running the pipeline From 490a8a8a840566a53ac252b107c3c1fff207c61c Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 9 Jun 2022 08:15:58 +0200 Subject: [PATCH 240/306] Remove dump --- subworkflows/local/profiling.nf | 1 - 1 file changed, 1 deletion(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index b83c78f..03f0bf5 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -76,7 +76,6 @@ workflow PROFILING { [ new_meta, reads, db ] } .groupTuple(by: [0,2]) - .dump(tag: "into_malt") .multiMap { it -> reads: [ it[0], it[1].flatten() ] From ec13b8d608c5a95e9a2e27ae6c2b76691d5b0448 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 9 Jun 2022 08:21:48 +0200 Subject: [PATCH 241/306] Remove support for uncompressed FASTQ files --- bin/check_samplesheet.py | 2 +- docs/usage.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index d10ee90..47c6452 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -48,7 +48,7 @@ def check_samplesheet(file_in, file_out): 2613,ERR5766181,ILLUMINA,ERX5474937_ERR5766181_1.fastq.gz,ERX5474937_ERR5766181_2.fastq.gz, """ - FQ_EXTENSIONS = (".fq", ".fq.gz", ".fastq", ".fastq.gz") + FQ_EXTENSIONS = (".fq.gz", ".fastq.gz") FA_EXTENSIONS = ( ".fa", ".fa.gz", diff --git a/docs/usage.md b/docs/usage.md index 54ffce0..8b40745 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -12,6 +12,8 @@ nf-core/taxprofiler can accept as input raw or preprocessed single- or paired-end short-read (e.g. Illumina) FASTQ files, long-read FASTQ files (e.g. Oxford Nanopore), or FASTA sequences (available for a subset of profilers). +> ⚠️ Input FASTQ files _must_ be gzipped, while FASTA files may optionally be uncompressed (although this is not recommended) + You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 6 columns, and a header row as shown in the examples below. Furthermother, nf-core/taxprofiler also requires a second comma-separated file of 3 columns with a header row as in the examples below. This samplesheet is then specified on the command line as follows: From 621b6a3d092e00a11086444083c95544ad8781b8 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Fri, 10 Jun 2022 11:27:51 +0200 Subject: [PATCH 242/306] Add comments about MALT id replacement and refactor for simplicity --- subworkflows/local/profiling.nf | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 9bae127..7d35837 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -66,13 +66,20 @@ workflow PROFILING { .filter { it[0]['instrument_platform'] == 'ILLUMINA' } .map { meta, reads, db_meta, db -> - def sam_format = params.malt_save_reads ? ' --alignments ./ -za false' : "" - def temp_meta = [ id: meta['db_name'] ] + def new_meta = meta.clone() def new_db_meta = db_meta.clone() + + // Add the saving of alignments in SAM format to params + def sam_format = params.malt_save_reads ? ' --alignments ./ -za false' : "" new_db_meta['db_params'] = db_meta['db_params'] + sam_format - def new_meta = temp_meta + new_db_meta - new_meta['id'] = new_meta['db_name'] - [ new_meta, reads, db ] + + // As MALT has huge databases, we don't run on a per-sample basis but multiple + // samples at once. This replaces the ID of the particular process with the + // db_name instead to prevent `null` in job name, and in publishDir) + def updated_meta = new_meta + new_db_meta + updated_meta['id'] = updated_meta['db_name'] + + [ updated_meta, reads, db ] } .groupTuple(by: [0,2]) .multiMap { From e0ad49ebc9f22120ff001c08f3b21facf6038c22 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 10 Jun 2022 19:33:37 +0200 Subject: [PATCH 243/306] Fix metadata manipulaton for malt --- subworkflows/local/profiling.nf | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 7d35837..de5bea1 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -61,25 +61,29 @@ workflow PROFILING { // MALT: We groupTuple to have all samples in one channel for MALT as database // loading takes a long time, so we only want to run it once per database - // TODO document somewhere we only accept illumina short reads for MALT? ch_input_for_malt = ch_input_for_profiling.malt .filter { it[0]['instrument_platform'] == 'ILLUMINA' } .map { meta, reads, db_meta, db -> - def new_meta = meta.clone() + + // Reset entire input meta for MALT to just database name, + // as we don't run run on a per-sample basis due to huge datbaases + // so all samples are in one run and so sample-specific metadata + // unnecessary. Set as database name to prevent `null` job ID and prefix. + def temp_meta = [ id: meta['db_name'] ] + + // Extend database parameters to specify whether to save alignments or not def new_db_meta = db_meta.clone() - - // Add the saving of alignments in SAM format to params def sam_format = params.malt_save_reads ? ' --alignments ./ -za false' : "" new_db_meta['db_params'] = db_meta['db_params'] + sam_format - - // As MALT has huge databases, we don't run on a per-sample basis but multiple - // samples at once. This replaces the ID of the particular process with the - // db_name instead to prevent `null` in job name, and in publishDir) - def updated_meta = new_meta + new_db_meta - updated_meta['id'] = updated_meta['db_name'] - [ updated_meta, reads, db ] + // Combine reduced sample metadata with updated database parameters metadata, + // make sure id is db_name for publishing purposes. + def new_meta = temp_meta + new_db_meta + new_meta['id'] = new_meta['db_name'] + + [ new_meta, reads, db ] + } .groupTuple(by: [0,2]) .multiMap { From c1a7d6335f1b4c73067926563f8e2d7b0ba80d57 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 13 Jun 2022 21:02:12 +0200 Subject: [PATCH 244/306] Do not require fixed column order --- bin/check_samplesheet.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index d10ee90..d7ea5a9 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -37,6 +37,7 @@ def print_error(error, context="Line", context_str=""): print(error_str) sys.exit(1) + def check_samplesheet(file_in, file_out): """ This function checks that the samplesheet follows the following structure: @@ -87,10 +88,13 @@ def check_samplesheet(file_in, file_out): "fasta", ] header = [x.strip('"') for x in fin.readline().strip().split(",")] - if header[: len(HEADER)] != HEADER: + + ## Check for missing mandatory columns + missing_columns = list(set(HEADER) - set(header)) + if len(missing_columns) > 0: print( - "ERROR: Please check samplesheet header -> {} != {}".format( - ",".join(header), ",".join(HEADER) + "ERROR: Missing required column header -> {}. Note some columns can otherwise be empty. See pipeline documentation (https://nf-co.re/taxprofiler/usage).".format( + ",".join(missing_columns) ) ) sys.exit(1) @@ -173,7 +177,9 @@ def check_samplesheet(file_in, file_out): ## Auto-detect paired-end/single-end if sample and fastq_1 and fastq_2: ## Paired-end short reads sample_info.extend(["0", fastq_1, fastq_2, fasta]) - elif sample and fastq_1 and not fastq_2: ## Single-end short/long fastq reads + elif ( + sample and fastq_1 and not fastq_2 + ): ## Single-end short/long fastq reads sample_info.extend(["1", fastq_1, fastq_2, fasta]) elif ( sample and fasta and not fastq_1 and not fastq_2 From a84f693479340e5ae13f97576d76c42c7320d4c2 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Tue, 14 Jun 2022 08:13:36 -0500 Subject: [PATCH 245/306] Install KRAKENTOOLS_KREPORT2KRONA module --- modules.json | 3 ++ .../modules/krakentools/kreport2krona/main.nf | 36 ++++++++++++++++ .../krakentools/kreport2krona/meta.yml | 41 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 modules/nf-core/modules/krakentools/kreport2krona/main.nf create mode 100644 modules/nf-core/modules/krakentools/kreport2krona/meta.yml diff --git a/modules.json b/modules.json index a8f5a57..b9d9dba 100644 --- a/modules.json +++ b/modules.json @@ -48,6 +48,9 @@ "kraken2/kraken2": { "git_sha": "abe025677cdd805cc93032341ab19885473c1a07" }, + "krakentools/kreport2krona": { + "git_sha": "8b2a473f586bed003e72d2b183acc43fc0ddc422" + }, "malt/run": { "git_sha": "72b96f4e504eef673f2b5c13560a9d90b669129b" }, diff --git a/modules/nf-core/modules/krakentools/kreport2krona/main.nf b/modules/nf-core/modules/krakentools/kreport2krona/main.nf new file mode 100644 index 0000000..3bf46ee --- /dev/null +++ b/modules/nf-core/modules/krakentools/kreport2krona/main.nf @@ -0,0 +1,36 @@ +def VERSION = '1.2' // Version information not provided by tool on CLI + +process KRAKENTOOLS_KREPORT2KRONA { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::krakentools=1.2" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/krakentools:1.2--pyh5e36f6f_0': + 'quay.io/biocontainers/krakentools:1.2--pyh5e36f6f_0' }" + + input: + tuple val(meta), path(kreport) + + output: + tuple val(meta), path("*.txt"), emit: txt + 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}" + """ + kreport2krona.py \\ + -r ${kreport} \\ + -o ${prefix}.txt \\ + ${args} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + kreport2krona.py: ${VERSION} + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/krakentools/kreport2krona/meta.yml b/modules/nf-core/modules/krakentools/kreport2krona/meta.yml new file mode 100644 index 0000000..2f8a163 --- /dev/null +++ b/modules/nf-core/modules/krakentools/kreport2krona/meta.yml @@ -0,0 +1,41 @@ +name: krakentools_kreport2krona +description: Takes a Kraken report file and prints out a krona-compatible TEXT file +keywords: + - kraken + - krona + - metagenomics + - visualization +tools: + - krakentools: + description: KrakenTools is a suite of scripts to be used for post-analysis of Kraken/KrakenUniq/Kraken2/Bracken results. Please cite the relevant paper if using KrakenTools with any of the listed programs. + homepage: https://github.com/jenniferlu717/KrakenTools + licence: ["GPL v3"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - kreport: + type: file + description: Kraken report + pattern: "*.{txt,kreport}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - krona: + type: file + description: Krona text-based input file converted from Kraken report + pattern: "*.{txt,krona}" + +authors: + - "@MillironX" From 87678c127bb5ed16c05f860ff2df59a7c4524f19 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Tue, 14 Jun 2022 08:23:47 -0500 Subject: [PATCH 246/306] Add KRONA_CLEANUP process module --- modules/local/krona_cleanup.nf | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 modules/local/krona_cleanup.nf diff --git a/modules/local/krona_cleanup.nf b/modules/local/krona_cleanup.nf new file mode 100644 index 0000000..804cb69 --- /dev/null +++ b/modules/local/krona_cleanup.nf @@ -0,0 +1,40 @@ +process KRONA_CLEANUP { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "conda-forge::sed=4.7" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + 'biocontainers/biocontainers:v1.2.0_cv1' }" + + input: + tuple val(meta), path(krona, stageAs: 'uncleaned.krona.txt') + + output: + tuple val(meta), path("*.txt"), emit: txt + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + # Copy the file to a new name + cp ${krona} ${prefix}.txt + + # Remove ugly 'x__' prefixes for each of the taxonomic levels + LEVELS=(d k p c o f g s) + for L in "\${LEVELS[@]}"; do + sed -i "s/\${L}__//g" ${prefix}.txt + done + + # Remove underscores that are standing in place of spaces + sed -i "s/_/ /g" ${prefix}.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sed: \$(echo \$(sed --version 2>&1) | sed 's/^.*GNU sed) //; s/ .*\$//') + END_VERSIONS + """ +} From 5da8d5b7418cc778284f64110f7fe721a29aa148 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Tue, 14 Jun 2022 08:27:42 -0500 Subject: [PATCH 247/306] Install KRONA_KTIMPORTTEXT --- modules.json | 3 ++ .../modules/krona/ktimporttext/main.nf | 34 ++++++++++++++ .../modules/krona/ktimporttext/meta.yml | 47 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 modules/nf-core/modules/krona/ktimporttext/main.nf create mode 100644 modules/nf-core/modules/krona/ktimporttext/meta.yml diff --git a/modules.json b/modules.json index b9d9dba..4956cc8 100644 --- a/modules.json +++ b/modules.json @@ -51,6 +51,9 @@ "krakentools/kreport2krona": { "git_sha": "8b2a473f586bed003e72d2b183acc43fc0ddc422" }, + "krona/ktimporttext": { + "git_sha": "cdefbec66999c0b49d8bfeea9d6f9d19056635a2" + }, "malt/run": { "git_sha": "72b96f4e504eef673f2b5c13560a9d90b669129b" }, diff --git a/modules/nf-core/modules/krona/ktimporttext/main.nf b/modules/nf-core/modules/krona/ktimporttext/main.nf new file mode 100644 index 0000000..de0cfc2 --- /dev/null +++ b/modules/nf-core/modules/krona/ktimporttext/main.nf @@ -0,0 +1,34 @@ +process KRONA_KTIMPORTTEXT { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::krona=2.8.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/krona:2.8.1--pl5321hdfd78af_1': + 'quay.io/biocontainers/krona:2.8.1--pl5321hdfd78af_1' }" + + input: + tuple val(meta), path(report) + + output: + tuple val(meta), path ('*.html'), emit: html + 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}" + """ + ktImportText \\ + $args \\ + -o ${prefix}.html \\ + $report + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + krona: \$( echo \$(ktImportText 2>&1) | sed 's/^.*KronaTools //g; s/- ktImportText.*\$//g') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/krona/ktimporttext/meta.yml b/modules/nf-core/modules/krona/ktimporttext/meta.yml new file mode 100644 index 0000000..a7108e0 --- /dev/null +++ b/modules/nf-core/modules/krona/ktimporttext/meta.yml @@ -0,0 +1,47 @@ +name: "krona_ktimporttext" +description: Creates a Krona chart from text files listing quantities and lineages. +keywords: + - plot + - taxonomy + - interactive + - html + - visualisation + - krona chart + - metagenomics +tools: + - krona: + description: Krona Tools is a set of scripts to create Krona charts from several Bioinformatics tools as well as from text and XML files. + homepage: https://github.com/marbl/Krona/wiki/KronaTools + documentation: http://manpages.ubuntu.com/manpages/impish/man1/ktImportTaxonomy.1.html + tool_dev_url: https://github.com/marbl/Krona + doi: 10.1186/1471-2105-12-385 + licence: https://raw.githubusercontent.com/marbl/Krona/master/KronaTools/LICENSE.txt + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - report: + type: file + description: "Tab-delimited text file. Each line should be a number followed by a list of wedges to contribute to (starting from the highest level). If no wedges are listed (and just a quantity is given), it will contribute to the top level. If the same lineage is listed more than once, the values will be added. Quantities can be omitted if -q is specified. Lines beginning with '#' will be ignored." + pattern: "*.{txt}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test' ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - html: + type: file + description: A html file containing an interactive krona plot. + pattern: "*.{html}" + +authors: + - "@jianhong" From 2e7f26f380246ade93c56e50f5611fb8f0643b60 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 14 Jun 2022 20:09:48 +0000 Subject: [PATCH 248/306] Replace header tuple with sub-set list of only relevant columns for sample checking --- bin/check_samplesheet.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index d7ea5a9..4e80b46 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -99,9 +99,17 @@ def check_samplesheet(file_in, file_out): ) sys.exit(1) + ## Find locations of mandatory columns + header_locs = dict() + for i in HEADER: + header_locs[i] = header.index(i) + ## Check sample entries for line in fin: - lspl = [x.strip().strip('"') for x in line.strip().split(",")] + + ## Pull out only relevant columns for downstream checking + line_parsed = [x.strip().strip('"') for x in line.strip().split(",")] + lspl = [line_parsed[i] for i in header_locs.values()] # Check valid number of columns per row if len(lspl) < len(HEADER): @@ -121,6 +129,7 @@ def check_samplesheet(file_in, file_out): ) ## Check sample name entries + ( sample, run_accession, From 4ff2145e44d0c5b98f0d90cc43813cb343b2ee55 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 20 Jun 2022 08:23:14 +0200 Subject: [PATCH 249/306] Roll back MALT version --- modules.json | 2 +- modules/nf-core/modules/malt/run/main.nf | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules.json b/modules.json index a8f5a57..9561b0f 100644 --- a/modules.json +++ b/modules.json @@ -49,7 +49,7 @@ "git_sha": "abe025677cdd805cc93032341ab19885473c1a07" }, "malt/run": { - "git_sha": "72b96f4e504eef673f2b5c13560a9d90b669129b" + "git_sha": "be8d7b3293cac26cc63e4dbfb364deb8ed6ec7e5" }, "megan/rma2info": { "git_sha": "2d38566eca4cc15142b2ffa7c11837569b39aece" diff --git a/modules/nf-core/modules/malt/run/main.nf b/modules/nf-core/modules/malt/run/main.nf index 4e2e50c..2b91d90 100644 --- a/modules/nf-core/modules/malt/run/main.nf +++ b/modules/nf-core/modules/malt/run/main.nf @@ -2,10 +2,10 @@ process MALT_RUN { tag "$meta.id" label 'process_high' - conda (params.enable_conda ? "bioconda::malt=0.53" : null) + conda (params.enable_conda ? "bioconda::malt=0.41" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/malt:0.53--hdfd78af_0' : - 'quay.io/biocontainers/malt:0.53--hdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/malt:0.41--1' : + 'quay.io/biocontainers/malt:0.41--1' }" input: tuple val(meta), path(fastqs) @@ -33,7 +33,6 @@ process MALT_RUN { """ malt-run \\ - -J-Xmx${avail_mem}g \\ -t $task.cpus \\ -v \\ -o . \\ From 4552b53e757518cd51791f4f841a068bd8896da9 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Mon, 20 Jun 2022 08:30:23 +0200 Subject: [PATCH 250/306] Add note about MALT version to docs --- docs/usage.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index 9c47898..1893c10 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -213,6 +213,12 @@ Activating this functionality will concatenate the FASTQ files with the same sam You can optionally save the FASTQ output of the run merging with the `--save_runmerged_reads`. +##### Profiling + +###### MALT + +nf-core/taxprofiler uses MALT 0.4.1, which is a compatively old version. However it has been found that the most recent version of MALT (0.5.\*), at the time of writing, is broken. [The the LCA step appears not to be executed](http://megan.informatik.uni-tuebingen.de/t/lca-placement-failure-with-malt-v-0-5-2-and-0-5-3/1996/3), pushing all hits to the leaves of the taxonomy. However, if you need to use a more recent taxonomy map file with your databases, the output of `malt-build` from MALT 0.5.3 should be still be compatible with `malt-run` of 0.4.1. + ### 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: From a40a442178487dce25aa9f10109098976868a198 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Mon, 27 Jun 2022 08:22:47 -0500 Subject: [PATCH 251/306] Create VISUALIZATION_KRONA workflow to create Krona charts --- subworkflows/local/visualization_krona.nf | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 subworkflows/local/visualization_krona.nf diff --git a/subworkflows/local/visualization_krona.nf b/subworkflows/local/visualization_krona.nf new file mode 100644 index 0000000..87db112 --- /dev/null +++ b/subworkflows/local/visualization_krona.nf @@ -0,0 +1,55 @@ +// +// Create Krona visualizations +// + +include { KRAKENTOOLS_KREPORT2KRONA } from '../../modules/nf-core/modules/krakentools/kreport2krona/main' +include { KRONA_CLEANUP } from '../../modules/local/krona_cleanup' +include { KRONA_KTIMPORTTEXT } from '../../modules/nf-core/modules/krona/ktimporttext/main' + +workflow VISUALIZATION_KRONA { + take: + profiles + + main: + ch_krona_text = Channel.empty() + ch_krona_html = Channel.empty() + ch_versions = Channel.empty() + + /* + Split profile results based on tool they come from + */ + ch_input_profiles = profiles + .branch { + kraken2: it[0]['tool'] == 'kraken2' + unknown: true + } + + /* + Convert Kraken2 formatted reports into Krona text files + */ + ch_kraken_reports = ch_input_profiles.kraken2 + KRAKENTOOLS_KREPORT2KRONA ( ch_kraken_reports ) + ch_krona_text = ch_krona_text.mix( KRAKENTOOLS_KREPORT2KRONA.out.txt ) + ch_versions = ch_versions.mix( KRAKENTOOLS_KREPORT2KRONA.out.versions.first() ) + + /* + Remove taxonomy level annotations from the Krona text files + */ + KRONA_CLEANUP( ch_krona_text ) + ch_cleaned_krona_text = KRONA_CLEANUP.out.txt + ch_versions = ch_versions.mix( KRONA_CLEANUP.out.versions.first() ) + + /* + Convert Krona text files into html Krona visualizations + */ + ch_krona_text_for_import = ch_cleaned_krona_text + .map{[[id: it[0]['db_name']], it[1]]} + .groupTuple() + KRONA_KTIMPORTTEXT( ch_krona_text_for_import ) + ch_krona_html = ch_krona_html.mix( KRONA_KTIMPORTTEXT.out.html ) + ch_versions = ch_versions.mix( KRONA_KTIMPORTTEXT.out.versions.first() ) + + emit: + html = ch_krona_html + versions = ch_versions +} From ad9bc4b7e9d2ec09bcd8fd53a6ea67f86a1e2f73 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Mon, 20 Jun 2022 09:03:47 -0500 Subject: [PATCH 252/306] Add the visualization workflow to the main workflow --- workflows/taxprofiler.nf | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index f29a366..313790c 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -62,6 +62,7 @@ include { SHORTREAD_HOSTREMOVAL } from '../subworkflows/local/shortread_ include { LONGREAD_HOSTREMOVAL } from '../subworkflows/local/longread_hostremoval' include { SHORTREAD_COMPLEXITYFILTERING } from '../subworkflows/local/shortread_complexityfiltering' include { PROFILING } from '../subworkflows/local/profiling' +include { VISUALIZATION_KRONA } from '../subworkflows/local/visualization_krona' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -209,6 +210,12 @@ workflow TAXPROFILER { PROFILING ( ch_reads_runmerged, DB_CHECK.out.dbs ) ch_versions = ch_versions.mix( PROFILING.out.versions ) + /* + SUBWORKFLOW: VISUALIZATION_KRONA + */ + VISUALIZATION_KRONA ( PROFILING.out.profiles ) + ch_versions = ch_versions.mix( VISUALIZATION_KRONA.out.versions ) + /* MODULE: MultiQC */ From 0e8edd5b2f097e2c7a6c1b7a39fb5a19671395ce Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Tue, 14 Jun 2022 08:48:16 -0500 Subject: [PATCH 253/306] Add output configuration for Kraken Krona chart --- conf/modules.config | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/conf/modules.config b/conf/modules.config index 46320cf..f9d0329 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -293,6 +293,14 @@ process { ] } + withName: KRONA_KTIMPORTTEXT { + publishDir = [ + path: { "${params.outdir}/krona" }, + mode: params.publish_dir_mode, + pattern: '*.{html}' + ] + } + withName: METAPHLAN3 { ext.args = { "${meta.db_params}" } ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } From 15ac8b39fa8a2e6b490d05d69e1634f48eb35117 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Tue, 14 Jun 2022 10:54:10 -0500 Subject: [PATCH 254/306] Add citation for Krona --- CITATIONS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CITATIONS.md b/CITATIONS.md index 8044658..1ce4ec2 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -36,6 +36,10 @@ > Wood, Derrick E., Jennifer Lu, and Ben Langmead. 2019. Improved Metagenomic Analysis with Kraken 2. Genome Biology 20 (1): 257. doi: 10.1186/s13059-019-1891-0. +- [Krona](https://doi.org/10.1186/1471-2105-12-385) + + > Ondov, Brian D., Nicholas H. Bergman, and Adam M. Phillippy. 2011. Interactive metagenomic visualization in a Web browser. BMC Bioinformatics 12 (1): 385. doi: 10.1186/1471-2105-12-385. + - [MALT](https://doi.org/10.1038/s41559-017-0446-6) > Vågene, Åshild J., Alexander Herbig, Michael G. Campana, Nelly M. Robles García, Christina Warinner, Susanna Sabin, Maria A. Spyrou, et al. 2018. Salmonella Enterica Genomes from Victims of a Major Sixteenth-Century Epidemic in Mexico. Nature Ecology & Evolution 2 (3): 520-28. doi: 10.1038/s41559-017-0446-6. From 18284e6e32d1090336f37fd024bb5703cc80503b Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Mon, 27 Jun 2022 08:41:42 -0500 Subject: [PATCH 255/306] Add run_krona option to allow skipping Krona chart creation --- conf/test.config | 1 + conf/test_nopreprocessing.config | 1 + nextflow.config | 3 +++ nextflow_schema.json | 3 +++ workflows/taxprofiler.nf | 6 ++++-- 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/conf/test.config b/conf/test.config index e9fa62e..04dfb4d 100644 --- a/conf/test.config +++ b/conf/test.config @@ -37,6 +37,7 @@ params { run_metaphlan3 = true run_centrifuge = true run_diamond = true + run_krona = true malt_save_reads = true kraken2_save_reads = true centrifuge_save_reads = true diff --git a/conf/test_nopreprocessing.config b/conf/test_nopreprocessing.config index 60cdde8..92fd29d 100644 --- a/conf/test_nopreprocessing.config +++ b/conf/test_nopreprocessing.config @@ -37,6 +37,7 @@ params { run_metaphlan3 = true run_centrifuge = true run_diamond = true + run_krona = true } process { diff --git a/nextflow.config b/nextflow.config index 73fd0b3..29c87cd 100644 --- a/nextflow.config +++ b/nextflow.config @@ -125,6 +125,9 @@ params { run_diamond = false diamond_output_format = 'tsv' // TSV is only format with taxonomic information apparently diamond_save_reads = false // this will override default diamond output format so no taxonomic profile is generated! + + // krona + run_krona = false } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index 2c00348..682bf2f 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -433,6 +433,9 @@ }, "diamond_save_reads": { "type": "boolean" + }, + "run_krona": { + "type": "boolean" } } } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 313790c..f7ef07a 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -213,8 +213,10 @@ workflow TAXPROFILER { /* SUBWORKFLOW: VISUALIZATION_KRONA */ - VISUALIZATION_KRONA ( PROFILING.out.profiles ) - ch_versions = ch_versions.mix( VISUALIZATION_KRONA.out.versions ) + if ( params.run_krona ) { + VISUALIZATION_KRONA ( PROFILING.out.profiles ) + ch_versions = ch_versions.mix( VISUALIZATION_KRONA.out.versions ) + } /* MODULE: MultiQC From 23fec89b062d74b2052c31d982a52d301bc2809c Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Mon, 27 Jun 2022 08:52:29 -0500 Subject: [PATCH 256/306] Add Krona chart creation for Centrifuge reports --- subworkflows/local/visualization_krona.nf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subworkflows/local/visualization_krona.nf b/subworkflows/local/visualization_krona.nf index 87db112..b9f645d 100644 --- a/subworkflows/local/visualization_krona.nf +++ b/subworkflows/local/visualization_krona.nf @@ -20,6 +20,7 @@ workflow VISUALIZATION_KRONA { */ ch_input_profiles = profiles .branch { + centrifuge: it[0]['tool'] == 'centrifuge' kraken2: it[0]['tool'] == 'kraken2' unknown: true } @@ -28,6 +29,7 @@ workflow VISUALIZATION_KRONA { Convert Kraken2 formatted reports into Krona text files */ ch_kraken_reports = ch_input_profiles.kraken2 + .mix( ch_input_profiles.centrifuge ) KRAKENTOOLS_KREPORT2KRONA ( ch_kraken_reports ) ch_krona_text = ch_krona_text.mix( KRAKENTOOLS_KREPORT2KRONA.out.txt ) ch_versions = ch_versions.mix( KRAKENTOOLS_KREPORT2KRONA.out.versions.first() ) From 5b80c7cab9e65702338f2c93e1f9dcfe06ecba28 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Mon, 27 Jun 2022 08:58:28 -0500 Subject: [PATCH 257/306] Install KAIJU_KAIJU2KRONA --- modules.json | 3 ++ .../nf-core/modules/kaiju/kaiju2krona/main.nf | 39 ++++++++++++++++ .../modules/kaiju/kaiju2krona/meta.yml | 44 +++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 modules/nf-core/modules/kaiju/kaiju2krona/main.nf create mode 100644 modules/nf-core/modules/kaiju/kaiju2krona/meta.yml diff --git a/modules.json b/modules.json index 4956cc8..1770a53 100644 --- a/modules.json +++ b/modules.json @@ -42,6 +42,9 @@ "kaiju/kaiju": { "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" }, + "kaiju/kaiju2krona": { + "git_sha": "2f0b19240430de6807b1232e6d9d0e8084e8a28f" + }, "kaiju/kaiju2table": { "git_sha": "538dbac98ba9c8f799536cd5a617195501439457" }, diff --git a/modules/nf-core/modules/kaiju/kaiju2krona/main.nf b/modules/nf-core/modules/kaiju/kaiju2krona/main.nf new file mode 100644 index 0000000..c95d5a7 --- /dev/null +++ b/modules/nf-core/modules/kaiju/kaiju2krona/main.nf @@ -0,0 +1,39 @@ +process KAIJU_KAIJU2KRONA { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::kaiju=1.8.2" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/kaiju:1.8.2--h5b5514e_1': + 'quay.io/biocontainers/kaiju:1.8.2--h5b5514e_1' }" + + input: + tuple val(meta), path(tsv) + path(db) + + output: + tuple val(meta), path("*.txt"), emit: txt + 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}" + """ + dbnodes=`find -L ${db} -name "*nodes.dmp"` + dbnames=`find -L ${db} -name "*names.dmp"` + kaiju2krona \\ + $args \\ + -t \$dbnodes \\ + -n \$dbnames \\ + -i ${tsv} \\ + -o ${prefix}.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + kaiju: \$(echo \$( kaiju -h 2>&1 | sed -n 1p | sed 's/^.*Kaiju //' )) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/kaiju/kaiju2krona/meta.yml b/modules/nf-core/modules/kaiju/kaiju2krona/meta.yml new file mode 100644 index 0000000..a0dc2fd --- /dev/null +++ b/modules/nf-core/modules/kaiju/kaiju2krona/meta.yml @@ -0,0 +1,44 @@ +name: kaiju_kaiju2krona +description: Convert Kaiju's tab-separated output file into a tab-separated text file which can be imported into Krona. +keywords: + - taxonomy + - visualisation + - krona chart + - metagenomics +tools: + - "kaiju": + description: Fast and sensitive taxonomic classification for metagenomics + homepage: https://kaiju.binf.ku.dk/ + documentation: https://github.com/bioinformatics-centre/kaiju/blob/master/README.md + tool_dev_url: https://github.com/bioinformatics-centre/kaiju + doi: "10.1038/ncomms11257" + licence: ["GNU GPL v3"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - tsv: + type: file + description: Kaiju tab-separated output file + pattern: "*.{tsv,txt}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - txt: + type: file + description: Krona text-based input file converted from Kaiju report + pattern: "*.{txt,krona}" + +authors: + - "@MillironX" From 821dd844d8e7d87db095b2dc18ddb98cd01a81b4 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Mon, 27 Jun 2022 09:23:20 -0500 Subject: [PATCH 258/306] Add Kaiju profile conversion to visualization workflow --- subworkflows/local/visualization_krona.nf | 22 ++++++++++++++++++++++ workflows/taxprofiler.nf | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/visualization_krona.nf b/subworkflows/local/visualization_krona.nf index b9f645d..31dd5bc 100644 --- a/subworkflows/local/visualization_krona.nf +++ b/subworkflows/local/visualization_krona.nf @@ -2,6 +2,7 @@ // Create Krona visualizations // +include { KAIJU_KAIJU2KRONA } from '../../modules/nf-core/modules/kaiju/kaiju2krona/main' include { KRAKENTOOLS_KREPORT2KRONA } from '../../modules/nf-core/modules/krakentools/kreport2krona/main' include { KRONA_CLEANUP } from '../../modules/local/krona_cleanup' include { KRONA_KTIMPORTTEXT } from '../../modules/nf-core/modules/krona/ktimporttext/main' @@ -9,6 +10,7 @@ include { KRONA_KTIMPORTTEXT } from '../../modules/nf-core/modules/krona/ workflow VISUALIZATION_KRONA { take: profiles + databases main: ch_krona_text = Channel.empty() @@ -21,6 +23,7 @@ workflow VISUALIZATION_KRONA { ch_input_profiles = profiles .branch { centrifuge: it[0]['tool'] == 'centrifuge' + kaiju: it[0]['tool'] == 'kaiju' kraken2: it[0]['tool'] == 'kraken2' unknown: true } @@ -34,6 +37,25 @@ workflow VISUALIZATION_KRONA { ch_krona_text = ch_krona_text.mix( KRAKENTOOLS_KREPORT2KRONA.out.txt ) ch_versions = ch_versions.mix( KRAKENTOOLS_KREPORT2KRONA.out.versions.first() ) + /* + Combine Kaiju profiles with their databases + */ + ch_input_for_kaiju2krona = ch_input_profiles.kaiju + .map{ [it[0]['db_name'], it[0], it[1]] } + .combine( databases.map{ [it[0]['db_name'], it[1]] }, by: 0 ) + .multiMap{ + it -> + profiles: [it[1], it[2]] + db: it[3] + } + + /* + Convert Kaiju formatted reports into Krona text files + */ + KAIJU_KAIJU2KRONA( ch_input_for_kaiju2krona.profiles, ch_input_for_kaiju2krona.db ) + ch_krona_text = ch_krona_text.mix( KAIJU_KAIJU2KRONA.out.txt ) + ch_versions = ch_versions.mix( KAIJU_KAIJU2KRONA.out.versions.first() ) + /* Remove taxonomy level annotations from the Krona text files */ diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index f7ef07a..79a2bfd 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -214,7 +214,7 @@ workflow TAXPROFILER { SUBWORKFLOW: VISUALIZATION_KRONA */ if ( params.run_krona ) { - VISUALIZATION_KRONA ( PROFILING.out.profiles ) + VISUALIZATION_KRONA ( PROFILING.out.profiles, DB_CHECK.out.dbs ) ch_versions = ch_versions.mix( VISUALIZATION_KRONA.out.versions ) } From af854f5f346c93f1b1eae655ac5e1808ced1a39c Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 28 Jun 2022 12:42:41 +0200 Subject: [PATCH 259/306] Update mimetype for database csv as per https://github.com/nf-core/mag/pull/325/files --- nextflow_schema.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nextflow_schema.json b/nextflow_schema.json index 682bf2f..fe77913 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -260,6 +260,8 @@ "properties": { "databases": { "type": "string", + "mimetype": "text/csv", + "format": "file-path", "default": "None" }, "shortread_qc_excludeunmerged": { From 4de43040a3403dae9b85679cdda9719ff2bb7117 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Mon, 27 Jun 2022 12:12:16 -0500 Subject: [PATCH 260/306] Switch to using raw classifications for Kaiju2Krona --- subworkflows/local/profiling.nf | 25 ++++++++++++++--------- subworkflows/local/visualization_krona.nf | 9 ++++++-- workflows/taxprofiler.nf | 2 +- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index de5bea1..45c49f5 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -21,7 +21,8 @@ workflow PROFILING { main: ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() - ch_raw_profiles = Channel.empty() + ch_raw_classifications = Channel.empty() + ch_raw_profiles = Channel.empty() /* COMBINE READS WITH POSSIBLE DATABASES @@ -110,6 +111,7 @@ workflow PROFILING { MEGAN_RMA2INFO (ch_maltrun_for_megan, params.malt_generate_megansummary ) ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( MALT_RUN.out.versions.first(), MEGAN_RMA2INFO.out.versions.first() ) + ch_raw_classifications = ch_raw_classifications.mix( ch_maltrun_for_megan ) ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) } @@ -124,9 +126,10 @@ workflow PROFILING { } KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db, params.kraken2_save_reads, params.kraken2_save_readclassification ) - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) - ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.report ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report.collect{it[1]}.ifEmpty([]) ) + ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) + ch_raw_classifications = ch_raw_classifications.mix( KRAKEN2_KRAKEN2.out.classified_reads_assignment ) + ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.report ) } @@ -145,8 +148,9 @@ workflow PROFILING { CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_reads, params.centrifuge_save_reads, params.centrifuge_save_reads ) CENTRIFUGE_KREPORT (CENTRIFUGE_CENTRIFUGE.out.results, ch_input_for_centrifuge.db) - ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) - ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_KREPORT.out.kreport ) + ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) + ch_raw_classifications = ch_raw_classifications.mix( CENTRIFUGE_CENTRIFUGE.out.results ) + ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_KREPORT.out.kreport ) } @@ -182,6 +186,7 @@ workflow PROFILING { KAIJU_KAIJU2TABLE (KAIJU_KAIJU.out.results, ch_input_for_kaiju.db, params.kaiju_taxon_name) ch_multiqc_files = ch_multiqc_files.mix( KAIJU_KAIJU2TABLE.out.summary.collect{it[1]}.ifEmpty([]) ) ch_versions = ch_versions.mix( KAIJU_KAIJU.out.versions.first() ) + ch_raw_classifications = ch_raw_classifications.mix( KAIJU_KAIJU.out.results ) ch_raw_profiles = ch_raw_profiles.mix( KAIJU_KAIJU2TABLE.out.summary ) } @@ -206,8 +211,8 @@ workflow PROFILING { } emit: - profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom - versions = ch_versions // channel: [ versions.yml ] - mqc = ch_multiqc_files + classifications = ch_raw_classifications + profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom + versions = ch_versions // channel: [ versions.yml ] + mqc = ch_multiqc_files } - diff --git a/subworkflows/local/visualization_krona.nf b/subworkflows/local/visualization_krona.nf index 31dd5bc..c5ca97a 100644 --- a/subworkflows/local/visualization_krona.nf +++ b/subworkflows/local/visualization_krona.nf @@ -9,6 +9,7 @@ include { KRONA_KTIMPORTTEXT } from '../../modules/nf-core/modules/krona/ workflow VISUALIZATION_KRONA { take: + classifications profiles databases @@ -23,10 +24,14 @@ workflow VISUALIZATION_KRONA { ch_input_profiles = profiles .branch { centrifuge: it[0]['tool'] == 'centrifuge' - kaiju: it[0]['tool'] == 'kaiju' kraken2: it[0]['tool'] == 'kraken2' unknown: true } + ch_input_classifications = classifications + .branch { + kaiju: it[0]['tool'] == 'kaiju' + unknown: true + } /* Convert Kraken2 formatted reports into Krona text files @@ -40,7 +45,7 @@ workflow VISUALIZATION_KRONA { /* Combine Kaiju profiles with their databases */ - ch_input_for_kaiju2krona = ch_input_profiles.kaiju + ch_input_for_kaiju2krona = ch_input_classifications.kaiju .map{ [it[0]['db_name'], it[0], it[1]] } .combine( databases.map{ [it[0]['db_name'], it[1]] }, by: 0 ) .multiMap{ diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 79a2bfd..7eec13d 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -214,7 +214,7 @@ workflow TAXPROFILER { SUBWORKFLOW: VISUALIZATION_KRONA */ if ( params.run_krona ) { - VISUALIZATION_KRONA ( PROFILING.out.profiles, DB_CHECK.out.dbs ) + VISUALIZATION_KRONA ( PROFILING.out.classifications, PROFILING.out.profiles, DB_CHECK.out.dbs ) ch_versions = ch_versions.mix( VISUALIZATION_KRONA.out.versions ) } From 069370db8610aedd557438b0f071771cf97832fd Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Mon, 27 Jun 2022 13:39:42 -0500 Subject: [PATCH 261/306] Add unclassified and verbose flags to kaiju2krona --- conf/modules.config | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conf/modules.config b/conf/modules.config index f9d0329..6563cc9 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -351,6 +351,10 @@ process { ] } + withName: KAIJU_KAIJU2KRONA { + ext.args = '-v -u' + } + withName: DIAMOND_BLASTX { ext.args = { "${meta.db_params}" } ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } From 6673ccf8d8bb0883d5b066e16ac59293830a80c1 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Tue, 28 Jun 2022 13:13:37 +0000 Subject: [PATCH 262/306] Update channel assignment alignment in profiling subworkflow Co-authored-by: James A. Fellows Yates --- subworkflows/local/profiling.nf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 45c49f5..c10ef5b 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -109,10 +109,10 @@ workflow PROFILING { } MEGAN_RMA2INFO (ch_maltrun_for_megan, params.malt_generate_megansummary ) - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) - ch_versions = ch_versions.mix( MALT_RUN.out.versions.first(), MEGAN_RMA2INFO.out.versions.first() ) + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) + ch_versions = ch_versions.mix( MALT_RUN.out.versions.first(), MEGAN_RMA2INFO.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( ch_maltrun_for_megan ) - ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) + ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) } From 0050f453456cdf7007881b2a7e4244c03a6a6c2c Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 30 Jun 2022 11:43:52 +0200 Subject: [PATCH 263/306] Add taxonomy directory param --- nextflow.config | 1 + subworkflows/local/visualization_krona.nf | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/nextflow.config b/nextflow.config index 29c87cd..88b8d43 100644 --- a/nextflow.config +++ b/nextflow.config @@ -128,6 +128,7 @@ params { // krona run_krona = false + krona_taxonomy_directory = null } // Load base.config by default for all pipelines diff --git a/subworkflows/local/visualization_krona.nf b/subworkflows/local/visualization_krona.nf index b9f645d..d945184 100644 --- a/subworkflows/local/visualization_krona.nf +++ b/subworkflows/local/visualization_krona.nf @@ -51,6 +51,10 @@ workflow VISUALIZATION_KRONA { ch_krona_html = ch_krona_html.mix( KRONA_KTIMPORTTEXT.out.html ) ch_versions = ch_versions.mix( KRONA_KTIMPORTTEXT.out.versions.first() ) + /* + Convert Krona + */ + emit: html = ch_krona_html versions = ch_versions From 02c06b9f7bc24085cd271cd100f47e02373ff60d Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 30 Jun 2022 18:33:11 +0200 Subject: [PATCH 264/306] Add support for MALT display in Krona, tweaks other krona modules to dispaly tool name in output file --- conf/modules.config | 27 ++++++++++- modules.json | 6 +++ modules/nf-core/modules/gunzip/main.nf | 34 ++++++++++++++ modules/nf-core/modules/gunzip/meta.yml | 34 ++++++++++++++ .../modules/krona/ktimporttaxonomy/main.nf | 38 +++++++++++++++ .../modules/krona/ktimporttaxonomy/meta.yml | 47 +++++++++++++++++++ subworkflows/local/visualization_krona.nf | 21 ++++++++- workflows/taxprofiler.nf | 2 + 8 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 modules/nf-core/modules/gunzip/main.nf create mode 100644 modules/nf-core/modules/gunzip/meta.yml create mode 100644 modules/nf-core/modules/krona/ktimporttaxonomy/main.nf create mode 100644 modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml diff --git a/conf/modules.config b/conf/modules.config index 6563cc9..098d9bb 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -273,7 +273,7 @@ process { ] } - withName: MEGAN_RMA2INFO { + withName: 'NFCORE_TAXPROFILER:TAXPROFILER:PROFILING:MEGAN_RMA2INFO' { ext.args = "-c2c Taxonomy" ext.prefix = { "${meta.id}" } publishDir = [ @@ -293,7 +293,32 @@ process { ] } + withName: KRONA_CLEANUP { + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + publishDir = [ + path: { "${params.outdir}/krona" }, + mode: params.publish_dir_mode, + pattern: '*.{html}' + ] + } + withName: KRONA_KTIMPORTTEXT { + ext.prefix = { "${meta.tool}-${meta.id}" } + publishDir = [ + path: { "${params.outdir}/krona" }, + mode: params.publish_dir_mode, + pattern: '*.{html}' + ] + } + + withName: 'NFCORE_TAXPROFILER:TAXPROFILER:VISUALIZATION_KRONA:MEGAN_RMA2INFO' { + ext.args = { "--read2class Taxonomy" } + ext.prefix = { "${meta.id}-${meta.db_name}" } + } + + withName: KRONA_KTIMPORTTAXONOMY { + ext.args = "-i" + ext.prefix = { "${meta.tool}-${meta.id}" } publishDir = [ path: { "${params.outdir}/krona" }, mode: params.publish_dir_mode, diff --git a/modules.json b/modules.json index 7da0e79..13c6592 100644 --- a/modules.json +++ b/modules.json @@ -39,6 +39,9 @@ "filtlong": { "git_sha": "089f761f0bf79c4a486f1df9b6205f650196a2c1" }, + "gunzip": { + "git_sha": "9aadd9a6d3f5964476582319b3a1c54a3e3fe7c9" + }, "kaiju/kaiju": { "git_sha": "8856f127c58f6af479128be8b8df4d42e442ddbe" }, @@ -54,6 +57,9 @@ "krakentools/kreport2krona": { "git_sha": "8b2a473f586bed003e72d2b183acc43fc0ddc422" }, + "krona/ktimporttaxonomy": { + "git_sha": "233fa70811a03a4cecb2ece483b5c8396e2cee1d" + }, "krona/ktimporttext": { "git_sha": "cdefbec66999c0b49d8bfeea9d6f9d19056635a2" }, diff --git a/modules/nf-core/modules/gunzip/main.nf b/modules/nf-core/modules/gunzip/main.nf new file mode 100644 index 0000000..61bf1af --- /dev/null +++ b/modules/nf-core/modules/gunzip/main.nf @@ -0,0 +1,34 @@ +process GUNZIP { + tag "$archive" + label 'process_low' + + conda (params.enable_conda ? "conda-forge::sed=4.7" : null) + 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(archive) + + output: + tuple val(meta), path("$gunzip"), emit: gunzip + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + gunzip = archive.toString() - '.gz' + """ + gunzip \\ + -f \\ + $args \\ + $archive + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + gunzip: \$(echo \$(gunzip --version 2>&1) | sed 's/^.*(gzip) //; s/ Copyright.*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/gunzip/meta.yml b/modules/nf-core/modules/gunzip/meta.yml new file mode 100644 index 0000000..4d2ebc8 --- /dev/null +++ b/modules/nf-core/modules/gunzip/meta.yml @@ -0,0 +1,34 @@ +name: gunzip +description: Compresses and decompresses files. +keywords: + - gunzip + - compression +tools: + - gunzip: + description: | + gzip is a file format and a software application used for file compression and decompression. + documentation: https://www.gnu.org/software/gzip/manual/gzip.html + licence: ["GPL-3.0-or-later"] +input: + - meta: + type: map + description: | + Optional groovy Map containing meta information + e.g. [ id:'test', single_end:false ] + - archive: + type: file + description: File to be compressed/uncompressed + pattern: "*.*" +output: + - gunzip: + type: file + description: Compressed/uncompressed file + pattern: "*.*" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@joseespinosa" + - "@drpatelh" + - "@jfy133" diff --git a/modules/nf-core/modules/krona/ktimporttaxonomy/main.nf b/modules/nf-core/modules/krona/ktimporttaxonomy/main.nf new file mode 100644 index 0000000..5ea9e9e --- /dev/null +++ b/modules/nf-core/modules/krona/ktimporttaxonomy/main.nf @@ -0,0 +1,38 @@ +process KRONA_KTIMPORTTAXONOMY { + tag "${meta.id}" + label 'process_high' + + // WARN: Version information not provided by tool on CLI. Please update version string below when bumping container versions. + conda (params.enable_conda ? "bioconda::krona=2.8" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/krona:2.8--pl5262hdfd78af_2' : + 'quay.io/biocontainers/krona:2.8--pl5262hdfd78af_2' }" + + input: + tuple val(meta), path(report) + path taxonomy + + output: + tuple val(meta), path ('*.html'), emit: html + 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}" + def VERSION = '2.8' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. + """ + ktImportTaxonomy \\ + $args \\ + -o ${prefix}.html \\ + -tax $taxonomy \\ + $report + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + krona: $VERSION + END_VERSIONS + """ +} \ No newline at end of file diff --git a/modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml b/modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml new file mode 100644 index 0000000..df0ad1c --- /dev/null +++ b/modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml @@ -0,0 +1,47 @@ +name: krona_ktimporttaxonomy +description: KronaTools Import Taxonomy imports taxonomy classifications and produces an interactive Krona plot. +keywords: + - plot + - taxonomy + - interactive + - html + - visualisation + - krona chart +tools: + - krona: + description: Krona Tools is a set of scripts to create Krona charts from several Bioinformatics tools as well as from text and XML files. + homepage: https://github.com/marbl/Krona/wiki/KronaTools + documentation: http://manpages.ubuntu.com/manpages/impish/man1/ktImportTaxonomy.1.html + tool_dev_url: + doi: https://doi.org/10.1186/1471-2105-12-385 + licence: + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - database: + type: file + description: | + Path to the taxonomy database .tab file downloaded by krona/ktUpdateTaxonomy + The file will be saved under a folder named "taxonomy" as "taxonomy/taxonomy.tab". + The parent folder will be passed as argument to ktImportTaxonomy. + - report: + type: file + description: "A tab-delimited file with taxonomy IDs and (optionally) query IDs, magnitudes, and scores. Query IDs are taken from column 1, taxonomy IDs from column 2, and scores from column 3. Lines beginning with # will be ignored." + pattern: "*.{tsv}" + +output: + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - html: + type: file + description: A html file containing an interactive krona plot. + pattern: "*.{html}" + +authors: + - "@mjakobs" diff --git a/subworkflows/local/visualization_krona.nf b/subworkflows/local/visualization_krona.nf index aac7475..7a94fc6 100644 --- a/subworkflows/local/visualization_krona.nf +++ b/subworkflows/local/visualization_krona.nf @@ -2,10 +2,13 @@ // Create Krona visualizations // +include { MEGAN_RMA2INFO } from '../../modules/nf-core/modules/megan/rma2info/main' include { KAIJU_KAIJU2KRONA } from '../../modules/nf-core/modules/kaiju/kaiju2krona/main' include { KRAKENTOOLS_KREPORT2KRONA } from '../../modules/nf-core/modules/krakentools/kreport2krona/main' include { KRONA_CLEANUP } from '../../modules/local/krona_cleanup' include { KRONA_KTIMPORTTEXT } from '../../modules/nf-core/modules/krona/ktimporttext/main' +include { KRONA_KTIMPORTTAXONOMY } from '../../modules/nf-core/modules/krona/ktimporttaxonomy/main' +include { GUNZIP } from '../../modules/nf-core/modules/gunzip/main' workflow VISUALIZATION_KRONA { take: @@ -30,6 +33,7 @@ workflow VISUALIZATION_KRONA { ch_input_classifications = classifications .branch { kaiju: it[0]['tool'] == 'kaiju' + malt: it[0]['tool'] == 'malt' unknown: true } @@ -72,15 +76,28 @@ workflow VISUALIZATION_KRONA { Convert Krona text files into html Krona visualizations */ ch_krona_text_for_import = ch_cleaned_krona_text - .map{[[id: it[0]['db_name']], it[1]]} + .map{[[id: it[0]['db_name'], tool: it[0]['tool']], it[1]]} .groupTuple() + .dump(tag: "text") KRONA_KTIMPORTTEXT( ch_krona_text_for_import ) ch_krona_html = ch_krona_html.mix( KRONA_KTIMPORTTEXT.out.html ) ch_versions = ch_versions.mix( KRONA_KTIMPORTTEXT.out.versions.first() ) /* - Convert Krona + Convert MALT/MEGAN RMA2INFO files into html Krona visualisations */ + if ( params.krona_taxonomy_directory ) { + MEGAN_RMA2INFO ( ch_input_classifications.malt, false ) + GUNZIP ( MEGAN_RMA2INFO.out.txt ) + ch_krona_taxonomy_for_input = GUNZIP.out.gunzip + .map{[[id: it[0]['db_name'], tool: it[0]['tool']], it[1]]} + .groupTuple() + .dump(tag: "taxonomy") + KRONA_KTIMPORTTAXONOMY ( ch_krona_taxonomy_for_input, file(params.krona_taxonomy_directory, checkExists: true) ) + ch_krona_html.mix( KRONA_KTIMPORTTAXONOMY.out.html ) + ch_versions = ch_versions.mix( MEGAN_RMA2INFO.out.versions.first() ) + ch_versions = ch_versions.mix( KRONA_KTIMPORTTAXONOMY.out.versions.first() ) + } emit: html = ch_krona_html diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 7eec13d..2037649 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -35,6 +35,8 @@ if (params.longread_hostremoval_index ) { ch_longread_reference_index = fi if (params.diamond_save_reads ) log.warn "[nf-core/taxprofiler] DIAMOND only allows output of a single format. As --diamond_save_reads supplied, only aligned reads in SAM format will be produced, no taxonomic profiles will be available." +if (params.run_malt && params.run_krona && !params.krona_taxonomy_directory) log.warn "[nf-core/taxprofiler] Krona can only be run on MALT output if path to Krona taxonomy database supplied to --krona_taxonomy_directory. Krona will not be executed in this run for MALT." + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIG FILES From 97e6cdf4b4a8944fc60fb12389f4a9ab868aa216 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Thu, 30 Jun 2022 18:36:13 +0200 Subject: [PATCH 265/306] Add Krona taxonomy DB to schema build --- nextflow_schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nextflow_schema.json b/nextflow_schema.json index fe77913..78635a7 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -438,6 +438,10 @@ }, "run_krona": { "type": "boolean" + }, + "krona_taxonomy_directory": { + "type": "string", + "default": null } } } From 447b8f09057bbd994385ae4490e30c53d599e6b0 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 1 Jul 2022 17:21:19 +0200 Subject: [PATCH 266/306] Get fixed Krona module and add Krona taxonomy to test profile --- conf/test.config | 1 + modules.json | 2 +- modules/nf-core/modules/krona/ktimporttaxonomy/main.nf | 9 ++++++--- modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml | 7 ++++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/conf/test.config b/conf/test.config index 04dfb4d..375f463 100644 --- a/conf/test.config +++ b/conf/test.config @@ -38,6 +38,7 @@ params { run_centrifuge = true run_diamond = true run_krona = true + krona_taxonomy_directory = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/sarscov2/metagenome/krona_taxonomy.tab' malt_save_reads = true kraken2_save_reads = true centrifuge_save_reads = true diff --git a/modules.json b/modules.json index 13c6592..b4c13f0 100644 --- a/modules.json +++ b/modules.json @@ -58,7 +58,7 @@ "git_sha": "8b2a473f586bed003e72d2b183acc43fc0ddc422" }, "krona/ktimporttaxonomy": { - "git_sha": "233fa70811a03a4cecb2ece483b5c8396e2cee1d" + "git_sha": "0e9fd9370ad1845870b8a9c63fcc47d999a1739e" }, "krona/ktimporttext": { "git_sha": "cdefbec66999c0b49d8bfeea9d6f9d19056635a2" diff --git a/modules/nf-core/modules/krona/ktimporttaxonomy/main.nf b/modules/nf-core/modules/krona/ktimporttaxonomy/main.nf index 5ea9e9e..9b03462 100644 --- a/modules/nf-core/modules/krona/ktimporttaxonomy/main.nf +++ b/modules/nf-core/modules/krona/ktimporttaxonomy/main.nf @@ -10,7 +10,7 @@ process KRONA_KTIMPORTTAXONOMY { input: tuple val(meta), path(report) - path taxonomy + path taxonomy, stageAs: 'taxonomy.tab' output: tuple val(meta), path ('*.html'), emit: html @@ -24,10 +24,13 @@ process KRONA_KTIMPORTTAXONOMY { def prefix = task.ext.prefix ?: "${meta.id}" def VERSION = '2.8' // WARN: Version information not provided by tool on CLI. Please update this string when bumping container versions. """ + TAXONOMY=\$(find -L . -name '*.tab' -exec dirname {} \\;) + echo \$TAXONOMY + ktImportTaxonomy \\ $args \\ -o ${prefix}.html \\ - -tax $taxonomy \\ + -tax \$TAXONOMY/ \\ $report cat <<-END_VERSIONS > versions.yml @@ -35,4 +38,4 @@ process KRONA_KTIMPORTTAXONOMY { krona: $VERSION END_VERSIONS """ -} \ No newline at end of file +} diff --git a/modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml b/modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml index df0ad1c..0fd7d5f 100644 --- a/modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml +++ b/modules/nf-core/modules/krona/ktimporttaxonomy/meta.yml @@ -25,9 +25,10 @@ input: - database: type: file description: | - Path to the taxonomy database .tab file downloaded by krona/ktUpdateTaxonomy - The file will be saved under a folder named "taxonomy" as "taxonomy/taxonomy.tab". - The parent folder will be passed as argument to ktImportTaxonomy. + Path to a Krona taxonomy .tab file normally downloaded and generated by + krona/ktUpdateTaxonomy. Custom taxonomy files can have any name, but + must end in `.tab`. + pattern: "*tab" - report: type: file description: "A tab-delimited file with taxonomy IDs and (optionally) query IDs, magnitudes, and scores. Query IDs are taken from column 1, taxonomy IDs from column 2, and scores from column 3. Lines beginning with # will be ignored." From a0ee82bf43ad79ef94b5b3a8e7f9dc72ae30e18a Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 12 Jul 2022 11:39:26 +0200 Subject: [PATCH 267/306] Add motus/merge and biom support --- conf/modules.config | 8 +++ conf/test_motus.config | 1 + modules.json | 5 +- modules/nf-core/modules/motus/merge/main.nf | 47 +++++++++++++++ modules/nf-core/modules/motus/merge/meta.yml | 57 ++++++++++++++++++ modules/nf-core/modules/motus/profile/main.nf | 2 +- nextflow.config | 4 ++ nextflow_schema.json | 60 ++++++++++++++++--- subworkflows/local/profiling.nf | 1 + .../local/standardisation_profiles.nf | 56 +++++++++++++++++ subworkflows/local/visualization_krona.nf | 4 +- workflows/taxprofiler.nf | 9 +++ 12 files changed, 241 insertions(+), 13 deletions(-) create mode 100644 modules/nf-core/modules/motus/merge/main.nf create mode 100644 modules/nf-core/modules/motus/merge/meta.yml create mode 100644 subworkflows/local/standardisation_profiles.nf diff --git a/conf/modules.config b/conf/modules.config index b858ec3..1558a98 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -391,12 +391,20 @@ process { } withName: MOTUS_PROFILE { + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/motus/${meta.db_name}" }, mode: params.publish_dir_mode ] } + withName: MOTUS_MERGE { + publishDir = [ + path: { "${params.outdir}/motus/" }, + mode: params.publish_dir_mode + ] + } + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ path: { "${params.outdir}/pipeline_info" }, diff --git a/conf/test_motus.config b/conf/test_motus.config index 9d39ad4..1405447 100644 --- a/conf/test_motus.config +++ b/conf/test_motus.config @@ -38,4 +38,5 @@ params { run_centrifuge = false run_diamond = false run_motus = true + run_profile_standardisation = true } diff --git a/modules.json b/modules.json index 1d40748..f98cd62 100644 --- a/modules.json +++ b/modules.json @@ -78,8 +78,11 @@ "minimap2/index": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, + "motus/merge": { + "git_sha": "b02e648c221e1da17cb589eefe297e61ec9e9c49" + }, "motus/profile": { - "git_sha": "6b960f0e75bbb4d5bd301cd3875fa078d0eab4d1" + "git_sha": "b02e648c221e1da17cb589eefe297e61ec9e9c49" }, "multiqc": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" diff --git a/modules/nf-core/modules/motus/merge/main.nf b/modules/nf-core/modules/motus/merge/main.nf new file mode 100644 index 0000000..01ca5a2 --- /dev/null +++ b/modules/nf-core/modules/motus/merge/main.nf @@ -0,0 +1,47 @@ +VERSION = '3.0.1' + +process MOTUS_MERGE { + label 'process_low' + + conda (params.enable_conda ? "bioconda::motus=3.0.1" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/motus:3.0.1--pyhdfd78af_0': + 'quay.io/biocontainers/motus:3.0.1--pyhdfd78af_0' }" + + input: + path input + path db // to stop docker saying it can't find it... would have to have the module in upstream steps anyway + path profile_version_yml, stageAs: 'profile_version.yml' + val biom_format + + output: + path("*.txt") , optional: true, emit: txt + path("*.biom"), optional: true, emit: biom + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = 'motus_merged' + def cmd_input = input.size() > 1 ? "-i ${input.join(',')}" : input.isDirectory() ? "-d ${input}" : "-i ${input}" + def output = biom_format ? "-B -o ${prefix}.biom" : "-o ${prefix}.txt" + """ + motus \\ + merge \\ + -db $db \\ + ${cmd_input} \\ + $args \\ + ${output} + + ## Take version from the mOTUs/profile module output, as cannot reconstruct + ## version without having database staged in this directory. + VERSION=\$(cat ${profile_version_yml} | grep '/*motus:.*' | sed 's/.*otus: //g') + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + motus: \$VERSION + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/motus/merge/meta.yml b/modules/nf-core/modules/motus/merge/meta.yml new file mode 100644 index 0000000..c9c7711 --- /dev/null +++ b/modules/nf-core/modules/motus/merge/meta.yml @@ -0,0 +1,57 @@ +name: "motus_merge" +description: Taxonomic meta-omics profiling using universal marker genes +keywords: + - classify + - metagenomics + - fastq + - taxonomic profiling + - merging + - merge + - otu table +tools: + - "motus": + description: "Marker gene-based OTU (mOTU) profiling" + homepage: "https://motu-tool.org/" + documentation: "https://github.com/motu-tool/mOTUs/wiki" + tool_dev_url: "https://github.com/motu-tool/mOTUs" + doi: "10.1038/s41467-019-08844-4" + licence: "['GPL v3']" + +input: + - input: + type: file + description: | + List of output files (more than one) from motus profile, + or a single directory containing motus output files. + - db: + type: directory + description: | + mOTUs database downloaded by `motus downloadDB` + pattern: "db_mOTU/" + - profile_version_yml: + type: file + description: | + A single versions.yml file output from motus/profile. motus/merge cannot reconstruct + this itself without having the motus database present and configured with the tool + so here we take it from what is already reported by the upstream module. + pattern: "versions.yml" + - biom_format: + type: boolean + description: Whether to save output OTU table in biom format + +output: + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - txt: + type: file + description: OTU table in txt format, if BIOM format not requested + pattern: "*.txt" + - biom: + type: file + description: OTU table in biom format, if BIOM format requested + pattern: "*.biom" + +authors: + - "@jfy133" diff --git a/modules/nf-core/modules/motus/profile/main.nf b/modules/nf-core/modules/motus/profile/main.nf index 6a1acd3..2747984 100644 --- a/modules/nf-core/modules/motus/profile/main.nf +++ b/modules/nf-core/modules/motus/profile/main.nf @@ -48,7 +48,7 @@ process MOTUS_PROFILE { fi cat <<-END_VERSIONS > versions.yml "${task.process}": - mOTUs: \$VERSION + motus: \$VERSION END_VERSIONS """ } diff --git a/nextflow.config b/nextflow.config index 7160d0f..58c9254 100644 --- a/nextflow.config +++ b/nextflow.config @@ -132,6 +132,10 @@ params { // krona run_krona = false krona_taxonomy_directory = null + + // profile standardisation + run_profile_standardisation = false + generate_biom_output = false } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index 4eec889..28050ba 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -52,7 +55,8 @@ "type": "string", "description": "Name of iGenomes reference.", "fa_icon": "fas fa-book", - "help_text": "If using a reference genome configured in the pipeline using iGenomes, use this parameter to give the ID for the reference. This is then used to build the full paths for all required reference genome files e.g. `--genome GRCh38`. \n\nSee the [nf-core website docs](https://nf-co.re/usage/reference_genomes) for more details." + "help_text": "If using a reference genome configured in the pipeline using iGenomes, use this parameter to give the ID for the reference. This is then used to build the full paths for all required reference genome files e.g. `--genome GRCh38`. \n\nSee the [nf-core website docs](https://nf-co.re/usage/reference_genomes) for more details.", + "hidden": true }, "igenomes_base": { "type": "string", @@ -173,7 +177,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -287,7 +298,10 @@ "shortread_qc_tool": { "type": "string", "default": "fastp", - "enum": ["fastp", "adapterremoval"] + "enum": [ + "fastp", + "adapterremoval" + ] }, "shortread_qc_skipadaptertrim": { "type": "boolean" @@ -313,7 +327,11 @@ "shortread_complexityfilter_tool": { "type": "string", "default": "bbduk", - "enum": ["bbduk", "prinseqplusplus", "fastp"] + "enum": [ + "bbduk", + "prinseqplusplus", + "fastp" + ] }, "shortread_complexityfilter_bbduk_windowsize": { "type": "integer", @@ -329,7 +347,10 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": ["entropy", "dust"] + "enum": [ + "entropy", + "dust" + ] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -385,7 +406,14 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": ["phylum", "class", "order", "family", "genus", "species"] + "enum": [ + "phylum", + "class", + "order", + "family", + "genus", + "species" + ] }, "run_diamond": { "type": "boolean" @@ -393,7 +421,15 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] + "enum": [ + "blast", + "xml", + "txt", + "daa", + "sam", + "tsv", + "paf" + ] }, "longread_hostremoval_index": { "type": "string", @@ -444,7 +480,13 @@ }, "krona_taxonomy_directory": { "type": "string", - "default": null + "default": "None" + }, + "run_profile_standardisation": { + "type": "boolean" + }, + "generate_biom_output": { + "type": "boolean" } } } diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 60963c9..68f8dcc 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -234,5 +234,6 @@ workflow PROFILING { classifications = ch_raw_classifications profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom versions = ch_versions // channel: [ versions.yml ] + motu_version = MOTUS_PROFILE.out.versions.first() mqc = ch_multiqc_files } diff --git a/subworkflows/local/standardisation_profiles.nf b/subworkflows/local/standardisation_profiles.nf new file mode 100644 index 0000000..92ceb16 --- /dev/null +++ b/subworkflows/local/standardisation_profiles.nf @@ -0,0 +1,56 @@ +// +// Create Krona visualizations +// + +include { MOTUS_MERGE } from '../../modules/nf-core/modules/motus/merge/main' + +workflow STANDARDISATION_PROFILES { + take: + classifications + profiles + databases + motu_version + + main: + ch_standardised_tables = Channel.empty() + ch_versions = Channel.empty() + + /* + Split profile results based on tool they come from + */ + ch_input_profiles = profiles + .branch { + motus: it[0]['tool'] == 'motus' + unknown: true + } + + ch_input_classifications = classifications + .branch { + unknown: true + } + + ch_input_databases = databases + .branch { + motus: it[0]['tool'] == 'motus' + unknown: true + } + + /* + Standardise and aggregate + */ + + // mOTUs has a 'single' database, and cannot create custom ones. + // Therefore removing db info here, and publish merged at root mOTUs results + // directory + MOTUS_MERGE ( ch_input_profiles.motus.map{it[1]}.collect(), ch_input_databases.motus.map{it[1]}, motu_version, params.generate_biom_output ) + if ( params.generate_biom_output ) { + ch_standardised_tables = ch_standardised_tables.mix ( MOTUS_MERGE.out.biom ) + } else { + ch_standardised_tables = ch_standardised_tables.mix ( MOTUS_MERGE.out.txt ) + } + ch_versions = ch_versions.mix( MOTUS_MERGE.out.versions ) + + emit: + tables = ch_standardised_tables + versions = ch_versions +} diff --git a/subworkflows/local/visualization_krona.nf b/subworkflows/local/visualization_krona.nf index 7a94fc6..397251f 100644 --- a/subworkflows/local/visualization_krona.nf +++ b/subworkflows/local/visualization_krona.nf @@ -78,7 +78,7 @@ workflow VISUALIZATION_KRONA { ch_krona_text_for_import = ch_cleaned_krona_text .map{[[id: it[0]['db_name'], tool: it[0]['tool']], it[1]]} .groupTuple() - .dump(tag: "text") + KRONA_KTIMPORTTEXT( ch_krona_text_for_import ) ch_krona_html = ch_krona_html.mix( KRONA_KTIMPORTTEXT.out.html ) ch_versions = ch_versions.mix( KRONA_KTIMPORTTEXT.out.versions.first() ) @@ -92,7 +92,7 @@ workflow VISUALIZATION_KRONA { ch_krona_taxonomy_for_input = GUNZIP.out.gunzip .map{[[id: it[0]['db_name'], tool: it[0]['tool']], it[1]]} .groupTuple() - .dump(tag: "taxonomy") + KRONA_KTIMPORTTAXONOMY ( ch_krona_taxonomy_for_input, file(params.krona_taxonomy_directory, checkExists: true) ) ch_krona_html.mix( KRONA_KTIMPORTTAXONOMY.out.html ) ch_versions = ch_versions.mix( MEGAN_RMA2INFO.out.versions.first() ) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 2037649..e382e05 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -65,6 +65,7 @@ include { LONGREAD_HOSTREMOVAL } from '../subworkflows/local/longread_h include { SHORTREAD_COMPLEXITYFILTERING } from '../subworkflows/local/shortread_complexityfiltering' include { PROFILING } from '../subworkflows/local/profiling' include { VISUALIZATION_KRONA } from '../subworkflows/local/visualization_krona' +include { STANDARDISATION_PROFILES } from '../subworkflows/local/standardisation_profiles' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -220,6 +221,14 @@ workflow TAXPROFILER { ch_versions = ch_versions.mix( VISUALIZATION_KRONA.out.versions ) } + /* + SUBWORKFLOW: PROFILING STANDARDISATION + */ + if ( params.run_profile_standardisation ) { + STANDARDISATION_PROFILES ( PROFILING.out.classifications, PROFILING.out.profiles, DB_CHECK.out.dbs, PROFILING.out.motu_version ) + ch_versions = ch_versions.mix( STANDARDISATION_PROFILES.out.versions ) + } + /* MODULE: MultiQC */ From d8c0e9e7fe6ac0f077754017ea41ce11a43cd01e Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 12 Jul 2022 11:41:07 +0200 Subject: [PATCH 268/306] Prettier --- nextflow_schema.json | 49 +++++++------------------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 28050ba..b4cb837 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -177,14 +174,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -298,10 +288,7 @@ "shortread_qc_tool": { "type": "string", "default": "fastp", - "enum": [ - "fastp", - "adapterremoval" - ] + "enum": ["fastp", "adapterremoval"] }, "shortread_qc_skipadaptertrim": { "type": "boolean" @@ -327,11 +314,7 @@ "shortread_complexityfilter_tool": { "type": "string", "default": "bbduk", - "enum": [ - "bbduk", - "prinseqplusplus", - "fastp" - ] + "enum": ["bbduk", "prinseqplusplus", "fastp"] }, "shortread_complexityfilter_bbduk_windowsize": { "type": "integer", @@ -347,10 +330,7 @@ "shortread_complexityfilter_prinseqplusplus_mode": { "type": "string", "default": "entropy", - "enum": [ - "entropy", - "dust" - ] + "enum": ["entropy", "dust"] }, "shortread_complexityfilter_prinseqplusplus_dustscore": { "type": "number", @@ -406,14 +386,7 @@ "kaiju_taxon_name": { "type": "string", "default": "species", - "enum": [ - "phylum", - "class", - "order", - "family", - "genus", - "species" - ] + "enum": ["phylum", "class", "order", "family", "genus", "species"] }, "run_diamond": { "type": "boolean" @@ -421,15 +394,7 @@ "diamond_output_format": { "type": "string", "default": "tsv", - "enum": [ - "blast", - "xml", - "txt", - "daa", - "sam", - "tsv", - "paf" - ] + "enum": ["blast", "xml", "txt", "daa", "sam", "tsv", "paf"] }, "longread_hostremoval_index": { "type": "string", From 4a316df2093a2cecc9b482d3502ce54d9147bb77 Mon Sep 17 00:00:00 2001 From: sofstam Date: Tue, 12 Jul 2022 17:15:36 +0200 Subject: [PATCH 269/306] Update the dev branch with the filtlong update --- modules/nf-core/modules/filtlong/main.nf | 4 +++- modules/nf-core/modules/filtlong/meta.yml | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/nf-core/modules/filtlong/main.nf b/modules/nf-core/modules/filtlong/main.nf index 9dbf05b..afaa938 100644 --- a/modules/nf-core/modules/filtlong/main.nf +++ b/modules/nf-core/modules/filtlong/main.nf @@ -12,7 +12,8 @@ process FILTLONG { output: tuple val(meta), path("*.fastq.gz"), emit: reads - path "versions.yml" , emit: versions + tuple val(meta), path("*.log") , emit: log + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when @@ -27,6 +28,7 @@ process FILTLONG { $short_reads \\ $args \\ $longreads \\ + 2> ${prefix}.log \\ | gzip -n > ${prefix}.fastq.gz cat <<-END_VERSIONS > versions.yml diff --git a/modules/nf-core/modules/filtlong/meta.yml b/modules/nf-core/modules/filtlong/meta.yml index b3626e6..a50b452 100644 --- a/modules/nf-core/modules/filtlong/meta.yml +++ b/modules/nf-core/modules/filtlong/meta.yml @@ -45,6 +45,11 @@ output: type: file description: Filtered (compressed) fastq file pattern: "*.fastq.gz" + - log: + type: file + description: Standard error logging file containing summary statistics + pattern: "*.log" authors: - "@d4straub" + - "@sofstam" From 1fbe241d27d9fe41c0bd12b5b3591d97bcb7a2e5 Mon Sep 17 00:00:00 2001 From: sofstam Date: Tue, 12 Jul 2022 17:43:52 +0200 Subject: [PATCH 270/306] Update filtlong module --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 1d40748..487b76c 100644 --- a/modules.json +++ b/modules.json @@ -37,7 +37,7 @@ "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, "filtlong": { - "git_sha": "089f761f0bf79c4a486f1df9b6205f650196a2c1" + "git_sha": "957cb9b83668075f4af101fc99502908cca487e3" }, "gunzip": { "git_sha": "9aadd9a6d3f5964476582319b3a1c54a3e3fe7c9" From c22946cfabbd3acc0228026e82475ffb0ad1ffc6 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Jul 2022 12:36:29 +0200 Subject: [PATCH 271/306] Fix motu versions not being exported if not run --- subworkflows/local/profiling.nf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 68f8dcc..ed61f4b 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -227,13 +227,12 @@ workflow PROFILING { MOTUS_PROFILE ( ch_input_for_motus.reads, ch_input_for_motus.db ) ch_versions = ch_versions.mix( MOTUS_PROFILE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MOTUS_PROFILE.out.out ) - } emit: classifications = ch_raw_classifications profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom versions = ch_versions // channel: [ versions.yml ] - motu_version = MOTUS_PROFILE.out.versions.first() + motu_version = params.run_motus ? MOTUS_PROFILE.out.versions.first() : Channel.empty([]) mqc = ch_multiqc_files } From eaa69b65d81b6ede5f0afca7d8f24a56b025ff08 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Jul 2022 12:38:30 +0200 Subject: [PATCH 272/306] Update mOTUs profile module and mix log for multiqc --- modules.json | 2 +- modules/nf-core/modules/motus/profile/main.nf | 6 ++++-- modules/nf-core/modules/motus/profile/meta.yml | 4 ++++ subworkflows/local/profiling.nf | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/modules.json b/modules.json index 1d40748..faa267d 100644 --- a/modules.json +++ b/modules.json @@ -79,7 +79,7 @@ "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, "motus/profile": { - "git_sha": "6b960f0e75bbb4d5bd301cd3875fa078d0eab4d1" + "git_sha": "b6ed584443ad68ac41e6975994139454a4f23c18" }, "multiqc": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" diff --git a/modules/nf-core/modules/motus/profile/main.nf b/modules/nf-core/modules/motus/profile/main.nf index 6a1acd3..bd7a127 100644 --- a/modules/nf-core/modules/motus/profile/main.nf +++ b/modules/nf-core/modules/motus/profile/main.nf @@ -15,6 +15,7 @@ process MOTUS_PROFILE { tuple val(meta), path("*.out"), emit: out tuple val(meta), path("*.bam"), optional: true, emit: bam tuple val(meta), path("*.mgc"), optional: true, emit: mgc + tuple val(meta), path("*.log") , emit: log path "versions.yml" , emit: versions when: @@ -36,7 +37,8 @@ process MOTUS_PROFILE { $refdb \\ -t $task.cpus \\ -n $prefix \\ - -o ${prefix}.out + -o ${prefix}.out \\ + 2> ${prefix}.log ## mOTUs version number is not available from command line. ## mOTUs save the version number in index database folder. @@ -48,7 +50,7 @@ process MOTUS_PROFILE { fi cat <<-END_VERSIONS > versions.yml "${task.process}": - mOTUs: \$VERSION + motus: \$VERSION END_VERSIONS """ } diff --git a/modules/nf-core/modules/motus/profile/meta.yml b/modules/nf-core/modules/motus/profile/meta.yml index 19803bd..3c3b660 100644 --- a/modules/nf-core/modules/motus/profile/meta.yml +++ b/modules/nf-core/modules/motus/profile/meta.yml @@ -56,6 +56,10 @@ output: type: file description: Optional intermediate mgc read count table file saved with `-M`. pattern: "*.{mgc}" + - log: + type: file + description: Standard error logging file containing summary statistics + pattern: "*.log" authors: - "@jianhong" diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 60963c9..0de726b 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -227,6 +227,7 @@ workflow PROFILING { MOTUS_PROFILE ( ch_input_for_motus.reads, ch_input_for_motus.db ) ch_versions = ch_versions.mix( MOTUS_PROFILE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MOTUS_PROFILE.out.out ) + ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log ) } From 13280e246855e159e90e5992fc47da52ccac4e5b Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Jul 2022 12:44:44 +0200 Subject: [PATCH 273/306] Fix .empty --- subworkflows/local/profiling.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index ed61f4b..d23ee11 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -233,6 +233,6 @@ workflow PROFILING { classifications = ch_raw_classifications profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom versions = ch_versions // channel: [ versions.yml ] - motu_version = params.run_motus ? MOTUS_PROFILE.out.versions.first() : Channel.empty([]) + motu_version = params.run_motus ? MOTUS_PROFILE.out.versions.first() : Channel.empty() mqc = ch_multiqc_files } From 31119f83c23622783cb647577afffbdfb4f8153b Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Jul 2022 14:29:47 +0200 Subject: [PATCH 274/306] Send to MQC only the log file not the meta --- subworkflows/local/profiling.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 0de726b..38bc9a8 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -227,7 +227,7 @@ workflow PROFILING { MOTUS_PROFILE ( ch_input_for_motus.reads, ch_input_for_motus.db ) ch_versions = ch_versions.mix( MOTUS_PROFILE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MOTUS_PROFILE.out.out ) - ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log ) + ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log.map{it[1]} ) } From 449b6a55537e85e1be45461609c8d9c0fda44bd0 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 13 Jul 2022 15:25:58 +0200 Subject: [PATCH 275/306] Fix file name output clashing wiht multiqc --- conf/modules.config | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/modules.config b/conf/modules.config index b858ec3..df90c38 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -391,6 +391,7 @@ process { } withName: MOTUS_PROFILE { + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/motus/${meta.db_name}" }, mode: params.publish_dir_mode From cc3967a3fd0af46000b362e217d7ba3c68707cbe Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Fri, 15 Jul 2022 10:51:41 +0200 Subject: [PATCH 276/306] Apply suggestions from code review --- subworkflows/local/profiling.nf | 2 +- workflows/taxprofiler.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index d23ee11..87a6a98 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -233,6 +233,6 @@ workflow PROFILING { classifications = ch_raw_classifications profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom versions = ch_versions // channel: [ versions.yml ] - motu_version = params.run_motus ? MOTUS_PROFILE.out.versions.first() : Channel.empty() + motus_version = params.run_motus ? MOTUS_PROFILE.out.versions.first() : Channel.empty() mqc = ch_multiqc_files } diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index e382e05..85a3a51 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -225,7 +225,7 @@ workflow TAXPROFILER { SUBWORKFLOW: PROFILING STANDARDISATION */ if ( params.run_profile_standardisation ) { - STANDARDISATION_PROFILES ( PROFILING.out.classifications, PROFILING.out.profiles, DB_CHECK.out.dbs, PROFILING.out.motu_version ) + STANDARDISATION_PROFILES ( PROFILING.out.classifications, PROFILING.out.profiles, DB_CHECK.out.dbs, PROFILING.out.motus_version ) ch_versions = ch_versions.mix( STANDARDISATION_PROFILES.out.versions ) } From d83c747533e4bb916622c2066d2c2fa258d4c7cc Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Fri, 15 Jul 2022 12:38:20 +0200 Subject: [PATCH 277/306] Fix modules JSON --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index f98cd62..132ff8b 100644 --- a/modules.json +++ b/modules.json @@ -82,7 +82,7 @@ "git_sha": "b02e648c221e1da17cb589eefe297e61ec9e9c49" }, "motus/profile": { - "git_sha": "b02e648c221e1da17cb589eefe297e61ec9e9c49" + "git_sha": "b6ed584443ad68ac41e6975994139454a4f23c18" }, "multiqc": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" From 5a2fd260fd6551854006c5f23da21bedb068ef9f Mon Sep 17 00:00:00 2001 From: sofstam Date: Fri, 15 Jul 2022 12:50:50 +0200 Subject: [PATCH 278/306] Log file into multiqc channel --- subworkflows/local/longread_preprocessing.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 6a23b0e..08d366d 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -49,7 +49,7 @@ workflow LONGREAD_PREPROCESSING { } FASTQC_PROCESSED ( ch_processed_reads ) - ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip, FILTLONG.out.log ) emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] From 035b4b7a1745efccf61513348da54e41c809dc7a Mon Sep 17 00:00:00 2001 From: mjamy Date: Fri, 15 Jul 2022 16:28:07 +0200 Subject: [PATCH 279/306] Update the dev branch with the DIAMOND module update --- modules.json | 2 +- modules/nf-core/modules/diamond/blastx/main.nf | 6 +++++- modules/nf-core/modules/diamond/blastx/meta.yml | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/modules.json b/modules.json index faa267d..2b0d5c0 100644 --- a/modules.json +++ b/modules.json @@ -28,7 +28,7 @@ "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" }, "diamond/blastx": { - "git_sha": "bd3bfe0817246082525ab93707976676b1fe208b" + "git_sha": "3531824af826c16cd252bc5aa82ae169b244ebaa" }, "fastp": { "git_sha": "d0a1cbb703a130c19f6796c3fce24fbe7dfce789" diff --git a/modules/nf-core/modules/diamond/blastx/main.nf b/modules/nf-core/modules/diamond/blastx/main.nf index d327227..1f4ff25 100644 --- a/modules/nf-core/modules/diamond/blastx/main.nf +++ b/modules/nf-core/modules/diamond/blastx/main.nf @@ -21,6 +21,7 @@ process DIAMOND_BLASTX { tuple val(meta), path('*.sam') , optional: true, emit: sam tuple val(meta), path('*.tsv') , optional: true, emit: tsv tuple val(meta), path('*.paf') , optional: true, emit: paf + tuple val(meta), path("*.log") , emit: log path "versions.yml" , emit: versions when: @@ -54,7 +55,10 @@ process DIAMOND_BLASTX { --query $fasta \\ --outfmt ${outfmt} ${columns} \\ $args \\ - --out ${prefix}.${out_ext} + --out ${prefix}.${out_ext} \\ + --log + + mv diamond.log ${prefix}.log cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/diamond/blastx/meta.yml b/modules/nf-core/modules/diamond/blastx/meta.yml index 2dcd7bc..327b893 100644 --- a/modules/nf-core/modules/diamond/blastx/meta.yml +++ b/modules/nf-core/modules/diamond/blastx/meta.yml @@ -70,7 +70,12 @@ output: type: file description: File containing software versions pattern: "versions.yml" + - log: + type: file + description: Log file containing stdout information + pattern: "*.{log}" authors: - "@spficklin" - "@jfy133" + - "@mjamy" From 33a5402b255a7cf82491b1169e4fcfcaaa947fce Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 19 Jul 2022 13:50:39 +0200 Subject: [PATCH 280/306] Add emitting porechop logs to multiqc and fix collection of others --- .../nf-core-taxprofiler_icon_border.svg | 445 ++++++++++++++++++ .../nf_core_taxprofiler_icon_border.png | Bin 0 -> 142407 bytes modules.json | 2 +- modules/nf-core/modules/porechop/main.nf | 4 +- modules/nf-core/modules/porechop/meta.yml | 5 + subworkflows/local/longread_preprocessing.nf | 5 +- subworkflows/local/profiling.nf | 2 +- 7 files changed, 459 insertions(+), 4 deletions(-) create mode 100644 docs/images/nf-core-taxprofiler_icon_border.svg create mode 100644 docs/images/nf_core_taxprofiler_icon_border.png diff --git a/docs/images/nf-core-taxprofiler_icon_border.svg b/docs/images/nf-core-taxprofiler_icon_border.svg new file mode 100644 index 0000000..887e8e8 --- /dev/null +++ b/docs/images/nf-core-taxprofiler_icon_border.svg @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/nf_core_taxprofiler_icon_border.png b/docs/images/nf_core_taxprofiler_icon_border.png new file mode 100644 index 0000000000000000000000000000000000000000..c513de0c016247223a1dc7a240bc6207200067cd GIT binary patch literal 142407 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%0DjZH?3iqlV|AwXNsKGgWF{SX;)Yk zbaQQGWgcZ?FqjitTTm#0^RThU+-UeaST}hciK;SuXqaGdX{i$8eIfPEaUi~i#~0GU z3w9+%trPp;J`4H7hvnaB=4KYi3wyc~p1)GF^=%AjiUkB|1fRCteC6S+I^BIgkq}G0 zrrrUbj=4<_`L@5G?n04dgCjreBxhZpEW{U_KH^d#{OL8!>JVH*&K@}KCLz&3GF+=n zBE{u^HFY`>%Fc2%zLQVhLY5=qik}SS+H@SWq|vB!Wv03I>3HyDtGw6+ zMdw4Yva+9_t@>C}r#lImNO6rWJvla+IyrMY^V$(t%z_^VJrCnnZ4h#d$TWDbh(mUK z?P7yO*}{v-5njgN#7`1pik+?Ed5ul=Xn6?yFb1b45ygW%Wr`vTmKHbbOYGZ-<%4f< zKl?+FR^Kx*71Ph6>B%??)1G46=0^pcv3&Ud^lXZ!w+N=oHU5%;I+l@UA(G; zR|&`0)Y5ObK0}rDJPZGv;%!|pXU^^aznIR!9^o?-dptZ&o^Nt37$xkuQDH2T2#ekw z)qAEAc6jiNd!|^UrN;n#se<6W26^h6p*0C5WzTRQDJv&rkV7aFvFpcKdCpLi)OppK zdgIzXlbNH{-*vgI3o6RpeN3B5Zf4ifBCsT>07?9cQ){*YQCU|8_sSE_c1m#byF~-^92Ic8GRH#f`PN_6=Diue!VAv#g-I7g*)$9Udy9aGu&~7qKl<;8 z*Aox66)uYuQ`46--q_2(=aqIn<2bJhX+Oa<2I%<+a=8!2i4Lcn7=Ga*W_2m zY{Ro(96YnO_jZRa zq@|KkG1VDDG=l%z___Up@WjMa8&pLOfS3K>!yHP$BbwEcquhv|f zPTG`9zk#HLl(y^LH1$wReYqMP`01-kw;P>~Bp@qHfRAcpc-&bEM&&E``pQ@w2xtrh9xRV+i4b6cF?pn`ybUmfkU1*NT*uE>9y~y$| zWfx4oO#OYs_Lv9+b8<08TCXX*ZGa8kysV0F>EN`4O`H{wSCYtbP_-3!%IsGfHyz%Omk_6pGc!XkjFiz8aibj zKV%|NRsPWNz2rIS&%r3ck5N;3QR4Cwy}?LN)r$Kuwp5)tS$FSqxoZ`yxU__)6h}$M z^Yn2`YWWklWem6nRT1I#|`l40REaD9a99>o&{=G>M(L{5?04g96Jw zvOaPKPFs%nKo&9fEpFNI=sWJ)hAoI%ly}#2%*)EPGkJ*$yVH7i8$L?mwe1TM3x#S@ zYGv1#{g>ZrAJX$se*m^?SzvZ|q`VqZS?Jna@D;EDAbUE5w+K0gT) z0Ugx)*zLsZ;X(vzjK7ztav~y2PVq$ogY0ES`%O1a7lWiJRQo*Mvp`lX3`i(6C=^VI>>v~1zY z3!G$LKUUGj=p9U388c%{8$(A>aYWs}B~zH;j+rq7NwWZ4UN?|qHFPUiz`x|R3bbP2 z6Lf*$P=u7RiyRL+hj1tI5+g4O(8bQs8by7y-xbhr7 z6|bnII0UFx*%T%xN=nPh0WKmFLP0}MRye-8|3}UCh5UE&Zk`?ebx3I*41>2{TE*PW38Sv_!*_;xys5lSpW_kZ@9I&w`#I?sC0PVfv|>eOV(r+j^Xj_SqO#6kO5yLA+c#3P|iN(FH*_YZ8Sbl)VBZIsGGL} zq3u`gnjRMRMu7HAY|tL_f=}$<@ThBrU93p`-d{)QZ_-u+}yjzwL6}Q6Dp8# zq_~*zB{>xZ-(DjY0FJIQC4c#J-M}q9!K;1o;h@EmP<_tMRWp5eRX{llA23f?8r3%U zLGn9`$xA1iZ9m?_B&{9w5tEQiq;_?zYN2veW$np)fn&%VyIrT_`-%gSH<01c`LR|w_vb^OhG?wTQ70nml;EPpmscWUrYSu*?Kg@pq`usG}kxjyxG>IBH=`ML_agns4EUWt$3sNUEOna_1+`{J@T*30)L z@>^zQz<4De7Y-a)9Z0TR(z5xoZs_3TkLR80cg}5sx2UQ+*J5_-uahGXt3BUcU#qv^a z2`%mMoL7APXHKyt?e{MbyB-f{U3Qw!oU=~J3fc!HVXcJgSGPNwcI3s|FNTAC;G9zO()K9sP{V2@~1gAyMM7isNGFK^mU?qaXqHQJP;^5Y8(l zQ)jlu1oFRrN4S>4p2dtVwUFZ}rk2@sf&LBjZAoq!MjH_zWh%qMf__4GDAB?TxW-sV zpxzYHg@4#Tr;<5fjFRMyIquw5dsm%f2ak{FQjufFO@rzsp<(@f#flxs>Pue7mhA}l z@6WskIh)p5L_8tgY1>hMr_3?quu4MWBqR-Hqc<)wZO8ziRC&23rpsNC1UP04ZsXl4 z@9Ft8BQ^#$)??j4BDDOrtCn7VlVD`OkgU^(h;xljA_I55A9mhFWq%IpGAh{uBg6g? ziHkd%+IFrB!oKcN_aUe;TnLktlh)Eexmm|Fm44`S{Bqwm3?rRS75u zRx2|gKALgPz;R;3UhV*GH4a1m_M2E)AMOwARB4g-qdU3J)d0J6rzduLS@5Hvb4o#;)h9uxKV9WRPWt0^h;&?OoMSYG7+sl2 zsM?khR)25m^-z6(`xU#cy-SIxMJ#Zccbp^%saW6!!qb{^KbvChN781wcq?BR=_*nI zG^rh4J~}bi+n#Q^K>1sH_dh!73GCs!*Dbq3Dh302M{BG*{a@v7|VylqZ zLc2JIY;aDPV_r`;YH&>=DI%bnEb^&ndvNrJ09yli#V{|I}E; z_#OSZA>Z1XGt=zul;sOG1L!-Fj9762M1xJgIZykpx3^A67+^(E6+9eoflBtXaU*9& zxTC44UN;HxCxgr>40Lq)bW@W4i$T5h%|K6?pl@IJrav90Oh@zk>=SPoXt%poBA9Mh zOp9}hiijW=!LPvkAZowaT4xa)g&>4Uf^^PQp+x_o=Yi(awkG}9KG`m0BJ^u^J1B9* zeA%i%9q8qd2JCwGe4q=^Qz3`C?)VxIO-0Q`@KQ7T(g}aG(g`m}Joty?e`}ITTQ_7l z`_dlfCjIKT(Pa z{}xe>`2&)v@lQ;hfTZL1-UhU>NTwN(?F2@ zwOZR-xA&%}+hGoiXVq_l7DWxer!EbA)INqkuqD6l!!%u#`|uy%S2xti zJw3>cE{L6ee4a6QVl?q#HcOSR12>~(6x>IXB&k4^L*GCbKN_gip5r8fBP^muH&|*WO1(? zGUcIAxZ+}5URiI$b0}-=05qiPPZza-03f)DU%UItZ5fc@^W|S=7S_|mo>E+&__&f*KQ%=b z@rt|YNcA6Dx{~h^u6-Sb;hPF#hPr*RyH+}hnSAe^#IS;~IkRqxzI_wf`Np;7Ngx@^ z%B2y{Jl5cWMzGonAh4QW2OnB;$5wtjJ0{P!6U-DAaiG1`R(Ag86D^y9(yn=Sm)cj0 zdF8!#1KpzAAu!-bz?(qUL;6Cn{pc;M5eDGAo%`4Z&Sb{}SdCPSY__yP(=et7+7XLC z5j>D(t)1b{aO}ngb7k-oX5xEV^J|U+r)jRb`=j_uPvDzc&Y*$m!S%;qm2FaL|JM#0 zbU|e6nFfAZZTOY}?}Go45~k7Z9G?ikZ?pdSD=o zy!>5q&>xHW#gHsfK%?F0XOV=*`ZMu!!<8&bi?3NE(C2v`mDAPzKb@+9aDkdX)Z@c( za;&!?wudT@-8@0mKKsyc!Y~Ir`}6w06{$sCoQ=8KQ&3&PHmye`wqx(^9_W@wfPC?i zmP5;P@`YWPY9wg}g(Ye!pY1B<7F8w~? zY&JGKi+P&;D`y7CSiIEu79UINxn!Fohu269vcSZJL0}xk9@1%x1xXZn&UBo0=|Xk> z$nL@&cC#kZ8P;084J9y@L=X<{@b!D}ulCm!Kplw~=e8HyFM~d4rB-Nho$tSkI)3L> z&TXy}YjhatI|XB%2A6*7sJ8Cq%Z=oQSa?}g7<0Q(L2lID$mK1*@PUmwB)s$(UbFB? zK)BQY7fIUs1DI^TekI%UMIjxz@VB~_7w76uH)G)6loI}={9Dz0(O;AAYR+S?;mNe zYMSAHQ6VnDA~IbjvM29bjGPSSfxp>Rv)ysI2880IHzA^2tby}kI4AVv3f>PTk!LdH8$Oko9b!dB# zYpvRBW<4i@HX8kJL9l>sOeBk$d8C_NORE_%sN6-SDPc3O+jD3 z8M=}es`{s@Ao9diyP6T6k*9xNYXf0pb*YK#jCGF2bRel=%barY>>ZxI^N}!gj0e10 z{HVSeqlWV9YYU3C=?n=N!$r)MO~BESnTG#zo3$9}NN)~x7{7y^&cRyHgxro|Z5Krs z$lY7&ceH($SsuK8)s%W@>0-ikUr~(SI~x&R(FowZMInQ>&nPK|{Ajza4coW+P}8<; z2%+dVC!g_>w}Sk;W|D!=lws2mu9{2lslKkFg|hG&)bOUuf5g8-hZ6;-i3Phwm@WR)yFo``&IMllLmR#t&@x6QJ36Rkl z{o}Zm9MKb)9|-$@YY?X`J2;thdO&z<#vsd6TNluX(=si=pq#V!_2ndH>+2JY{2FWa z3+DLYINEX7bUigIuGjxpjX%5X&+MdU*UD6xa5yRqQ9?tBg6x<=W$R=1F(%MF1u?u< z;5CdHC}%4>BTGv0n|JrH?2JvP3hat1&;^jv%Mug-7!m&*I)st~RJ2PDH7amp_1icQ zQNP_e>|iqVAT5UieBvderGZ)Iz@efSEdT^VLCR@Vz?X#8pwF}i;}EpN04j1vMG$yL zfMbuJ((Uj}atpPfnK$Oi$$-$c^1CMOXs4-@0Vtq12#cU9;Ztdmq(&E>~k5 zp~1mQHZcv2?@#H^?e~Xc(>ZyKMO1CWLCNXd57#CiNs?1i6<yiFS_@=-EA7l+_8s@xA_3gayI8S}Bi@BH)N> z&Xy$V{q_uVnSs%1r%m2H^H~^vIBC6tbZ|}3vyg*J5_`*62BK^_A2g$fWm`O75ytfR zBmWoXM|E(cP4{mBnZN@06KqLj@NdnE@_Q8FF88{0c6Dv(JJE?CW*g!pz}pao2`#@? zf;tRa2Cv&=K7@mfO)xmr@|jI2G}=ZIm#s@nr;IE^6_E#S2&NZ;AF^a*zQuKjJgFuiuZ706Hu{c?J*98d~;hUj{6JB z9|BF!kqDvkyiRF)lZUgkSQH(=-xwMgAJ}w6WGJ7g$pl6Vxl{Xm;gIYZdNzQox2C9T z(w2`689SdL6ELNaKJ! zEdlcjf)F+Y@+S4o9Q_Y?9SrWLM}s+Fm|BOcOCu<}2)QP>CKWFT4deFZF%d8F@Y}vl zApaXhWv!9>E^eJNF&Q(Uxsu-31&1;g!sl16MKYt!AQEv;Jf5l`Dn-yFf`+}m4Rz{2 z%LlMRw_D%UDnY?~Un0HzDB+JB-IWOPgmqgn>Np8cSz^97h$sGu#oP>Q8_<^0?w`LS z=NpL?>!o1fOa|^ViKSdq0fskkWc91PA*q7zr>UBgA*zcdiE7l_w0}+>1l=J8@~0?5 zSok#rQ@m+64TD1D3UNgNojL7^hvZ249hoX?f|fY&k4^{-@6Em7@fpSdTk=UAZW|F? zogW7J&1)S%!Ro>$;G|p=&PAfDE%OPfZ`g%&+FKz0-#uXbd{f>q)rMZfTFZK*Pun*v zfLOe|-td@k?XmZ1OVtMzC_(sys?9f=$>F-RzgT3~0#4lcQLE2a?y z9*BMwMoI>5C_;Wa6jV+z5KSeCzIU`T8PL{NWk!RAG7WY}YpO+Q*&pLQ^I2@muX-xi z6!6aFj=+UQhv!I(C53Aay%|3ox!W{pjN36a!vX_$*y0RiGZ#J?(1PBk3&IT`(A$q! zlJ#^i%H-HLtZ%YDV2|>MTe>#Xk@Ec>`#YY{neRiL_50SPeA2m7vq4xt=!9 z-M|hZ_|G9iWnlI*K##d13pNvLjCD#=(DDm%0A!Gm2;yp>aPgT|gfo=c9nN=0xfK^1 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 jMTQL&w#e}Rf(%NX$`2 ${prefix}.log cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/porechop/meta.yml b/modules/nf-core/modules/porechop/meta.yml index 81399d2..e526317 100644 --- a/modules/nf-core/modules/porechop/meta.yml +++ b/modules/nf-core/modules/porechop/meta.yml @@ -38,6 +38,10 @@ output: type: file description: Demultiplexed and/or adapter-trimmed fastq.gz file pattern: "*.{fastq.gz}" + - log: + type: file + description: Log file containing stdout information + pattern: "*.log" authors: - "@ggabernet" @@ -48,3 +52,4 @@ authors: - "@jonasscheid" - "@jonoave" - "@GokceOGUZ" + - "@jfy133" diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 6a23b0e..2de3617 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -23,9 +23,11 @@ workflow LONGREAD_PREPROCESSING { def meta_new = meta.clone() meta_new['single_end'] = 1 [ meta_new, reads ] + } ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) - } + ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log.collect{it[1]}.ifEmpty([]) ) + } else if ( !params.longread_qc_run_clip && params.longread_qc_run_filter ) { ch_processed_reads = FILTLONG ( reads.map{ meta, reads -> [meta, [], reads ]} ) @@ -45,6 +47,7 @@ workflow LONGREAD_PREPROCESSING { ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log.collect{it[1]}.ifEmpty([]) ) } diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 7c2c90c..39b6cb0 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -227,7 +227,7 @@ workflow PROFILING { MOTUS_PROFILE ( ch_input_for_motus.reads, ch_input_for_motus.db ) ch_versions = ch_versions.mix( MOTUS_PROFILE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MOTUS_PROFILE.out.out ) - ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log.map{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log.collect{it[1]}.ifEmpty([]) ) } emit: From 1ba6a08135dad689f11a182376f8a19b4de73b42 Mon Sep 17 00:00:00 2001 From: sofstam Date: Tue, 19 Jul 2022 13:53:29 +0200 Subject: [PATCH 281/306] Seperate multiqc for filtlong --- subworkflows/local/longread_preprocessing.nf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 08d366d..27e5e37 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -49,7 +49,8 @@ workflow LONGREAD_PREPROCESSING { } FASTQC_PROCESSED ( ch_processed_reads ) - ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip, FILTLONG.out.log ) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) + ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([])) emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] From 526ed797639ae275874f972e17511e21d76d425f Mon Sep 17 00:00:00 2001 From: sofstam Date: Tue, 19 Jul 2022 14:01:11 +0200 Subject: [PATCH 282/306] Fix filtlong logs --- subworkflows/local/longread_preprocessing.nf | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 27e5e37..ccdbeb2 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -12,7 +12,7 @@ workflow LONGREAD_PREPROCESSING { main: ch_versions = Channel.empty() - ch_multiqc_files = Channel.empty() + = Channel.empty() if ( params.longread_qc_run_clip && !params.longread_qc_run_filter ) { PORECHOP ( reads ) @@ -30,6 +30,7 @@ workflow LONGREAD_PREPROCESSING { ch_processed_reads = FILTLONG ( reads.map{ meta, reads -> [meta, [], reads ]} ) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) + ch_multiqc_files = .mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([])) } else { PORECHOP ( reads ) @@ -45,16 +46,16 @@ workflow LONGREAD_PREPROCESSING { ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([])) } FASTQC_PROCESSED ( ch_processed_reads ) - ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) - ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([])) + = .mix( FASTQC_PROCESSED.out.zip ) emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] versions = ch_versions // channel: [ versions.yml ] - mqc = ch_multiqc_files + mqc = } From 8147dba0b49129eaff62954a26a2c355e7baab0f Mon Sep 17 00:00:00 2001 From: sofstam Date: Tue, 19 Jul 2022 14:03:10 +0200 Subject: [PATCH 283/306] Fix typos --- subworkflows/local/longread_preprocessing.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index ccdbeb2..aaec456 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -12,7 +12,7 @@ workflow LONGREAD_PREPROCESSING { main: ch_versions = Channel.empty() - = Channel.empty() + ch_multiqc_files = Channel.empty() if ( params.longread_qc_run_clip && !params.longread_qc_run_filter ) { PORECHOP ( reads ) @@ -30,7 +30,7 @@ workflow LONGREAD_PREPROCESSING { ch_processed_reads = FILTLONG ( reads.map{ meta, reads -> [meta, [], reads ]} ) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) - ch_multiqc_files = .mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([])) } else { PORECHOP ( reads ) From 25bc01f9c4aaa4e46e5e0ddf08e311ebd31154ee Mon Sep 17 00:00:00 2001 From: sofstam Date: Tue, 19 Jul 2022 14:04:49 +0200 Subject: [PATCH 284/306] Fix typos --- subworkflows/local/longread_preprocessing.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index aaec456..7dca57d 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -51,7 +51,7 @@ workflow LONGREAD_PREPROCESSING { } FASTQC_PROCESSED ( ch_processed_reads ) - = .mix( FASTQC_PROCESSED.out.zip ) + ch_multiqc_files = ch_multiqc_files.mix( FASTQC_PROCESSED.out.zip ) emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] From 171258d545731feaa38aebd0868d65cf917fbfb9 Mon Sep 17 00:00:00 2001 From: sofstam Date: Tue, 19 Jul 2022 14:07:13 +0200 Subject: [PATCH 285/306] Fix typos --- subworkflows/local/longread_preprocessing.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 7dca57d..a68d447 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -56,6 +56,6 @@ workflow LONGREAD_PREPROCESSING { emit: reads = ch_processed_reads // channel: [ val(meta), [ reads ] ] versions = ch_versions // channel: [ versions.yml ] - mqc = + mqc = ch_multiqc_files } From c4c82212e12ce9ab082858f5e72bc97692baec3f Mon Sep 17 00:00:00 2001 From: mjamy Date: Tue, 19 Jul 2022 14:12:16 +0200 Subject: [PATCH 286/306] incorporate diamond log file into multiqc channel --- subworkflows/local/profiling.nf | 1 + 1 file changed, 1 insertion(+) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 38bc9a8..d4edc99 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -208,6 +208,7 @@ workflow PROFILING { DIAMOND_BLASTX ( ch_input_for_diamond.reads, ch_input_for_diamond.db, ch_diamond_reads_format , [] ) ch_versions = ch_versions.mix( DIAMOND_BLASTX.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( DIAMOND_BLASTX.out.tsv ) + ch_multiqc_files = ch_multiqc_files.mix( DIAMOND_BLASTX.out.log.collect{it[1]}.ifEmpty([]) ) } From 8d7ddb584785f7e50d7e62f7962e39731bf09e85 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 19 Jul 2022 16:30:13 +0200 Subject: [PATCH 287/306] Apply suggestions from code review --- subworkflows/local/longread_preprocessing.nf | 2 +- subworkflows/local/profiling.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 2de3617..0710e07 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -47,7 +47,7 @@ workflow LONGREAD_PREPROCESSING { ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log ) } diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 39b6cb0..1cd513f 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -227,7 +227,7 @@ workflow PROFILING { MOTUS_PROFILE ( ch_input_for_motus.reads, ch_input_for_motus.db ) ch_versions = ch_versions.mix( MOTUS_PROFILE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MOTUS_PROFILE.out.out ) - ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log ) } emit: From 237e9aa6236e2bc40cbffaa7f5f114b0de2927bd Mon Sep 17 00:00:00 2001 From: Sofia Stamouli <91951607+sofstam@users.noreply.github.com> Date: Tue, 19 Jul 2022 16:44:08 +0200 Subject: [PATCH 288/306] Update longread_preprocessing.nf --- subworkflows/local/longread_preprocessing.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index a68d447..13c486e 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -30,7 +30,7 @@ workflow LONGREAD_PREPROCESSING { ch_processed_reads = FILTLONG ( reads.map{ meta, reads -> [meta, [], reads ]} ) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log ) } else { PORECHOP ( reads ) @@ -46,7 +46,7 @@ workflow LONGREAD_PREPROCESSING { ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([])) + ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log ) } From 811ded38afd7b6b2d0b3257d6a18f2afaada4a31 Mon Sep 17 00:00:00 2001 From: Mahwash Jamy <40695699+mjamy@users.noreply.github.com> Date: Tue, 19 Jul 2022 16:44:48 +0200 Subject: [PATCH 289/306] Update subworkflows/local/profiling.nf Co-authored-by: James A. Fellows Yates --- subworkflows/local/profiling.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index d4edc99..4735838 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -208,7 +208,7 @@ workflow PROFILING { DIAMOND_BLASTX ( ch_input_for_diamond.reads, ch_input_for_diamond.db, ch_diamond_reads_format , [] ) ch_versions = ch_versions.mix( DIAMOND_BLASTX.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( DIAMOND_BLASTX.out.tsv ) - ch_multiqc_files = ch_multiqc_files.mix( DIAMOND_BLASTX.out.log.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( DIAMOND_BLASTX.out.log ) } From 255afee4c7dc6c8287a0d7eb39542ef2a86cc9fb Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 19 Jul 2022 17:10:10 +0200 Subject: [PATCH 290/306] Re-add the collects --- subworkflows/local/longread_preprocessing.nf | 2 +- subworkflows/local/profiling.nf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index 0710e07..2de3617 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -47,7 +47,7 @@ workflow LONGREAD_PREPROCESSING { ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log ) + ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log.collect{it[1]}.ifEmpty([]) ) } diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 1cd513f..0ef32b3 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -227,13 +227,13 @@ workflow PROFILING { MOTUS_PROFILE ( ch_input_for_motus.reads, ch_input_for_motus.db ) ch_versions = ch_versions.mix( MOTUS_PROFILE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MOTUS_PROFILE.out.out ) - ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log ) + ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log.collect{it[1]}.ifEmpty([]) ) } emit: classifications = ch_raw_classifications profiles = ch_raw_profiles // channel: [ val(meta), [ reads ] ] - should be text files or biom versions = ch_versions // channel: [ versions.yml ] - motus_version = params.run_motus ? MOTUS_PROFILE.out.versions.first() : Channel.empty() + motus_version = params.run_motus ? MOTUS_PROFILE.out.versions.first() : Channel.empty() mqc = ch_multiqc_files } From 139cf0d6aa9fb93ed3596f5106a4ddf214287927 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 26 Jul 2022 10:12:33 +0200 Subject: [PATCH 291/306] Fix --- subworkflows/local/profiling.nf | 8 ++++---- workflows/taxprofiler.nf | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 7c2c90c..1099155 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -110,7 +110,7 @@ workflow PROFILING { } MEGAN_RMA2INFO (ch_maltrun_for_megan, params.malt_generate_megansummary ) - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log ) ch_versions = ch_versions.mix( MALT_RUN.out.versions.first(), MEGAN_RMA2INFO.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( ch_maltrun_for_megan ) ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) @@ -127,7 +127,7 @@ workflow PROFILING { } KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db, params.kraken2_save_reads, params.kraken2_save_readclassification ) - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( KRAKEN2_KRAKEN2.out.classified_reads_assignment ) ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.report ) @@ -185,7 +185,7 @@ workflow PROFILING { KAIJU_KAIJU ( ch_input_for_kaiju.reads, ch_input_for_kaiju.db) KAIJU_KAIJU2TABLE (KAIJU_KAIJU.out.results, ch_input_for_kaiju.db, params.kaiju_taxon_name) - ch_multiqc_files = ch_multiqc_files.mix( KAIJU_KAIJU2TABLE.out.summary.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( KAIJU_KAIJU2TABLE.out.summary ) ch_versions = ch_versions.mix( KAIJU_KAIJU.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( KAIJU_KAIJU.out.results ) ch_raw_profiles = ch_raw_profiles.mix( KAIJU_KAIJU2TABLE.out.summary ) @@ -227,7 +227,7 @@ workflow PROFILING { MOTUS_PROFILE ( ch_input_for_motus.reads, ch_input_for_motus.db ) ch_versions = ch_versions.mix( MOTUS_PROFILE.out.versions.first() ) ch_raw_profiles = ch_raw_profiles.mix( MOTUS_PROFILE.out.out ) - ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log.map{it[1]} ) + ch_multiqc_files = ch_multiqc_files.mix( MOTUS_PROFILE.out.log ) } emit: diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 85a3a51..b6f3a37 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -266,7 +266,7 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_HOSTREMOVAL.out.mqc.collect{it[1]}.ifEmpty([])) } - ch_multiqc_files = ch_multiqc_files.mix( PROFILING.out.mqc ) + ch_multiqc_files = ch_multiqc_files.mix( PROFILING.out.mqc.collect{it[1]}.ifEmpty([]) ) // TODO create multiQC module for metaphlan MULTIQC ( From e76b299705152e6ef3015658507cf78213818194 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 26 Jul 2022 08:18:54 +0000 Subject: [PATCH 292/306] Start working getting kraken and centrifuge working in MQC --- assets/multiqc_config.yml | 32 ++++++++++++++++++++++++++++++++ conf/modules.config | 6 +++--- subworkflows/local/profiling.nf | 3 ++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 438fb42..00e09c5 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -13,3 +13,35 @@ export_plots: true custom_logo: "nf-core-taxprofiler_logo_custom_light.png" custom_logo_url: https://nf-co.re/taxprofiler custom_logo_title: "nf-core/taxprofiler" + +run_modules: + - fastqc + - adapterRemoval + - fastp + - bowtie2 + - kraken + - malt + +#extra_fn_clean_exts: +# - '_fastp' +# - '.pe.settings' +# - '.se.settings' + +top_modules: + - 'fastqc': + name: "FastQC (pre-processing)" + path_filters: + - '*_raw.zip' + - 'fastp' + - 'fastqc': + name: "FastQC (post-processing)" + path_filters: + - '*_processed.zip' + - 'kraken': + name: "Kraken" + path_filters: + - '*kraken.report' + - 'kraken': + name: Centrifuge" + path_filters: + - '*centrifuge.kreport' diff --git a/conf/modules.config b/conf/modules.config index 1558a98..ea2f487 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -285,7 +285,7 @@ process { withName: KRAKEN2_KRAKEN2 { ext.args = { "${meta.db_params}" } - ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}.kraken" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}.kraken" } publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: params.publish_dir_mode, @@ -343,12 +343,12 @@ process { pattern: '*.{txt,sam,gz}' ] ext.args = { "${meta.db_params}" } - ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}.centrifuge" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}.centrifuge" } } withName: CENTRIFUGE_KREPORT { ext.args = { "${meta.db_params}" } - ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}.centrifuge" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}.centrifuge" } publishDir = [ path: { "${params.outdir}/centrifuge/${meta.db_name}" }, mode: params.publish_dir_mode, diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 7c2c90c..921f291 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -127,7 +127,7 @@ workflow PROFILING { } KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db, params.kraken2_save_reads, params.kraken2_save_readclassification ) - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( KRAKEN2_KRAKEN2.out.classified_reads_assignment ) ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.report ) @@ -152,6 +152,7 @@ workflow PROFILING { ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( CENTRIFUGE_CENTRIFUGE.out.results ) ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_KREPORT.out.kreport ) + ch_multiqc_files = ch_multiqc_files.mix( CENTRIFUGE_KREPORT.out.kreport ) } From 3054dea7c217d4d47b00e866cb4174047519f999 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 26 Jul 2022 08:41:06 +0000 Subject: [PATCH 293/306] Get multiqc input working --- subworkflows/local/profiling.nf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 921f291..ad9f45f 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -110,7 +110,7 @@ workflow PROFILING { } MEGAN_RMA2INFO (ch_maltrun_for_megan, params.malt_generate_megansummary ) - ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( MALT_RUN.out.log ) ch_versions = ch_versions.mix( MALT_RUN.out.versions.first(), MEGAN_RMA2INFO.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( ch_maltrun_for_megan ) ch_raw_profiles = ch_raw_profiles.mix( MEGAN_RMA2INFO.out.txt ) @@ -127,7 +127,7 @@ workflow PROFILING { } KRAKEN2_KRAKEN2 ( ch_input_for_kraken2.reads, ch_input_for_kraken2.db, params.kraken2_save_reads, params.kraken2_save_readclassification ) - ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report ) + ch_multiqc_files = ch_multiqc_files.mix( KRAKEN2_KRAKEN2.out.report.dump(tag: "hello") ) ch_versions = ch_versions.mix( KRAKEN2_KRAKEN2.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( KRAKEN2_KRAKEN2.out.classified_reads_assignment ) ch_raw_profiles = ch_raw_profiles.mix( KRAKEN2_KRAKEN2.out.report ) @@ -186,7 +186,7 @@ workflow PROFILING { KAIJU_KAIJU ( ch_input_for_kaiju.reads, ch_input_for_kaiju.db) KAIJU_KAIJU2TABLE (KAIJU_KAIJU.out.results, ch_input_for_kaiju.db, params.kaiju_taxon_name) - ch_multiqc_files = ch_multiqc_files.mix( KAIJU_KAIJU2TABLE.out.summary.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( KAIJU_KAIJU2TABLE.out.summary ) ch_versions = ch_versions.mix( KAIJU_KAIJU.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( KAIJU_KAIJU.out.results ) ch_raw_profiles = ch_raw_profiles.mix( KAIJU_KAIJU2TABLE.out.summary ) From 65b0af9a8b9960ca3731a04b8b587c3de7d7db28 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 26 Jul 2022 08:53:43 +0000 Subject: [PATCH 294/306] Fix profiling channels --- workflows/taxprofiler.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/taxprofiler.nf b/workflows/taxprofiler.nf index 85a3a51..b6f3a37 100644 --- a/workflows/taxprofiler.nf +++ b/workflows/taxprofiler.nf @@ -266,7 +266,7 @@ workflow TAXPROFILER { ch_multiqc_files = ch_multiqc_files.mix(SHORTREAD_HOSTREMOVAL.out.mqc.collect{it[1]}.ifEmpty([])) } - ch_multiqc_files = ch_multiqc_files.mix( PROFILING.out.mqc ) + ch_multiqc_files = ch_multiqc_files.mix( PROFILING.out.mqc.collect{it[1]}.ifEmpty([]) ) // TODO create multiQC module for metaphlan MULTIQC ( From 65ae82efc4fba6822a56914d1b8fc0a59b3fbdc6 Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 26 Jul 2022 18:37:33 +0000 Subject: [PATCH 295/306] Define separte krakena nd centrigfue modules --- assets/multiqc_config.yml | 39 ++++++++++++++++++++++----------------- conf/modules.config | 2 +- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 00e09c5..3ee6141 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -28,20 +28,25 @@ run_modules: # - '.se.settings' top_modules: - - 'fastqc': - name: "FastQC (pre-processing)" - path_filters: - - '*_raw.zip' - - 'fastp' - - 'fastqc': - name: "FastQC (post-processing)" - path_filters: - - '*_processed.zip' - - 'kraken': - name: "Kraken" - path_filters: - - '*kraken.report' - - 'kraken': - name: Centrifuge" - path_filters: - - '*centrifuge.kreport' + - 'fastqc': + name: 'FastQC (pre-Trimming)' + path_filters: + - '*raw_*fastqc.zip' + - 'fastp' + - 'adapterRemoval' + - 'fastqc': + name: 'FastQC (post-Trimming)' + path_filters: + - '*raw_*processed.zip' + - 'kraken': + name: 'Kraken' + path_filters: + - '*.kraken2.report.txt' + - 'kraken': + name: 'Centrifuge' + anchor: 'centrifuge' + target: 'Centrifuge' + doi: '10.1101/gr.210641.116' + info: "Centrifuge is a very rapid and memory-efficient system for the classification of DNA sequences from microbial samples. The system uses a novel indexing scheme based on the Burrows-Wheeler transform (BWT) and the Ferragina-Manzini (FM) index." + path_filters: + - '*.centrifuge.txt' diff --git a/conf/modules.config b/conf/modules.config index ea2f487..2b1f13c 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -285,7 +285,7 @@ process { withName: KRAKEN2_KRAKEN2 { ext.args = { "${meta.db_params}" } - ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}.kraken" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}.kraken" } + ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ path: { "${params.outdir}/kraken2/${meta.db_name}" }, mode: params.publish_dir_mode, From a3bcf8665a681e98747f0b8fe8543efcd0694560 Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Tue, 26 Jul 2022 18:47:40 +0000 Subject: [PATCH 296/306] [automated] Fix linting with Prettier --- assets/multiqc_config.yml | 56 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 3ee6141..da22e2b 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -15,12 +15,12 @@ custom_logo_url: https://nf-co.re/taxprofiler custom_logo_title: "nf-core/taxprofiler" run_modules: - - fastqc - - adapterRemoval - - fastp - - bowtie2 - - kraken - - malt + - fastqc + - adapterRemoval + - fastp + - bowtie2 + - kraken + - malt #extra_fn_clean_exts: # - '_fastp' @@ -28,25 +28,25 @@ run_modules: # - '.se.settings' top_modules: - - 'fastqc': - name: 'FastQC (pre-Trimming)' - path_filters: - - '*raw_*fastqc.zip' - - 'fastp' - - 'adapterRemoval' - - 'fastqc': - name: 'FastQC (post-Trimming)' - path_filters: - - '*raw_*processed.zip' - - 'kraken': - name: 'Kraken' - path_filters: - - '*.kraken2.report.txt' - - 'kraken': - name: 'Centrifuge' - anchor: 'centrifuge' - target: 'Centrifuge' - doi: '10.1101/gr.210641.116' - info: "Centrifuge is a very rapid and memory-efficient system for the classification of DNA sequences from microbial samples. The system uses a novel indexing scheme based on the Burrows-Wheeler transform (BWT) and the Ferragina-Manzini (FM) index." - path_filters: - - '*.centrifuge.txt' + - "fastqc": + name: "FastQC (pre-Trimming)" + path_filters: + - "*raw_*fastqc.zip" + - "fastp" + - "adapterRemoval" + - "fastqc": + name: "FastQC (post-Trimming)" + path_filters: + - "*raw_*processed.zip" + - "kraken": + name: "Kraken" + path_filters: + - "*.kraken2.report.txt" + - "kraken": + name: "Centrifuge" + anchor: "centrifuge" + target: "Centrifuge" + doi: "10.1101/gr.210641.116" + info: "Centrifuge is a very rapid and memory-efficient system for the classification of DNA sequences from microbial samples. The system uses a novel indexing scheme based on the Burrows-Wheeler transform (BWT) and the Ferragina-Manzini (FM) index." + path_filters: + - "*.centrifuge.txt" From 25bcc0798da7fa053cd409cea929d9d28d47edba Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Wed, 27 Jul 2022 14:40:17 +0200 Subject: [PATCH 297/306] Start work on multiQC config with distinguishing Kraken/Centrifuge --- assets/multiqc_config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index da22e2b..8df4c5e 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -47,6 +47,7 @@ top_modules: anchor: "centrifuge" target: "Centrifuge" doi: "10.1101/gr.210641.116" - info: "Centrifuge is a very rapid and memory-efficient system for the classification of DNA sequences from microbial samples. The system uses a novel indexing scheme based on the Burrows-Wheeler transform (BWT) and the Ferragina-Manzini (FM) index." + info: "is a very rapid and memory-efficient system for the classification of DNA sequences from microbial samples. The system uses a novel indexing scheme based on the Burrows-Wheeler transform (BWT) and the Ferragina-Manzini (FM) index. Note: Figure title" + extra: "Note: plot title will say Kraken2 due to Centrifuge producing the same output format as Kraken. If activated, see the actual Kraken2 results in the section above." path_filters: - "*.centrifuge.txt" From cecbb41f88b05d5534812aee8ee6781b275d48ff Mon Sep 17 00:00:00 2001 From: sofstam Date: Wed, 27 Jul 2022 14:42:35 +0200 Subject: [PATCH 298/306] Update centrifuge/kreport --- modules/nf-core/modules/centrifuge/kreport/main.nf | 4 ++-- modules/nf-core/modules/centrifuge/kreport/meta.yml | 4 ++-- subworkflows/local/profiling.nf | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/nf-core/modules/centrifuge/kreport/main.nf b/modules/nf-core/modules/centrifuge/kreport/main.nf index 124cbdb..381ddd6 100644 --- a/modules/nf-core/modules/centrifuge/kreport/main.nf +++ b/modules/nf-core/modules/centrifuge/kreport/main.nf @@ -8,7 +8,7 @@ process CENTRIFUGE_KREPORT { 'quay.io/biocontainers/centrifuge:1.0.4_beta--h9a82719_6' }" input: - tuple val(meta), path(results) + tuple val(meta), path(report) path db output: @@ -23,7 +23,7 @@ process CENTRIFUGE_KREPORT { def prefix = task.ext.prefix ?: "${meta.id}" """ db_name=`find -L ${db} -name "*.1.cf" -not -name "._*" | sed 's/.1.cf//'` - centrifuge-kreport -x \$db_name ${results} > ${prefix}.txt + centrifuge-kreport -x \$db_name ${report} > ${prefix}.txt cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/centrifuge/kreport/meta.yml b/modules/nf-core/modules/centrifuge/kreport/meta.yml index fbcae24..822a280 100644 --- a/modules/nf-core/modules/centrifuge/kreport/meta.yml +++ b/modules/nf-core/modules/centrifuge/kreport/meta.yml @@ -15,9 +15,9 @@ input: description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - results: + - report: type: file - description: File containing the centrifuge classification results + description: File containing the centrifuge classification report pattern: "*.{txt}" output: diff --git a/subworkflows/local/profiling.nf b/subworkflows/local/profiling.nf index 1099155..42820ba 100644 --- a/subworkflows/local/profiling.nf +++ b/subworkflows/local/profiling.nf @@ -148,7 +148,7 @@ workflow PROFILING { } CENTRIFUGE_CENTRIFUGE ( ch_input_for_centrifuge.reads, ch_input_for_centrifuge.db, params.centrifuge_save_reads, params.centrifuge_save_reads, params.centrifuge_save_reads ) - CENTRIFUGE_KREPORT (CENTRIFUGE_CENTRIFUGE.out.results, ch_input_for_centrifuge.db) + CENTRIFUGE_KREPORT (CENTRIFUGE_CENTRIFUGE.out.report, ch_input_for_centrifuge.db) ch_versions = ch_versions.mix( CENTRIFUGE_CENTRIFUGE.out.versions.first() ) ch_raw_classifications = ch_raw_classifications.mix( CENTRIFUGE_CENTRIFUGE.out.results ) ch_raw_profiles = ch_raw_profiles.mix( CENTRIFUGE_KREPORT.out.kreport ) From 9bc45a3f24763a5ef25f3023a129bcd7c81b10d4 Mon Sep 17 00:00:00 2001 From: sofstam Date: Wed, 27 Jul 2022 14:44:26 +0200 Subject: [PATCH 299/306] Update centrifuge/kreport --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 27db839..6c26e6b 100644 --- a/modules.json +++ b/modules.json @@ -22,7 +22,7 @@ "git_sha": "d2726fcf75063960f06b36d2229a4c0966614108" }, "centrifuge/kreport": { - "git_sha": "be4ae28c3c95b3c4047a7d9fb4cb0ed749631cea" + "git_sha": "734d0db6079a4aa43b6509b207e5d6feb35d4838" }, "custom/dumpsoftwareversions": { "git_sha": "e745e167c1020928ef20ea1397b6b4d230681b4d" From 71cf32cc6abd3769757871eba1ac2c4893a23b23 Mon Sep 17 00:00:00 2001 From: sofstam Date: Thu, 28 Jul 2022 08:54:32 +0200 Subject: [PATCH 300/306] Add error strategy for centrifuge/kreport --- conf/modules.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index 1558a98..c4ab581 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -145,7 +145,7 @@ process { publishDir = [ path: { "${params.outdir}/filtlong" }, mode: params.publish_dir_mode, - pattern: '*.fastq.gz', + pattern: '*.{fastq.gz,log}', enabled: params.save_preprocessed_reads ] } @@ -347,6 +347,7 @@ process { } withName: CENTRIFUGE_KREPORT { + errorStrategy = {task.exitStatus == 255 ? 'ignore' : 'retry'} ext.args = { "${meta.db_params}" } ext.prefix = params.perform_runmerging ? { "${meta.id}-${meta.db_name}" } : { "${meta.id}-${meta.run_accession}-${meta.db_name}" } publishDir = [ From 5cdf5e958a6be59eced1f9dbf0a24472c7483811 Mon Sep 17 00:00:00 2001 From: mjamy Date: Thu, 28 Jul 2022 15:52:31 +0200 Subject: [PATCH 301/306] Updated modules.config to add 'log' in diamond config --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index c4ab581..9c61999 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -387,7 +387,7 @@ process { publishDir = [ path: { "${params.outdir}/diamond/${meta.db_name}" }, mode: params.publish_dir_mode, - pattern: '*.{blast,xml,txt,daa,sam,tsv,paf}' + pattern: '*.{blast,xml,txt,daa,sam,tsv,paf,log}' ] } From f48b22d615c824165cd40b9466b1656b9850f79a Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Fri, 29 Jul 2022 09:52:49 +0200 Subject: [PATCH 302/306] Apply suggestions from code review --- subworkflows/local/longread_preprocessing.nf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subworkflows/local/longread_preprocessing.nf b/subworkflows/local/longread_preprocessing.nf index bc595d6..3464167 100644 --- a/subworkflows/local/longread_preprocessing.nf +++ b/subworkflows/local/longread_preprocessing.nf @@ -26,7 +26,7 @@ workflow LONGREAD_PREPROCESSING { } ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log ) } else if ( !params.longread_qc_run_clip && params.longread_qc_run_filter ) { @@ -48,8 +48,8 @@ workflow LONGREAD_PREPROCESSING { ch_versions = ch_versions.mix(PORECHOP.out.versions.first()) ch_versions = ch_versions.mix(FILTLONG.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log.collect{it[1]}.ifEmpty([]) ) - ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log.collect{it[1]}.ifEmpty([]) ) + ch_multiqc_files = ch_multiqc_files.mix( PORECHOP.out.log ) + ch_multiqc_files = ch_multiqc_files.mix( FILTLONG.out.log ) } FASTQC_PROCESSED ( ch_processed_reads ) From a97bb69cd28d93b4bae24b99014a6e5e6148c548 Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 23 Aug 2022 14:24:57 +0200 Subject: [PATCH 303/306] Prettier --- assets/multiqc_config.yml | 1 + conf/modules.config | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 8df4c5e..d16ed7e 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -21,6 +21,7 @@ run_modules: - bowtie2 - kraken - malt + - custom_content #extra_fn_clean_exts: # - '_fastp' diff --git a/conf/modules.config b/conf/modules.config index 0330abb..1f4914d 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -241,7 +241,8 @@ process { withName: PRINSEQPLUSPLUS { ext.args = [ params.shortread_complexityfilter_prinseqplusplus_mode == 'dust' ? "-lc_dust=${params.shortread_complexityfilter_prinseqplusplus_dustscore}" : "-lc_entropy=${params.shortread_complexityfilter_entropy}", - "-trim_qual_left=0 -trim_qual_left=0 -trim_qual_window=0 -trim_qual_step=0" + "-trim_qual_left=0 -trim_qual_left=0 -trim_qual_window=0 -trim_qual_step=0", + "-VERBOSE 2" ].join(' ').trim() ext.prefix = { "${meta.id}-${meta.run_accession}" } publishDir = [ From e6c7939305c2ef5477b0576a0efcbd63800b8e9d Mon Sep 17 00:00:00 2001 From: James Fellows Yates Date: Tue, 23 Aug 2022 14:37:01 +0200 Subject: [PATCH 304/306] Add test_nothing config profile for minimal testing --- conf/test_nothing.config | 47 ++++++++++++++++++++++++++++++++++++++++ nextflow.config | 11 +++++----- 2 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 conf/test_nothing.config diff --git a/conf/test_nothing.config b/conf/test_nothing.config new file mode 100644 index 0000000..e4846a3 --- /dev/null +++ b/conf/test_nothing.config @@ -0,0 +1,47 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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/taxprofiler -profile test, --outdir + +---------------------------------------------------------------------------------------- +*/ + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset without performing any profiling 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 + // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets + // TODO nf-core: Give any required params for the test so that command line flags are not needed + input = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/samplesheet.csv' + databases = 'https://raw.githubusercontent.com/nf-core/test-datasets/taxprofiler/database.csv' + perform_shortread_qc = false + perform_longread_qc = false + perform_shortread_complexityfilter = false + perform_shortread_hostremoval = false + perform_longread_hostremoval = false + perform_runmerging = false + hostremoval_reference = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/genomics/homo_sapiens/genome/genome.fasta' + run_kaiju = false + run_kraken2 = false + run_malt = false + run_metaphlan3 = false + run_centrifuge = false + run_diamond = false + run_motus = false +} + +process { + withName: MALT_RUN { + maxForks = 1 + } +} diff --git a/nextflow.config b/nextflow.config index 58c9254..4a83031 100644 --- a/nextflow.config +++ b/nextflow.config @@ -204,11 +204,12 @@ profiles { podman.enabled = false shifter.enabled = false } - test { includeConfig 'conf/test.config' } - test_full { includeConfig 'conf/test_full.config' } - test_noprofiling { includeConfig 'conf/test_noprofiling.config' } - test_nopreprocessing { includeConfig 'conf/test_nopreprocessing.config' } - test_motus { includeConfig 'conf/test_motus.config' } + test { includeConfig 'conf/test.config' } + test_full { includeConfig 'conf/test_full.config' } + test_noprofiling { includeConfig 'conf/test_noprofiling.config' } + test_nopreprocessing { includeConfig 'conf/test_nopreprocessing.config' } + test_nothing { includeConfig 'conf/test_nothing.config' } + test_motus { includeConfig 'conf/test_motus.config' } } // Load igenomes.config if required From 4315141c5fa31e721f0cce4154938275c494c68d Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 23 Aug 2022 15:44:53 +0200 Subject: [PATCH 305/306] Update bin/check_samplesheet.py Co-authored-by: Moritz E. Beber --- bin/check_samplesheet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 4e80b46..8c13690 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -100,7 +100,7 @@ def check_samplesheet(file_in, file_out): sys.exit(1) ## Find locations of mandatory columns - header_locs = dict() + header_locs = {} for i in HEADER: header_locs[i] = header.index(i) From a3055bf6dace3e27710b6d83f2a3236fdecbeddc Mon Sep 17 00:00:00 2001 From: "James A. Fellows Yates" Date: Tue, 23 Aug 2022 15:48:02 +0200 Subject: [PATCH 306/306] Update conf/test_nothing.config --- conf/test_nothing.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test_nothing.config b/conf/test_nothing.config index e4846a3..5cd89eb 100644 --- a/conf/test_nothing.config +++ b/conf/test_nothing.config @@ -12,7 +12,7 @@ params { config_profile_name = 'Test profile' - config_profile_description = 'Minimal test dataset without performing any profiling to check pipeline function' + config_profile_description = 'Minimal test dataset without performing any preprocessing nor profiling to check pipeline function. Useful when you only wish to test a single profiler without having to 'opt-out' of all the others' // Limit resources so that this can run on GitHub Actions max_cpus = 2