diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2ef9f8f1..3d115e80 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,12 +1,12 @@ -# nf-core/mag Contributing Guidelines +# nf-core/mag: Contributing Guidelines Hi there! Many thanks for taking an interest in improving nf-core/mag. -We try to manage the required tasks for nf-core/mag using GitHub issues, you probably came to this page when creating one. Please use the prefilled template to save time. +We try to manage the required tasks for nf-core/mag using GitHub issues, you probably came to this page when creating one. Please use the pre-filled template to save time. However, don't be put off by this template - other more general issues and suggestions are welcome! Contributions to the code are even more welcome ;) -> If you need help using nf-core/mag then the best place to go is the Gitter chatroom where you can ask us questions directly: https://gitter.im/nf-core/Lobby +> If you need help using or modifying nf-core/mag then the best place to ask is on the pipeline channel on [Slack](https://nf-co.re/join/slack). ## Contribution workflow If you'd like to write some code for nf-core/mag, the standard workflow @@ -15,11 +15,30 @@ is as follows: 1. Check that there isn't already an issue about your idea in the [nf-core/mag issues](https://github.com/nf-core/mag/issues) to avoid duplicating work. - * Feel free to add a new issue here for the same reason. + * If there isn't one already, please create one so that others know you're working on this 2. Fork the [nf-core/mag repository](https://github.com/nf-core/mag) to your GitHub account 3. Make the necessary changes / additions within your forked repository -4. Submit a Pull Request against the master branch and wait for the code to be reviewed and merged. +4. Submit a Pull Request against the `dev` branch and wait for the code to be reviewed and merged. If you're not used to this workflow with git, you can start with some [basic docs from GitHub](https://help.github.com/articles/fork-a-repo/) or even their [excellent interactive tutorial](https://try.github.io/). -For further information/help, please consult the [nf-core/mag documentation](https://github.com/nf-core/mag#documentation) and don't hesitate to get in touch on [Gitter](https://gitter.im/nf-core/Lobby) +## Tests +When you create a pull request with changes, [Travis CI](https://travis-ci.org/) will run automatic tests. +Typically, pull-requests are only fully reviewed when these tests are passing, though of course we can help out before then. + +There are typically two types of tests that run: + +### Lint Tests +The nf-core has a [set of guidelines](http://nf-co.re/guidelines) which all pipelines must adhere to. +To enforce these and ensure that all pipelines stay in sync, we have developed a helper tool which runs checks on the pipeline code. This is in the [nf-core/tools repository](https://github.com/nf-core/tools) and once installed can be run locally with the `nf-core lint ` command. + +If any failures or warnings are encountered, please follow the listed URL for more documentation. + +### Pipeline Tests +Each nf-core pipeline should be set up with a minimal set of test-data. +Travis CI then runs the pipeline on this data to ensure that it exists successfully. +If there are any failures then the automated tests fail. +These tests are run both with the latest available version of Nextflow and also the minimum required version that is stated in the pipeline code. + +## Getting help +For further information/help, please consult the [nf-core/mag documentation](https://github.com/nf-core/mag#documentation) and don't hesitate to get in touch on the pipeline channel on [Slack](https://nfcore.slack.com/channels/mag). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..789853ec --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +Hi there! + +Thanks for telling us about a problem with the pipeline. Please delete this text and anything that's not relevant from the template below: + +#### Describe the bug +A clear and concise description of what the bug is. + +#### Steps to reproduce +Steps to reproduce the behaviour: +1. Command line: `nextflow run ...` +2. See error: _Please provide your error message_ + +#### Expected behaviour +A clear and concise description of what you expected to happen. + +#### System: + - Hardware: [e.g. HPC, Desktop, Cloud...] + - Executor: [e.g. slurm, local, awsbatch...] + - OS: [e.g. CentOS Linux, macOS, Linux Mint...] + - Version [e.g. 7, 10.13.6, 18.3...] + +#### Nextflow Installation: + - Version: [e.g. 0.31.0] + +#### Container engine: + - Engine: [e.g. Conda, Docker or Singularity] + - version: [e.g. 1.0.0] + - Image tag: [e.g. nfcore/mag:1.0.0] + +#### Additional context +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..1f025b77 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +Hi there! + +Thanks for suggesting a new feature for the pipeline! Please delete this text and anything that's not relevant from the template below: + +#### Is your feature request related to a problem? Please describe. +A clear and concise description of what the problem is. +Ex. I'm always frustrated when [...] + +#### Describe the solution you'd like +A clear and concise description of what you want to happen. + +#### Describe alternatives you've considered +A clear and concise description of any alternative solutions or features you've considered. + +#### Additional context +Add any other context about the feature request here. diff --git a/.github/pull_request.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 96% rename from .github/pull_request.md rename to .github/PULL_REQUEST_TEMPLATE.md index 60b03628..b2bce74e 100644 --- a/.github/pull_request.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,7 +5,7 @@ Please fill in the appropriate checklist below (delete whatever is not relevant) ## PR checklist - [ ] This comment contains a description of changes (with reason) - [ ] If you've fixed a bug or added code that should be tested, add tests! - - [ ] If necessary, also make a PR on the [nf-core/mag branch on the nf-core/test-datasets repo]( https://github.com/nf-core/test-datasets/pull/newnf-core/mag) + - [ ] If necessary, also make a PR on the [nf-core/mag branch on the nf-core/test-datasets repo]( https://github.com/nf-core/test-datasets/pull/new/nf-core/mag) - [ ] Ensure the test suite passes (`nextflow run . -profile test,docker`). - [ ] Make sure your code lints (`nf-core lint .`). - [ ] Documentation in `docs` is updated diff --git a/.github/bug_report.md b/.github/bug_report.md deleted file mode 100644 index d0405d12..00000000 --- a/.github/bug_report.md +++ /dev/null @@ -1,29 +0,0 @@ -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Command line '...' -2. See error **Please provide your error message** - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**System (please complete the following information):** - - Hardware: [e.g. HPC, Desktop, Cloud...] - - Executor: [e.g. slurm, local, awsbatch...] - - OS: [e.g. CentOS Linux, macOS, Linux Mint...] - - Version [e.g. 7, 10.13.6, 18.3...] - -**Nextflow (please complete the following information):** - - Version: [e.g. 0.31.0] - -**Container engine (please complete the following information):** - - Engine: [e.g. Conda, Docker or Singularity] - - version: [e.g. 1.0.0] - -**Container (please complete the following information):** - - tag: [e.g. 1.0.0] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/feature_request.md b/.github/feature_request.md deleted file mode 100644 index 3616d75c..00000000 --- a/.github/feature_request.md +++ /dev/null @@ -1,16 +0,0 @@ -**Is your feature request related to a problem? Please describe.** - -A clear and concise description of what the problem is. -Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** - -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** - -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** - -Add any other context about the feature request here. diff --git a/.github/markdownlint.yml b/.github/markdownlint.yml new file mode 100644 index 00000000..e052a635 --- /dev/null +++ b/.github/markdownlint.yml @@ -0,0 +1,9 @@ +# Markdownlint configuration file +default: true, +line-length: false +no-multiple-blanks: 0 +blanks-around-headers: false +blanks-around-lists: false +header-increment: false +no-duplicate-header: + siblings_only: true diff --git a/.gitignore b/.gitignore index bedb6feb..636ede74 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,7 @@ work/ results/ .DS_Store tests/test_data +*.pyc .vscode -db/ \ No newline at end of file +db/ +tests/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9d844bef..43388f8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,30 +8,36 @@ matrix: fast_finish: true before_install: - # PRs made to 'master' branch should always orginate from another repo or the 'dev' branch - - '[ $TRAVIS_PULL_REQUEST = "false" ] || [ $TRAVIS_BRANCH != "master" ] || ([ $TRAVIS_PULL_REQUEST_SLUG = $TRAVIS_REPO_SLUG ] && [ $TRAVIS_PULL_REQUEST_BRANCH = "dev" ])' + # PRs to master are only ok if coming from dev branch + - '[ $TRAVIS_PULL_REQUEST = "false" ] || [ $TRAVIS_BRANCH != "master" ] || ([ $TRAVIS_PULL_REQUEST_SLUG = $TRAVIS_REPO_SLUG ] && ([ $TRAVIS_PULL_REQUEST_BRANCH = "dev" ] || [ $TRAVIS_PULL_REQUEST_BRANCH = "patch" ]))' # Pull the docker image first so the test doesn't wait for this - docker pull nfcore/mag:dev # Fake the tag locally so that the pipeline runs properly + # Looks weird when this is :dev to :dev, but makes sense when testing code for a release (:dev to :1.0.1) - docker tag nfcore/mag:dev nfcore/mag:1.0.0 install: # Install Nextflow - - mkdir /tmp/nextflow - - cd /tmp/nextflow + - mkdir /tmp/nextflow && cd /tmp/nextflow - wget -qO- get.nextflow.io | bash - sudo ln -s /tmp/nextflow/nextflow /usr/local/bin/nextflow # Install nf-core/tools + - pip install --upgrade pip - pip install nf-core # Reset - - cd ${TRAVIS_BUILD_DIR} + - mkdir ${TRAVIS_BUILD_DIR}/tests && cd ${TRAVIS_BUILD_DIR}/tests + # Install markdownlint-cli + - sudo apt-get install npm && npm install -g markdownlint-cli env: - - NXF_VER='0.32.0' # Specify a minimum NF version that should be tested and work - - NXF_VER='' # Plus: get the latest NF version and check, that it works + - NXF_VER='19.01.0' # Specify a minimum NF version that should be tested and work + - NXF_VER='' # Plus: get the latest NF version and check that it works script: # Lint the pipeline code - nf-core lint ${TRAVIS_BUILD_DIR} - # Run the pipeline with the test profile - - nextflow run main.nf -profile test,docker + # Lint the documentation + - markdownlint ${TRAVIS_BUILD_DIR} -c ${TRAVIS_BUILD_DIR}/.github/markdownlint.yml + # Run the pipeline with the test profile(s) + - nextflow run ${TRAVIS_BUILD_DIR} -profile test,docker + - nextflow run ${TRAVIS_BUILD_DIR} -profile test_hybrid,docker diff --git a/CHANGELOG.md b/CHANGELOG.md index afdcb19d..f00c9fa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ -## nf-core/mag version 1.0.0 - +# nf-core/mag: Changelog + +## v1.0.0 - 2019/12/20 Initial release of nf-core/mag, created with the [nf-core](http://nf-co.re/) template. +As this release the pipeline will have the following functionailities: + +- short and long reads QC (fastp, porechop, filtlong, fastqc) +- Lambda and PhiX detection and filtering (bowtie2, nanolyse) +- Taxonomic classification of reads (centrifuge, kraken2) +- Short read and hybrid assembly (megahit, metaspades) +- metagenome binning (metabat2) +- QC of bins (busco, quast) +- annotation (cat/bat) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 21096193..3a8a013d 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team on the [Gitter channel](https://gitter.im/nf-core/Lobby). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team on [Slack](nf-co.re/join/slack). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. diff --git a/Dockerfile b/Dockerfile index 1c3cb855..6f100d3f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,8 @@ -FROM nfcore/base +FROM nfcore/base:1.7 -LABEL maintainer="Hadrien Gourlé " -LABEL description="Docker image containing all requirements for nf-core/mag pipeline" +LABEL authors="Hadrien Gourlé , Daniel Straub " \ + description="Docker image containing all requirements for nf-core/mag pipeline" COPY environment.yml / RUN conda env create -f /environment.yml && conda clean -a ENV PATH /opt/conda/envs/nf-core-mag-1.0.0/bin:$PATH - diff --git a/LICENSE b/LICENSE index f4e324c3..30d2ccab 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright 2018, Hadrien Gourlé +Copyright 2018, Hadrien Gourlé, Daniel Straub Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 7aa34134..47b59a92 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,43 @@ # ![mag](https://raw.githubusercontent.com/nf-core/mag/master/docs/images/mag_logo.png) -# nf-core/mag +**Assembly, binning and annotation of metagenomes**. -**Assembly, binning and annotation of metagenomes** - -[![Build Status](https://travis-ci.org/nf-core/mag.svg?branch=master)](https://travis-ci.org/nf-core/mag) -[![Nextflow](https://img.shields.io/badge/nextflow-%E2%89%A50.32.0-brightgreen.svg)](https://www.nextflow.io/) +[![Build Status](https://travis-ci.com/nf-core/mag.svg?branch=master)](https://travis-ci.com/nf-core/mag) +[![Nextflow](https://img.shields.io/badge/nextflow-%E2%89%A519.01.0-brightgreen.svg)](https://www.nextflow.io/) [![install with bioconda](https://img.shields.io/badge/install%20with-bioconda-brightgreen.svg)](http://bioconda.github.io/) -[![Docker](https://img.shields.io/docker/automated/hadrieng/mag.svg)](https://hub.docker.com/r/hadrieng/mag) -![Singularity Container available](https://img.shields.io/badge/singularity-available-7E4C74.svg) +[![Docker](https://img.shields.io/docker/automated/nfcore/mag.svg)](https://hub.docker.com/r/nfcore/mag) + +## Introduction + +This pipeline is for assembly, binning and annotation of metagenomes. +It supports both short and long reads, quality trims the reads and adapters with [https://github.com/OpenGene/fastp](fastp) and [https://github.com/rrwick/Porechop](porechop) and performs basic QC with [https://www.bioinformatics.babraham.ac.uk/projects/fastqc/](fastqc). -### Introduction +The pipeline then: -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 comes with docker / singularity containers making installation trivial and results highly reproducible. +- assigns taxonomy to reads using [https://ccb.jhu.edu/software/centrifuge/](centrifuge) and/or [https://ccb.jhu.edu/software/kraken2/](kraken2) +- performs assembly using [https://github.com/voutcn/megahit](megahit) and [http://cab.spbu.ru/software/spades/](spades), and checks their quality using [http://quast.sourceforge.net/quast](quast) +- performs metagenome binning using [https://bitbucket.org/berkeleylab/metabat/src/master/](metabat2), and checks the quality of the genome bins using [https://busco.ezlab.org/](busco) -### Documentation +Furthermore, the pipeline creates various reports in the results directory specified, including a [https://multiqc.info/](multiqc) report summarizing some of the findings and software versions. + +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 comes with docker containers making installation trivial and results highly reproducible. + +## Documentation The nf-core/mag pipeline comes with documentation about the pipeline, found in the `docs/` directory: -1. [Installation](docs/installation.md) +1. [Installation](https://nf-co.re/usage/installation) 2. Pipeline configuration - - [Local installation](docs/configuration/local.md) - - [Adding your own system](docs/configuration/adding_your_own.md) + - [Local installation](https://nf-co.re/usage/local_installation) + - [Adding your own system config](https://nf-co.re/usage/adding_own_config) + - [Reference genomes](https://nf-co.re/usage/reference_genomes) 3. [Running the pipeline](docs/usage.md) 4. [Output and how to interpret the results](docs/output.md) -5. [Troubleshooting](docs/troubleshooting.md) +5. [Troubleshooting](https://nf-co.re/usage/troubleshooting) + +## Credits -### Credits +This pipeline was written by [Hadrien Gourlé](https://hadriengourle.com) at [SLU](https://slu.se) and Daniel Straub ([@d4straub](https://github.com/d4straub)). -This pipeline was written by [Hadrien Gourlé](https://hadriengourle.com) at [SLU](https://slu.se). +Long read processing was inspired by [caspargross/HybridAssembly](https://github.com/caspargross/HybridAssembly) written by Caspar Gross [@caspargross](https://github.com/caspargross) diff --git a/Singularity b/Singularity deleted file mode 100644 index 54c1cc42..00000000 --- a/Singularity +++ /dev/null @@ -1,19 +0,0 @@ -From:nfcore/base -Bootstrap:docker - -%labels -======= - MAINTAINER Hadrien Gourlé - DESCRIPTION Singularity image containing all requirements for nf-core/mag pipeline - VERSION 1.0.0 - -%environment - PATH=/opt/conda/envs/nf-core-mag-1.0.0/bin:$PATH - export PATH - -%files - environment.yml / - -%post - /opt/conda/bin/conda env create -f /environment.yml - /opt/conda/bin/conda clean -a diff --git a/assets/data/GCA_000840245.1_ViralProj14204_genomic.fna.gz b/assets/data/GCA_000840245.1_ViralProj14204_genomic.fna.gz new file mode 100644 index 00000000..2fa0ed2a Binary files /dev/null and b/assets/data/GCA_000840245.1_ViralProj14204_genomic.fna.gz differ diff --git a/assets/data/GCA_002596845.1_ASM259684v1_genomic.fna.gz b/assets/data/GCA_002596845.1_ASM259684v1_genomic.fna.gz new file mode 100644 index 00000000..511f57e8 Binary files /dev/null and b/assets/data/GCA_002596845.1_ASM259684v1_genomic.fna.gz differ diff --git a/assets/email_template.txt b/assets/email_template.txt index 5c47bcb3..c99c11e1 100644 --- a/assets/email_template.txt +++ b/assets/email_template.txt @@ -17,23 +17,6 @@ ${errorReport} } %> -<% if (!success){ - out << """#################################################### -## nf-core/mag execution completed unsuccessfully! ## -#################################################### -The exit status of the task that caused the workflow execution to fail was: $exitStatus. -The full error message was: - -${errorReport} -""" -} else { - out << "## nf-core/mag execution completed successfully! ##" -} -%> - - - - The workflow was completed at $dateComplete (duration: $duration) The command used to launch the workflow was as follows: diff --git a/conf/multiqc_config.yaml b/assets/multiqc_config.yaml similarity index 95% rename from conf/multiqc_config.yaml rename to assets/multiqc_config.yaml index 276fe4ae..c3cab132 100644 --- a/conf/multiqc_config.yaml +++ b/assets/multiqc_config.yaml @@ -5,3 +5,5 @@ report_comment: > report_section_order: nf-core/mag-software-versions: order: -1000 + +export_plots: true diff --git a/assets/sendmail_template.txt b/assets/sendmail_template.txt index fd1cd739..2d671220 100644 --- a/assets/sendmail_template.txt +++ b/assets/sendmail_template.txt @@ -1,11 +1,36 @@ To: $email Subject: $subject Mime-Version: 1.0 -Content-Type: multipart/related;boundary="nfmimeboundary" +Content-Type: multipart/related;boundary="nfcoremimeboundary" ---nfmimeboundary +--nfcoremimeboundary Content-Type: text/html; charset=utf-8 $email_html ---nfmimeboundary-- +<% +if (mqcFile){ +def mqcFileObj = new File("$mqcFile") +if (mqcFileObj.length() < mqcMaxSize){ +out << """ +--nfcoremimeboundary +Content-Type: text/html; name=\"multiqc_report\" +Content-Transfer-Encoding: base64 +Content-ID: +Content-Disposition: attachment; filename=\"${mqcFileObj.getName()}\" + +${mqcFileObj. + bytes. + encodeBase64(). + toString(). + tokenize( '\n' )*. + toList()*. + collate( 76 )*. + collect { it.join() }. + flatten(). + join( '\n' )} +""" +}} +%> + +--nfcoremimeboundary-- diff --git a/bin/__pycache__/scrape_software_versions.cpython-36.pyc b/bin/__pycache__/scrape_software_versions.cpython-36.pyc deleted file mode 100644 index b7b7d01a..00000000 Binary files a/bin/__pycache__/scrape_software_versions.cpython-36.pyc and /dev/null differ diff --git a/bin/combine_tables.py b/bin/combine_tables.py new file mode 100755 index 00000000..705eeaa7 --- /dev/null +++ b/bin/combine_tables.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +#USAGE: ./combine_tables.py + +import pandas as pd +from sys import stdout +from sys import argv + +# Read files +file1 = pd.read_csv(argv[1], sep="\t") +file2 = pd.read_csv(argv[2], sep="\t") + +# Merge files +result = pd.merge(file1, file2, left_on="GenomeBin", right_on="Assembly", how='outer') + +# Print to stdout +result.to_csv(stdout, sep='\t') diff --git a/bin/merge_bins.py b/bin/merge_bins.py deleted file mode 100755 index c91cb9c0..00000000 --- a/bin/merge_bins.py +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function - -import os -import sys -import argparse - -from shutil import copyfile, copyfileobj - -from Bio import SeqIO -from Bio.SeqUtils import GC - - -class Bin(object): - """a genome bin""" - - def __init__(self, id, compl, contam): - super(Bin, self).__init__() - self.id = id - self.compl = compl - self.contam = contam - self.read_cov = float() - self.phylo = [] - - -class PossibleMerge(object): - """A possible merger between two bins. Corresponds to one line of - merger.tsv""" - - def __init__(self, line): - self.bin_1 = Bin(line[0], line[2], line[3]) - self.bin_2 = Bin(line[1], line[4], line[5]) - self.delta_compl = float(line[6]) - self.delta_contam = float(line[7]) - self.delta_merger = float(line[8]) # ??? - self.merged_compl = float(line[9]) - self.merged_contam = float(line[10]) - - def add_profile_info(self, profile_txt, mean): - for profile in self.parse_profile(profile_txt): - cov = (float(profile[2]) * mean) / (float(profile[1]) * 1e6) - cov = round(cov, 2) - if profile[0] == self.bin_1.id and self.bin_1.read_cov == 0.0: - self.bin_1.read_cov = cov - elif profile[0] == self.bin_2.id and self.bin_2.read_cov == 0.0: - self.bin_2.read_cov = cov - - def add_tree_info(self, tree_qa): - for phylogeny in self.parse_tree(tree_qa): - if phylogeny[0] == self.bin_1.id and self.bin_1.phylo == []: - self.bin_1.phylo = phylogeny[3:] - elif phylogeny[0] == self.bin_2.id and self.bin_2.phylo == []: - self.bin_2.phylo = phylogeny[3:] - - @staticmethod - def parse_profile(profile_txt): - with open(profile_txt, "r") as p: - p.readline() - header = p.readline() - p.readline() - for l in p: - sl = l.split() - if len(sl) < 2: - continue - yield(sl[:3]) # bin_id, bin size, n mapped reads - - @staticmethod - def parse_tree(tree_qa): - with open(tree_qa, "r") as t: - t.readline() - header = t.readline() - t.readline() - for l in t: - sl = l.split() - if len(sl) < 2: - continue - yield(sum([i.split(";") for i in sl], [])) - - -def get_mean(length_file): - with open(length_file, "r") as f: - total = [int(line.split()[0]) for line in f] - mean = sum(total) / len(total) - return(mean) - - -def parse_merge_file(merger_tsv): - with open(merger_tsv, "r") as m: - m.readline() # discard the header - for l in m: - line = l.split() - pm = PossibleMerge(line) - yield(pm) - - -def concatenate(file_list, output): - """Concatenate files together - Args: - file_list (list): the list of input files (can be a generator) - output (string): the output file name - """ - try: - out_file = open(output, "wb") - except (IOError, OSError) as e: - print("Failed to open output file: %s" % e) - sys.exit(1) - - with out_file: - for file_name in file_list: - if file_name is not None: - with open(file_name, "rb") as f: - copyfile(f, out_file) - - -def merge(args): - all_bins = os.listdir(args.bins) - n_merged = 0 - - # merge bins according to criteria in - # https://doi.org/10.1038/s41564-017-0012-7 - mean_read_length = get_mean(args.length) - for pm in parse_merge_file(args.merger): - pm.add_profile_info(args.profile, mean_read_length) - pm.add_tree_info(args.tree) - abs_delta_cov = \ - max(pm.bin_1.read_cov, pm.bin_2.read_cov) /\ - min(pm.bin_1.read_cov, pm.bin_2.read_cov) - clades = [] - for p1, p2 in zip(pm.bin_1.phylo, pm.bin_2.phylo): - clades.append((p1, p2)) - - if pm.delta_contam <= args.delta_cont and \ - pm.merged_contam <= args.merged_cont and \ - pm.delta_compl >= args.delta_compl and \ - abs_delta_cov >= args.abs_delta_cov and \ - clades[-1][0] == clades[-1][1]: - - # then read the two bins - bin_1_p = "%s/%s.fa" % (args.bins, pm.bin_1.id) - bin_2_p = "%s/%s.fa" % (args.bins, pm.bin_2.id) - with open(bin_1_p, "r") as bin_1, open(bin_2_, "r") as bin_2: - concat_seq_1 = concat_seq_2 = "" - for record in SeqIO.parse(bin_1, "fasta"): - concat_seq_1 += record.seq - gc_bin_1 = GC(concat_seq_1) - for record in SeqIO.parse(bin_2, "fasta"): - concat_seq_2 += record.seq - gc_bin_2 = GC(concat_seq_2) - - # then merge if similar GC content - if abs(gc_bin_1 - gc_bin_2) <= args.delta_gc: - output_name = "%s/merged_%i.fa" % (args.outdir, n_merged) - concatenate([bin_1_p, bin_2_p], output_name) - all_bins.remove(bin_1_p) - all_bins.remove(bin_2_p) - n_merged += 1 - - else: - continue - # now puts the single bins in the same output directory - for bin in all_bins: - if bin.endswith(".fa") or bin.endswith(".fasta"): - path = "%s/%s" % (args.bins, bin) - output_name = "%s/%s" % (args.outdir, bin) - copyfile(path, output_name) - - -def main(): - parser = argparse.ArgumentParser( - prog="merge_bins.py", - usage="parse merger.tsv from checkm and merge bins" - ) - parser.add_argument( - "--profile", - metavar="profile.txt", - help="output of checkm profile" - ) - parser.add_argument( - "--tree", - metavar="tree_qa.txt", - help="output of checkm tree_qa" - ) - parser.add_argument( - "--length", - metavar="read_length.txt", - help="a file containing read length distribution" - ) - parser.add_argument( - "--merger", - metavar="merger.tsv", - help="merger.tsv produced by checkm merge" - ) - parser.add_argument( - "--delta_cont", - metavar="int", - default=5, - help="Max increase in contamination to merge bins (default: 5)" - ) - parser.add_argument( - "--merged_cont", - metavar="int", - default=15, - help="Max total contamination to merge bins (default: 15)" - ) - parser.add_argument( - "--delta_compl", - metavar="int", - default=10, - help="Min increase in completion to merge bins (default: 10)" - ) - parser.add_argument( - "--abs_delta_cov", - metavar="float", - default=0.75, - help="Min coverage ratio to merge bins (default: 0.75)" - ) - parser.add_argument( - "--delta_gc", - metavar="int", - default=3, - help="Max %GC diffrence to merge bins (default: 3)" - ) - parser.add_argument( - "bins", - metavar="bins/", - help="the bin directory (bins must end with .fa or .fasta)" - ) - parser.add_argument( - "outdir", - metavar="outdir/", - help="output directory (must exist)" - ) - parser.set_defaults(func=merge) - args = parser.parse_args() - - try: - args.func(args) - except AttributeError as e: - parser.print_help() - raise - - -if __name__ == "__main__": - main() diff --git a/bin/scrape_software_versions.py b/bin/scrape_software_versions.py index 6eb41868..1addaf9f 100755 --- a/bin/scrape_software_versions.py +++ b/bin/scrape_software_versions.py @@ -7,12 +7,20 @@ 'nf-core/mag': ['v_pipeline.txt', r"(\S+)"], 'Nextflow': ['v_nextflow.txt', r"(\S+)"], 'MultiQC': ['v_multiqc.txt', r"multiqc, version (\S+)"], - 'fastqc': ['v_fastqc.txt', r"FastQC v(\S+)"], - 'fastp': ['v_fastp.txt', r"fastp (\S+)"], - 'megahit': ['v_megahit.txt', r"MEGAHIT v(\S+)"], - 'metabat': ['v_metabat.txt', r"version (\S+)"], - 'checkm': ['v_checkm.txt', r"CheckM v(\S+)"], - 'refinem': ['v_refinem.txt', r"RefineM v(\S+)"] + 'FastQC': ['v_fastqc.txt', r"FastQC v(\S+)"], + 'Fastp': ['v_fastp.txt', r"fastp (\S+)"], + 'Megahit': ['v_megahit.txt', r"MEGAHIT v(\S+)"], + 'Metabat': ['v_metabat.txt', r"version (\S+)"], + 'NanoPlot': ['v_nanoplot.txt', r"NanoPlot (\S+)"], + 'Filtlong': ['v_filtlong.txt', r"Filtlong v(\S+)"], + 'Porechop': ['v_porechop.txt', r"(\S+)"], + 'NanoLyse': ['v_nanolyse.txt', r"NanoLyse (\S+)"], + 'SPAdes': ['v_spades.txt', r"SPAdes v(\S+)"], + 'BUSCO': ['v_busco.txt', r"BUSCO (\S+)"], + 'Centrifuge': ['v_centrifuge.txt', r"centrifuge-class version (\S+)"], + 'Kraken2': ['v_kraken2.txt', r"Kraken version (\S+)-beta"], + 'Quast': ['v_quast.txt', r"QUAST v(\S+)"], + 'CAT': ['v_cat.txt', r"CAT v(\S+)"] } results = OrderedDict() results['nf-core/mag'] = 'N/A' @@ -22,8 +30,16 @@ results['fastp'] = 'N/A' results['megahit'] = 'N/A' results['metabat'] = 'N/A' -results['checkm'] = 'N/A' -results['refinem'] = 'N/A' +results['NanoPlot'] = 'N/A' +results['Filtlong'] = 'N/A' +results['porechop'] = 'N/A' +results['NanoLyse'] = 'N/A' +results['SPAdes'] = 'N/A' +results['BUSCO'] = 'N/A' +results['centrifuge'] = 'N/A' +results['Kraken2'] = 'N/A' +results['CAT'] = 'N/A' +results['Quast'] = 'N/A' # Search each file using its regex for k, v in regexes.items(): @@ -33,9 +49,14 @@ if match: results[k] = "v{}".format(match.group(1)) +# Remove software set to false in results +for k in results: + if not results[k]: + del(results[k]) + # Dump to YAML print(''' -id: 'nf-core/mag-software-versions' +id: 'software_versions' section_name: 'nf-core/mag Software Versions' section_href: 'https://github.com/nf-core/mag' plot_type: 'html' @@ -44,5 +65,10 @@
''') for k, v in results.items(): - print("
{}
{}
".format(k, v)) + print("
{}
{}
".format(k, v)) print("
") + +# Write out regexes as csv file: +with open('software_versions.csv', 'w') as f: + for k, v in results.items(): + f.write("{}\t{}\n".format(k, v)) diff --git a/bin/summary_busco.py b/bin/summary_busco.py new file mode 100755 index 00000000..f3dd2eba --- /dev/null +++ b/bin/summary_busco.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# USAGE: ./summary.busco.py *.txt + +import re +from sys import argv + +# "# Summarized benchmarking in BUSCO notation for file MEGAHIT-testset1.contigs.fa" +# " C:0.0%[S:0.0%,D:0.0%],F:0.0%,M:100.0%,n:148" + +regexes = [r"# Summarized benchmarking in BUSCO notation for file (\S+)", r" C:(\S+)%\[S:", + r"%\[S:(\S+)%,D:", r"%,D:(\S+)%\],F:", r"%\],F:(\S+)%,M:", r"%,M:(\S+)%,n:", r"%,n:(\S+)"] +columns = ["GenomeBin", "%Complete", "%Complete and single-copy", + "%Complete and duplicated", "%Fragmented", "%Missing", "Total number"] + +# Search each file using its regex +print("\t".join(columns)) +for FILE in argv[1:]: + with open(FILE) as x: + results = [] + TEXT = x.read() + for REGEX in regexes: + match = re.search(REGEX, TEXT) + if match: + results.append(match.group(1)) + print("\t".join(results)) diff --git a/conf/awsbatch.config b/conf/awsbatch.config deleted file mode 100644 index 79078c7b..00000000 --- a/conf/awsbatch.config +++ /dev/null @@ -1,13 +0,0 @@ -/* - * ------------------------------------------------- - * Nextflow config file for AWS Batch - * ------------------------------------------------- - * Imported under the 'awsbatch' Nextflow profile in nextflow.config - * Uses docker for software depedencies automagically, so not specified here. - */ - -aws.region = params.awsregion -process.executor = 'awsbatch' -process.queue = params.awsqueue -executor.awscli = '/home/ec2-user/miniconda/bin/aws' -params.tracedir = './' diff --git a/conf/base.config b/conf/base.config index 78297e52..e89c2e68 100644 --- a/conf/base.config +++ b/conf/base.config @@ -11,31 +11,88 @@ process { - container = params.container - cpus = { check_max( 1 * task.attempt, 'cpus' ) } memory = { check_max( 8.GB * task.attempt, 'memory' ) } time = { check_max( 2.h * task.attempt, 'time' ) } - errorStrategy = { task.exitStatus in [143,137] ? 'retry' : 'finish' } + errorStrategy = { task.exitStatus in [143,137,104,134,139] ? 'retry' : 'finish' } maxRetries = 1 maxErrors = '-1' // Process-specific resource requirements - withName: fastqc_raw { - errorStrategy = { task.exitStatus in [143,137] ? 'retry' : 'ignore' } - } - withName: fastqc_trimmed { - errorStrategy = { task.exitStatus in [143,137] ? 'retry' : 'ignore' } + withName: busco_download_db { + time = 4.h } - withName: multiqc { + withName: busco { errorStrategy = { task.exitStatus in [143,137] ? 'retry' : 'ignore' } } - withName: checkm_download_db { + withName: phix_download_db { time = 4.h } - withName: refinem_download_db { - time = 4.h + withName: porechop { + cpus = { check_max (4 * task.attempt, 'cpus' ) } + memory = { check_max (30.GB * task.attempt, 'memory' ) } + time = { check_max (4.h * task.attempt, 'time' ) } + } + withName: nanolyse { + cpus = { check_max (2 * task.attempt, 'cpus' ) } + memory = { check_max (10.GB * task.attempt, 'memory' ) } + time = { check_max (3.h * task.attempt, 'time' ) } + } + //filtlong: exponential increase of memory and time with attempts + withName: filtlong { + cpus = { check_max (8 * task.attempt, 'cpus' ) } + memory = { check_max (64.GB * (2**(task.attempt-1)), 'memory' ) } + time = { check_max (24.h * (2**(task.attempt-1)), 'time' ) } + } + withName: remove_phix { + cpus = { check_max (4 * task.attempt, 'cpus' ) } + memory = { check_max (8.GB * task.attempt, 'memory' ) } + time = { check_max (6.h * task.attempt, 'time' ) } + } + withName: centrifuge { + cpus = { check_max (8 * task.attempt, 'cpus' ) } + memory = { check_max (40.GB * task.attempt, 'memory' ) } + time = { check_max (12.h * task.attempt, 'time' ) } + } + withName: kraken2 { + cpus = { check_max (8 * task.attempt, 'cpus' ) } + memory = { check_max (40.GB * task.attempt, 'memory' ) } + time = { check_max (12.h * task.attempt, 'time' ) } + } + withName: krona { + cpus = { check_max (8 * task.attempt, 'cpus' ) } + memory = { check_max (20.GB * task.attempt, 'memory' ) } + time = { check_max (12.h * task.attempt, 'time' ) } + } + withName: cat { + cpus = { check_max (8 * task.attempt, 'cpus' ) } + memory = { check_max (40.GB * task.attempt, 'memory' ) } + time = { check_max (12.h * task.attempt, 'time' ) } + } + withName: megahit { + cpus = { check_max (8 * task.attempt, 'cpus' ) } + memory = { check_max (40.GB * task.attempt, 'memory' ) } + time = { check_max (16.h * task.attempt, 'time' ) } + } + //SPAdes returns error(1) if it runs out of memory (and for other reasons as well...)! + //exponential increase of memory and time with attempts + withName: spades { + cpus = { check_max (10 * task.attempt, 'cpus' ) } + memory = { check_max (64.GB * (2**(task.attempt-1)), 'memory' ) } + time = { check_max (24.h * (2**(task.attempt-1)), 'time' ) } + errorStrategy = { task.exitStatus in [143,137,1] ? 'retry' : 'ignore' } + } + withName: spadeshybrid { + cpus = { check_max (10 * task.attempt, 'cpus' ) } + memory = { check_max (64.GB * (2**(task.attempt-1)), 'memory' ) } + time = { check_max (24.h * (2**(task.attempt-1)), 'time' ) } + errorStrategy = { task.exitStatus in [143,137,1] ? 'retry' : 'ignore' } + } + withName: metabat { + cpus = { check_max (8 * task.attempt, 'cpus' ) } + memory = { check_max (20.GB * task.attempt, 'memory' ) } + time = { check_max (8.h * task.attempt, 'time' ) } } } diff --git a/conf/test.config b/conf/test.config index b51324d6..63c407bd 100644 --- a/conf/test.config +++ b/conf/test.config @@ -4,18 +4,24 @@ * ------------------------------------------------- * Defines bundled input files and everything required * to run a fast and simple test. Use as follows: - * nextflow run nf-core/methylseq -profile test + * nextflow run nf-core/mag -profile test */ params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check pipeline function' + // Limit resources so that this can run on Travis max_cpus = 2 max_memory = 7.GB max_time = 48.h params.outdir = "./tests" params.temp_dir = "./tests/tmp_dir" - // Input data singleEnd = false readPaths = [ - ['test_minigut', ['https://github.com/HadrienG/test-datasets/raw/mag/test_data/test_minigut_R1.fastq.gz', 'https://github.com/HadrienG/test-datasets/raw/mag/test_data/test_minigut_R2.fastq.gz']] + ['test_minigut', ['https://github.com/nf-core/test-datasets/raw/mag/test_data/test_minigut_R1.fastq.gz', 'https://github.com/HadrienG/test-datasets/raw/mag/test_data/test_minigut_R2.fastq.gz']], + ['test_minigut_sample2', ['https://github.com/nf-core/test-datasets/raw/mag/test_data/test_minigut_sample2_R1.fastq.gz', 'https://github.com/HadrienG/test-datasets/raw/mag/test_data/test_minigut_sample2_R2.fastq.gz']] ] + centrifuge_db = "https://github.com/nf-core/test-datasets/raw/mag/test_data/minigut_cf.tar.gz" + kraken2_db = "https://github.com/nf-core/test-datasets/raw/mag/test_data/minigut_kraken.tgz" + skip_krona = true } diff --git a/conf/test_hybrid.config b/conf/test_hybrid.config new file mode 100644 index 00000000..6e7c76a5 --- /dev/null +++ b/conf/test_hybrid.config @@ -0,0 +1,19 @@ +/* + * ------------------------------------------------- + * Nextflow config file for running tests + * ------------------------------------------------- + * Defines bundled input files and everything required + * to run a fast and simple test. Use as follows: + * nextflow run nf-core/methylseq -profile test + */ + +params { + max_cpus = 2 + max_memory = 7.GB + max_time = 48.h + params.outdir = "./tests" + params.temp_dir = "./tests/tmp_dir" + // Input data + singleEnd = false + params.manifest = 'https://github.com/HadrienG/test-datasets/raw/mag/test_data/manifest.txt' +} diff --git a/docs/README.md b/docs/README.md index 938c19df..f38e697f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,9 +2,11 @@ The nf-core/mag documentation is split into the following files: -1. [Installation](installation.md) -2. [Running the pipeline](usage.md) -3. Pipeline configuration - - [Adding your own system](configuration/adding_your_own.md) +1. [Installation](https://nf-co.re/usage/installation) +2. Pipeline configuration + - [Local installation](https://nf-co.re/usage/local_installation) + - [Adding your own system config](https://nf-co.re/usage/adding_own_config) + - [Reference genomes](https://nf-co.re/usage/reference_genomes) +3. [Running the pipeline](usage.md) 4. [Output and how to interpret the results](output.md) -5. [Troubleshooting](troubleshooting.md) +5. [Troubleshooting](https://nf-co.re/usage/troubleshooting) diff --git a/docs/configuration/adding_your_own.md b/docs/configuration/adding_your_own.md deleted file mode 100644 index 7916774e..00000000 --- a/docs/configuration/adding_your_own.md +++ /dev/null @@ -1,100 +0,0 @@ -# nf-core/mag: Configuration for other clusters - -It is entirely possible to run this pipeline on other clusters, though you will need to set up your own config file so that the pipeline knows how to work with your cluster. - -> If you think that there are other people using the pipeline who would benefit from your configuration (eg. other common cluster setups), please let us know. We can add a new configuration and profile which can used by specifying `-profile ` when running the pipeline. - -If you are the only person to be running this pipeline, you can create your config file as `~/.nextflow/config` and it will be applied every time you run Nextflow. Alternatively, save the file anywhere and reference it when running the pipeline with `-c path/to/config` (see the [Nextflow documentation](https://www.nextflow.io/docs/latest/config.html) for more). - -A basic configuration comes with the pipeline, which runs by default (the `standard` config profile - see [`conf/base.config`](../conf/base.config)). This means that you only need to configure the specifics for your system and overwrite any defaults that you want to change. - -## Cluster Environment -By default, pipeline uses the `local` Nextflow executor - in other words, all jobs are run in the login session. If you're using a simple server, this may be fine. If you're using a compute cluster, this is bad as all jobs will run on the head node. - -To specify your cluster environment, add the following line to your config file: - -```nextflow -process.executor = 'YOUR_SYSTEM_TYPE' -``` - -Many different cluster types are supported by Nextflow. For more information, please see the [Nextflow documentation](https://www.nextflow.io/docs/latest/executor.html). - -Note that you may need to specify cluster options, such as a project or queue. To do so, use the `clusterOptions` config option: - -```nextflow -process { - executor = 'SLURM' - clusterOptions = '-A myproject' -} -``` - - -## Software Requirements -To run the pipeline, several software packages are required. How you satisfy these requirements is essentially up to you and depends on your system. If possible, we _highly_ recommend using either Docker or Singularity. - -Please see the [`installation documentation`](../installation.md) for how to run using the below as a one-off. These instructions are about configuring a config file for repeated use. - -### Docker -Docker is a great way to run nf-core/mag, as it manages all software installations and allows the pipeline to be run in an identical software environment across a range of systems. - -Nextflow has [excellent integration](https://www.nextflow.io/docs/latest/docker.html) with Docker, and beyond installing the two tools, not much else is required - nextflow will automatically fetch the [nfcore/mag](https://hub.docker.com/r/nfcore/mag/) image that we have created and is hosted at dockerhub at run time. - -To add docker support to your own config file, add the following: - -```nextflow -docker.enabled = true -process.container = "nfcore/mag" -``` - -Note that the dockerhub organisation name annoyingly can't have a hyphen, so is `nfcore` and not `nf-core`. - - -### Singularity image -Many HPC environments are not able to run Docker due to security issues. -[Singularity](http://singularity.lbl.gov/) is a tool designed to run on such HPC systems which is very similar to Docker. - -To specify singularity usage in your pipeline config file, add the following: - -```nextflow -<<<<<<< HEAD -singularity { - enabled = true -} -process { - container = "docker://$wf_container" -} -``` - -The variable `wf_container` is defined dynamically and automatically specifies the image tag if Nextflow is running with `-r`. - -If you intend to run the pipeline offline, nextflow will not be able to automatically download the singularity image for you. Instead, you'll have to do this yourself manually first, transfer the image file and then point to that. -======= -singularity.enabled = true -process.container = "shub://nf-core/mag" -``` - -If you intend to run the pipeline offline, nextflow will not be able to automatically download the singularity image for you. -Instead, you'll have to do this yourself manually first, transfer the image file and then point to that. ->>>>>>> TEMPLATE - -First, pull the image file where you have an internet connection: - -```bash -singularity pull --name nf-core-mag.simg shub://nf-core/mag -``` - -Then transfer this file and point the config file to the image: - -```nextflow -singularity.enabled = true -process.container = "/path/to/nf-core-mag.simg" -``` - - -### Conda -If you're not able to use Docker or Singularity, you can instead use conda to manage the software requirements. -To use conda in your own config file, add the following: - -```nextflow -process.conda = "$baseDir/environment.yml" -``` diff --git a/docs/configuration/local.md b/docs/configuration/local.md deleted file mode 100644 index e119e1af..00000000 --- a/docs/configuration/local.md +++ /dev/null @@ -1,42 +0,0 @@ -# nf-core/mag: Local Configuration - -If running the pipeline in a local environment, we highly recommend using either Docker or Singularity. - -## Docker -Docker is a great way to run nf-core/mag, as it manages all software installations and allows the pipeline to be run in an identical software environment across a range of systems. - -Nextflow has [excellent integration](https://www.nextflow.io/docs/latest/docker.html) with Docker, and beyond installing the two tools, not much else is required. The nf-core/mag profile comes with a configuration profile for docker, making it very easy to use. This also comes with the required presets to use the AWS iGenomes resource, meaning that if using common reference genomes you just specify the reference ID and it will be autaomtically downloaded from AWS S3. - -First, install docker on your system: [Docker Installation Instructions](https://docs.docker.com/engine/installation/) - -Then, simply run the analysis pipeline: -```bash -nextflow run nf-core/mag -profile docker --reads '' -``` - -Nextflow will recognise `nf-core/mag` and download the pipeline from GitHub. The `-profile docker` configuration lists the [hadrieng/mag](https://hub.docker.com/r/hadrieng/mag/) image that we have created and is hosted at dockerhub, and this is downloaded. - -For more information about how to work with reference genomes, see [`docs/configuration/reference_genomes.md`](docs/configuration/reference_genomes.md). - -### Pipeline versions -The public docker images are tagged with the same version numbers as the code, which you can use to ensure reproducibility. When running the pipeline, specify the pipeline version with `-r`, for example `-r v1.3`. This uses pipeline code and docker image from this tagged version. - - -## Singularity image -Many HPC environments are not able to run Docker due to security issues. [Singularity](http://singularity.lbl.gov/) is a tool designed to run on such HPC systems which is very similar to Docker. Even better, it can use create images directly from dockerhub. - -To use the singularity image for a single run, use `-with-singularity 'docker://hadrieng/mag'`. This will download the docker container from dockerhub and create a singularity image for you dynamically. - -If you intend to run the pipeline offline, nextflow will not be able to automatically download the singularity image for you. Instead, you'll have to do this yourself manually first, transfer the image file and then point to that. - -First, pull the image file where you have an internet connection: - -```bash -singularity pull --name mag.img docker://hadrieng/mag -``` - -Then transfer this file and run the pipeline with this path: - -```bash -nextflow run /path/to/nf-core/mag -with-singularity /path/to/mag.img -``` diff --git a/docs/installation.md b/docs/installation.md deleted file mode 100644 index 4e1a1baf..00000000 --- a/docs/installation.md +++ /dev/null @@ -1,115 +0,0 @@ -# nf-core/mag: Installation - -To start using the nf-core/mag pipeline, follow the steps below: - -1. [Install Nextflow](#1-install-nextflow) -2. [Install the pipeline](#2-install-the-pipeline) - * [Automatic](#21-automatic) - * [Offline](#22-offline) - * [Development](#23-development) -3. [Pipeline configuration](#3-pipeline-configuration) - * [Software deps: Docker and Singularity](#31-software-deps-docker-and-singularity) - * [Software deps: Bioconda](#32-software-deps-bioconda) - * [Configuration profiles](#33-configuration-profiles) -4. [Reference genomes](#4-reference-genomes) -5. [Appendices](#appendices) - * [Running on UPPMAX](#running-on-uppmax) - -## 1) Install NextFlow -Nextflow runs on most POSIX systems (Linux, Mac OSX etc). It can be installed by running the following commands: - -```bash -# Make sure that Java v8+ is installed: -java -version - -# Install Nextflow -curl -fsSL get.nextflow.io | bash - -# Add Nextflow binary to your PATH: -mv nextflow ~/bin/ -# OR system-wide installation: -# sudo mv nextflow /usr/local/bin -``` - -See [nextflow.io](https://www.nextflow.io/) for further instructions on how to install and configure Nextflow. - -## 2) Install the pipeline - -#### 2.1) Automatic -This pipeline itself needs no installation - NextFlow will automatically fetch it from GitHub if `nf-core/mag` is specified as the pipeline name. - -#### 2.2) Offline -The above method requires an internet connection so that Nextflow can download the pipeline files. If you're running on a system that has no internet connection, you'll need to download and transfer the pipeline files manually: - -```bash -wget https://github.com/nf-core/mag/archive/master.zip -mkdir -p ~/my-pipelines/nf-core/ -unzip master.zip -d ~/my-pipelines/nf-core/ -cd ~/my_data/ -nextflow run ~/my-pipelines/nf-core/mag-master -``` - -To stop nextflow from looking for updates online, you can tell it to run in offline mode by specifying the following environment variable in your ~/.bashrc file: - -```bash -export NXF_OFFLINE='TRUE' -``` - -#### 2.3) Development - -If you would like to make changes to the pipeline, it's best to make a fork on GitHub and then clone the files. Once cloned you can run the pipeline directly as above. - - -## 3) Pipeline configuration -By default, the pipeline runs with the `standard` configuration profile. This uses a number of sensible defaults for process requirements and is suitable for running on a simple (if powerful!) basic server. You can see this configuration in [`conf/base.config`](../conf/base.config). - -Be warned of two important points about this default configuration: - -1. The default profile uses the `local` executor - * All jobs are run in the login session. If you're using a simple server, this may be fine. If you're using a compute cluster, this is bad as all jobs will run on the head node. - * See the [nextflow docs](https://www.nextflow.io/docs/latest/executor.html) for information about running with other hardware backends. Most job scheduler systems are natively supported. -2. Nextflow will expect all software to be installed and available on the `PATH` - -#### 3.1) Software deps: Docker -First, install docker on your system: [Docker Installation Instructions](https://docs.docker.com/engine/installation/) - -Then, running the pipeline with the option `-profile standard,docker` tells Nextflow to enable Docker for this run. An image containing all of the software requirements will be automatically fetched and used from dockerhub (https://hub.docker.com/r/nfcore/mag). - -#### 3.1) Software deps: Singularity -If you're not able to use Docker then [Singularity](http://singularity.lbl.gov/) is a great alternative. -The process is very similar: running the pipeline with the option `-profile standard,singularity` tells Nextflow to enable singularity for this run. An image containing all of the software requirements will be automatically fetched and used from singularity hub. - -If running offline with Singularity, you'll need to download and transfer the Singularity image first: - -```bash -singularity pull --name nf-core-mag.simg shub://nf-core/mag -``` - -Once transferred, use `-with-singularity` and specify the path to the image file: - -```bash -nextflow run /path/to/nf-core-mag -with-singularity nf-core-mag.simg -``` - -Remember to pull updated versions of the singularity image if you update the pipeline. - - -#### 3.2) Software deps: conda -If you're not able to use Docker _or_ Singularity, you can instead use conda to manage the software requirements. -This is slower and less reproducible than the above, but is still better than having to install all requirements yourself! -The pipeline ships with a conda environment file and nextflow has built-in support for this. -To use it first ensure that you have conda installed (we recommend [miniconda](https://conda.io/miniconda.html)), then follow the same pattern as above and use the flag `-profile standard,conda` - - -## Appendices - -#### Running on UPPMAX -To run the pipeline on the [Swedish UPPMAX](https://www.uppmax.uu.se/) clusters (`rackham`, `irma`, `bianca` etc), use the command line flag `-profile uppmax`. This tells Nextflow to submit jobs using the SLURM job executor with Singularity for software dependencies. - -Note that you will need to specify your UPPMAX project ID when running a pipeline. To do this, use the command line flag `--project `. The pipeline will exit with an error message if you try to run it pipeline with the default UPPMAX config profile without a project. - -**Optional Extra:** To avoid having to specify your project every time you run Nextflow, you can add it to your personal Nextflow config file instead. Add this line to `~/.nextflow/config`: - -```nextflow -params.project = 'project_ID' // eg. b2017123 -``` diff --git a/docs/output.md b/docs/output.md index 6693d88c..5c21083b 100644 --- a/docs/output.md +++ b/docs/output.md @@ -7,16 +7,18 @@ This document describes the output produced by the pipeline. Most of the plots a The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using the following steps: -- [FastQC](#fastqc) - read quality control -- [fastp](#fastp) - read trimming -- [megahit](#megahit) - assembly -- [quast](#quast) - assembly quality report +- [Quality trimming and QC](#quality-trimming-and-qc) of input reads +- [Taxonomic classification](#taxonomic-classification) of trimmed reads +- [Assembly](#assembly) of trimmed reads +- [Binning](#binning) of assembled contigs - [MultiQC](#multiqc) - aggregate report, describing results of the whole pipeline -- [metabat](#metabat) - recovered draft genome bins from assembled metagenomes -- [checkm](#checkm) - genome bins quality control and merging of compatible bins -- [refinem](#refinem) - improving metagenome-assembled genome bins -## FastQC +## Quality trimming and QC + +These steps trim away the adapter sequences present in input reads, trims away bad quality bases and sicard reads that are too short. +It also removes sequencing controls, such as PhiX or the Lambda phage, as well as runs FastQC for visualising the general quality metrics of the sequencing runs before and after trimming. + +### FastQC [FastQC](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/) gives general quality metrics about your reads. It provides information about the quality score distribution across your reads, the per base sequence content (%T/A/G/C). You get information about adapter contamination and other overrepresented sequences. @@ -24,82 +26,149 @@ For further reading and documentation see the [FastQC help](http://www.bioinform > **NB:** The FastQC plots displayed in the MultiQC report shows both _untrimmed_ and _trimmed_ reads. -**Output directory: `results/fastqc`** +**Output directory: `results/QC_shortreads/fastqc`** - `sample_fastqc.html` - FastQC report, containing quality metrics for your untrimmed raw fastq files -- `zips/sample_fastqc.zip` - - zip file containing the FastQC report, tab-delimited data file and plot images -## fastp +### fastp [fastp](https://github.com/OpenGene/fastp) is a all-in-one fastq preprocessor for read/adapter trimming and quality control. It is used in this pipeline for trimming adapter sequences and discard low-quality reads. -**Output directory: `None`** +**Output directory: `results/QC_shortreads/fastp`** -- The trimmed reads are not included in the output +- The trimmed reads are not included in the output by default, but the `--keep_trimmed` will store the trimmed reads in this directory -## MultiQC +### remove_phix -[MultiQC](http://multiqc.info) is a visualisation tool that generates a single HTML report summarising all samples in your project. Most of the pipeline QC results are visualised in the report and further statistics are available in within the report data directory. +The pipeline uses bowtie2 to map the reads against PhiX and removes mapped reads. -The pipeline has special steps which allow the software versions used to be reported in the MultiQC output for future traceability. +**Output directory: `results/QC_shortreads/remove_phix`** -**Output directory: `results/multiqc`** +- Contains a brief log file indicating how many reads have been removed. -- `Project_multiqc_report.html` - - MultiQC report - a standalone HTML file that can be viewed in your web browser -- `Project_multiqc_data/` - - Directory containing parsed statistics from the different tools used in the pipeline +### keep_lambda + +The pipeline uses Nanolyse to map the reads against the Lambda phage and removes mapped reads. + +**Output directory: `results/QC_longreads/NanoLyse`** + +- Contains a brief log file indicating how many reads have been removed. + +### Filtlong and porechop + +The pipeline uses filtlong and porechop to perform quality control of the long reads that are eventually provided with the `--manifest` option. + +**Output directory: `results/QC_longreads/NanoPlot_${sample_name}`** + +- Contains various metrics and plots about the quality and length distribution of long reads, as well as an html report generated by nanoplot + +For more information about Nanoplot see the [online documentation](https://github.com/wdecoster/NanoPlot) + +## Taxonomic Classification + +### Kraken + +Kraken2 classifies reads using a k-mer based approach as well as assigns taxonomy using a Lowest Common Ancestor (LCA) algorithm. + +**Output directory: `results/Taxonomy/kraken2/${sample_name}`** + +- `*_kraken2.report`: Classification in the Kraken report format. See the [kraken manual](http://ccb.jhu.edu/software/kraken/MANUAL.html#sample-reports) for more details +- `taxonomy.krona.html`: an interactive pie chart produced by [KronaTools](https://github.com/marbl/Krona/wiki) + +### Centrifuge + +Centrifuge is commonly used for the classification of DNA sequences from microbial samples. It uses an indexing scheme based on the Burrows-Wheeler transform (BWT) and the Ferragina-Manzini (FM) index. + +More information on the [Centrifuge](https://ccb.jhu.edu/software/centrifuge/) website -For more information about how to use MultiQC reports, see http://multiqc.info +**Output directory: `results/Taxonomy/centrifuge/${sample_name}`** -## Megahit +- `report.txt`: Tab-delimited result file. See the [centrifuge manual](https://ccb.jhu.edu/software/centrifuge/manual.shtml#centrifuge-classification-output) for information about the fields +- `kreport.txt`: Classification in the Kraken report format. See the [kraken manual](http://ccb.jhu.edu/software/kraken/MANUAL.html#sample-reports) for more details +- `taxonomy.krona.html`: an interactive pie chart produced by [KronaTools](https://github.com/marbl/Krona/wiki) -[megahit](https://github.com/voutcn/megahit) is a single node assembler for large and complex metagenomics HTS reads. +### CAT -**Output directory: `results/megahit`** +[CAT](https://github.com/dutilh/CAT) is a toolkit for annotating contigs and bins from metagenome-assembled-genomes. The MAG pipeline uses CAT to assign taxonomy to the contigs from megahit and/or SPAdes, and to assign taxonomy to genome bins based on the taxnomy of the contigs -- `sample.fasta` - - metagenome assembly in fasta format +**Output directory: `results/Taxonomy/${assembler}`** -## Quast +- `*.ORF2LCA.txt`: Tab-delimited files containing the lineage of each contig +- `*.names.txt`: Taxonomy classification, with names of each lineage levels instead og taxids. +- `*.predicted_proteins.faa`: predicted protein sequences for each genome bins, in fasta format +- `*.predicted_proteins.gff`: predicted protein features for each genome bins, in gff format +- `*.log`: log files. +- `*.bin2classification.txt`: Taxonomy classification of the genome bins. + +## Assembly + +Trimmed (short) reads are assembled with both megahit and SPAdes. Hybrid assembly is only supported by SPAdes. + +### Megahit + +[megahit](https://github.com/voutcn/megahit) is a single node assembler for large and complex metagenomics short reads. + +**Output directory: `results/MEGAHIT`** + +- `${sample}.contigs.fasta`: metagenome assembly in fasta format +- `${sample}_QC/`: directory containing QUAST files + +### SPAdes + +[SPAdes](http://cab.spbu.ru/software/spades/) was originally a single genome assembler that later added support for assembling metagenomes. + +**Output directory: `results/SPAdes`** + +- `${sample}_contigs.fasta`: metagenome assembly in fasta format +- `${sample}_QC/`: directory containing QUAST files + +### SPAdesHybrid + +SPAdesHybrid is a part of the SPAdes software and is used when the user provides both long and short reads. + +**Output directory: `results/SPAdesHybrid`** + +- `${sample}_contigs.fasta`: metagenome assembly in fasta format +- `${sample}_QC/`: directory containing QUAST files + +### Quast [quast](http://cab.spbu.ru/software/quast/) is a tool that evaluates genome and metagenome assemblies by computing various metrics **output directory:** `None` -- The quast output is included in the multiqc report +- The quast output is included in the multiqc report, as well as in the assembly directories themselves + +## Binning -## Metabat +### Metabat -[metabat](https://bitbucket.org/berkeleylab/metabat) recovers genome bins (that is, contigs/scaffolds that all bolongs to a same organism) from metagenome assemblies. +[metabat](https://bitbucket.org/berkeleylab/metabat) recovers genome bins (that is, contigs/scaffolds that all belongs to a same organism) from metagenome assemblies. Additionally, Quast is run again on all the genome bins. -**output directory: `results/metabat`** +**output directory: `results/GenomeBinning/MetaBat2`** -- `sample.bam` - - reads mapped against the megahit assembly -- `bins/sample_X.fa` - - the putative genome bins retrieved by metabat +- `*.fa`: Genome bins retrieved from the different input assemblies -## checkm +### Busco -[checkm](https://github.com/Ecogenomics/CheckM) Assess the quality of microbial genomes recovered from isolates, single cells, and metagenomes. +Busco is a tool used to assess the completeness of a genome assembly. It is run on all the genome bins obtained by metabat2 -**output directory: `results/checkm`** +**output directory: `results/GenomeBinning/QC/`** -- `sample/` - - directory containing the improved bins -- `sample_stats` - - directory containing stats about completeness and contamination of the bins, as well as plots. +- `busco_summary.txt`: A summary tabke of the BUSCO results, with % of marker genes found -## refinem +## MultiQC + +[MultiQC](http://multiqc.info) is a visualisation tool that generates a single HTML report summarising all samples in your project. Most of the pipeline QC results are visualised in the report and further statistics are available in within the report data directory. -_(this process is optional)_ +The pipeline has special steps which allow the software versions used to be reported in the MultiQC output for future traceability. -[refinem](https://github.com/dparks1134/RefineM) is a companion tool to checkm, that refines and filter incongruent contigs from the genome bins. +**Output directory: `results/multiqc`** -**output directory: `results/refinem`** +- `Project_multiqc_report.html` + - MultiQC report - a standalone HTML file that can be viewed in your web browser +- `Project_multiqc_data/` + - Directory containing parsed statistics from the different tools used in the pipeline -- `sample.X.fa` - - the genome bins, improved by refinem +For more information about how to use MultiQC reports, see diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md deleted file mode 100644 index c7cbb086..00000000 --- a/docs/troubleshooting.md +++ /dev/null @@ -1,28 +0,0 @@ -# nf-core/mag: Troubleshooting - -## Input files not found - -If only no file, only one input file , or only read one and not read two is picked up then something is wrong with your input file declaration - -1. The path must be enclosed in quotes (`'` or `"`) -2. The path must have at least one `*` wildcard character. This is even if you are only running one paired end sample. -3. When using the pipeline with paired end data, the path must use `{1,2}` or `{R1,R2}` notation to specify read pairs. -4. If you are running Single end data make sure to specify `--singleEnd` - -If the pipeline can't find your files then you will get the following error - -``` -ERROR ~ Cannot find any reads matching: *{1,2}.fastq.gz -``` - -Note that if your sample name is "messy" then you have to be very particular with your glob specification. A file name like `L1-1-D-2h_S1_L002_R1_001.fastq.gz` can be difficult enough for a human to read. Specifying `*{1,2}*.gz` wont work give you what you want Whilst `*{R1,R2}*.gz` will. - - -## Data organization -The pipeline can't take a list of multiple input files - it takes a glob expression. If your input files are scattered in different paths then we recommend that you generate a directory with symlinked files. If running in paired end mode please make sure that your files are sensibly named so that they can be properly paired. See the previous point. - -## Extra resources and getting help -If you still have an issue with running the pipeline then feel free to contact us. -Have a look at the [pipeline website](https://github.com/nf-core/mag) to find out how. - -If you have problems that are related to Nextflow and not our pipeline then check out the [Nextflow gitter channel](https://gitter.im/nextflow-io/nextflow) or the [google group](https://groups.google.com/forum/#!forum/nextflow). diff --git a/docs/usage.md b/docs/usage.md index b713cadd..8b79e31e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -14,8 +14,11 @@ - [`none`](#none) - [`--reads`](#--reads) - [`--singleEnd`](#--singleend) + - [`--manifest`](#--manifest) - [Optional Arguments](#optional-arguments) - [Trimming Options](#trimming-options) + - [Trimming Options for long reads](#trimming-options-for-long-reads) + - [Taxonomic classification](#taxonomic-classification) - [Binning Options](#binning-options) - [Job Resources](#job-resources) - [Automatic resubmission](#automatic-resubmission) @@ -52,7 +55,7 @@ NXF_OPTS='-Xms1g -Xmx4g' The typical command for running the pipeline is as follows: ```bash -nextflow run nf-core/mag --reads '*_R{1,2}.fastq.gz' -profile standard,docker +nextflow run nf-core/mag --reads '*_R{1,2}.fastq.gz' -profile docker ``` This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. @@ -82,31 +85,28 @@ First, go to the [nf-core/mag releases page](https://github.com/nf-core/mag/rele This version number will be logged in reports when you run the pipeline, so that you'll know what you used when you look back in the future. -## Main Arguments +## Main arguments ### `-profile` -Use this parameter to choose a configuration profile. Profiles can give configuration presets for different compute environments. Note that multiple profiles can be loaded, for example: `-profile standard,docker` - the order of arguments is important! +Use this parameter to choose a configuration profile. Profiles can give configuration presets for different compute environments. Note that multiple profiles can be loaded, for example: `-profile docker` - the order of arguments is important! -- `standard` - - The default profile, used if `-profile` is not specified at all. - - Runs locally and expects all software to be installed and available on the `PATH`. +If `-profile` is not specified at all the pipeline will be run locally and expects all software to be installed and available on the `PATH`. + +- `awsbatch` + - A generic configuration profile to be used with AWS Batch. +- `conda` + - A generic configuration profile to be used with [conda](https://conda.io/docs/) + - Pulls most software from [Bioconda](https://bioconda.github.io/) - `docker` - A generic configuration profile to be used with [Docker](http://docker.com/) - Pulls software from dockerhub: [`nfcore/mag`](http://hub.docker.com/r/nfcore/mag/) - `singularity` - A generic configuration profile to be used with [Singularity](http://singularity.lbl.gov/) - - Pulls software from singularity-hub -- `conda` - - A generic configuration profile to be used with [conda](https://conda.io/docs/) - - Pulls most software from [Bioconda](https://bioconda.github.io/) -- `awsbatch` - - A generic configuration profile to be used with AWS Batch. -- `test` - - A profile with a complete configuration for automated testing + - Pulls software from DockerHub: [`nfcore/mag`](http://hub.docker.com/r/nfcore/mag/) +- `test`, `test_hybrid` + - Profiles with a complete configuration for automated testing - Includes links to test data so needs no other parameters -- `none` - - No configuration at all. Useful if you want to build your own config from scratch and want to avoid loading in the default `base` config profile (not recommended). ### `--reads` @@ -134,29 +134,86 @@ By default, the pipeline expects paired-end data. If you have single-end data, y It is not possible to run a mixture of single-end and paired-end files in one run. -## Optional arguments +### `--manifest` + +The pipeline has support for hybrid (with long and short reads) assembly, with the `--manifest` option. +The option take a tab-separated file with 4 headerless columns: Sample_Id, Long_Reads, Short_Reads_1, Short_Reads_2 +Only one file path per entry is allowed, and single-end short reads are not supported. + +## Trimming options + +### `--adapter_forward` + +Sequence of 3' adapter to remove in the forward reads + +### `--adapter_reverse` + +Sequence of 3' adapter to remove in the reverse reads + +### `--mean_quality` + +Mean qualified quality value for keeping read (default: 15) + +### `--trimming_quality` + +Trimming quality value for the sliding window (default: 15) + +### `--keep_phix` + +Keep reads similar to the Illumina internal standard PhiX genome (default: false) + +## Trimming options for long reads + +### `--skip_adapter_trimming` + +Skip removing adapter sequences from long reads + +### `--longreads_min_length` + +Discard any read which is shorter than this value (default: 1000) + +### `--longreads_keep_percent` -### Trimming options: +Keep this percent of bases (default: 90) - --adapter_forward Sequence of 3' adapter to remove in the forward reads - --adapter_reverse Sequence of 3' adapter to remove in the reverse reads - --mean_quality Mean qualified quality value for keeping read (default: 15) - --trimming_quality Trimming quality value for the sliding window (default: 15) +### `--longreads_length_weight` -### Binning options: +The higher the more important is read length when choosing the best reads (default: 10) - --refinem Enable bin refinement with refinem. - --refinem_db Path to refinem database - --no_checkm Disable bin QC and merging with checkm - --min_contig_size Minimum contig size to be considered for binning (default: 1500) - --delta_cont Maximum increase in contamination to merge compatible bins (default: 5) - --merged_cont Maximum total contamination to merge compatible bins (default: 15) - --delta_compl Minimum increase in completion to merge compatible bins (default: 10) - --abs_delta_cov Minimum coverage ratio to merge compatible bins (default: 0.75) - --delta_gc Maximum %GC difference to merge compatible bins (default: 3) - --ssu_evalue Evalue threshold to filter incongruent 16S (default 1e-6) +### `--keep_lambda` -## Job Resources +Keep reads similar to the ONT internal standard Escherichia virus Lambda genome (default: false) + +## Taxonomic classification + +Taxonomic classification is disabled by default. +You have to specify one of the options below to activate it. + +### `--centrifuge_db` + +Database for taxonomic binning with centrifuge (default: none). E.g. "" + +### `--kraken2_db` + +Database for taxonomic binning with kraken2 (default: none). E.g. "" + +### `--cat_db` + +Database for taxonomic classification of metagenome assembled genomes (default: none). E.g. "" +The zipped file needs to contain a folder named "\_taxonomy*" and "_CAT_database_" that hold the respective files. + +## Binning options + +### `--min_contig_size` + +Minimum contig size to be considered for binning (default: 1500) + +### `--busco_reference` + +Download path for BUSCO database, available databases are listed here: +(default: ) + +## Job resources ### Automatic resubmission @@ -164,7 +221,11 @@ Each step in the pipeline has a default set of requirements for number of CPUs, ### Custom resource requests -Wherever process-specific requirements are set in the pipeline, the default value can be changed by creating a custom config file. See the files in [`conf`](../conf) for examples. +Wherever process-specific requirements are set in the pipeline, the default value can be changed by creating a custom config file. See the files hosted at [`nf-core/configs`](https://github.com/nf-core/configs/tree/master/conf) for examples. + +If you are likely to be running `nf-core` pipelines regularly it may be a good idea to request that your custom config file is uploaded to the `nf-core/configs` git repository. Before you do this please can you test that the config file works with your pipeline of choice using the `-c` parameter (see definition below). You can then create a pull request to the `nf-core/configs` repository with the addition of your config file, associated documentation file (see examples in [`nf-core/configs/docs`](https://github.com/nf-core/configs/tree/master/docs)), and amending [`nfcore_custom.config`](https://github.com/nf-core/configs/blob/master/nfcore_custom.config) to include your custom profile. + +If you have any questions or issues please send us a message on [Slack](https://nf-core-invite.herokuapp.com/). ## AWS Batch specific parameters @@ -188,7 +249,7 @@ The output directory where the results will be saved. ### `--email` -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 speicfy this on the command line for every run. +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. ### `-name` @@ -212,16 +273,42 @@ Specify the path to a specific config file (this is a core NextFlow command). **NB:** Single hyphen (core Nextflow option) -Note - you can use this to override defaults. For example, you can specify a config file using `-c` that contains the following: +Note - you can use this to override pipeline defaults. + +### `--custom_config_version` + +Provide git commit id for custom Institutional configs hosted at `nf-core/configs`. This was implemented for reproducibility purposes. Default is set to `master`. + +```bash +## Download and use config file with following git commid id +--custom_config_version d52db660777c4bf36546ddb188ec530c3ada1b96 +``` + +### `--custom_config_base` + +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 the +`custom_config_base` option. For example: -```nextflow -process.$multiqc.module = [] +```bash +## Download and unzip the config files +cd /path/to/my/configs +wget https://github.com/nf-core/configs/archive/master.zip +unzip master.zip + +## Run the pipeline +cd /path/to/my/data +nextflow run /path/to/pipeline/ --custom_config_base /path/to/my/configs/configs-master/ ``` +> Note that the nf-core/tools helper package has a `download` command to download all required pipeline +> files + singularity containers + institutional configs in one go for you, to make this process easier. + ### `--max_memory` Use to set a top-limit for the default memory requirement for each process. -Should be a string in the format integer-unit. eg. `--max_memory '8.GB'`` +Should be a string in the format integer-unit. eg. `--max_memory '8.GB'` ### `--max_time` @@ -237,5 +324,10 @@ Should be a string in the format integer-unit. eg. `--max_cpus 1` Set to receive plain-text e-mails instead of HTML formatted. -### `--multiqc_config` +### `--monochrome_logs` + +Set to disable colourful command line output and live life in monochrome. + +### `--multiqc_config` + Specify a path to a custom MultiQC configuration file. diff --git a/environment.yml b/environment.yml index 1aa4a9f3..1ee43e81 100644 --- a/environment.yml +++ b/environment.yml @@ -1,25 +1,34 @@ +# You can use this file to create a conda environment for this pipeline: +# conda env create -f environment.yml name: nf-core-mag-1.0.0 channels: - - bioconda - conda-forge + - bioconda - defaults dependencies: - fastqc=0.11.8 - - multiqc=1.6 - - fastp=0.19.5 - - megahit=1.1.3 - - metabat2=2.12.1 + - multiqc=1.7 + - matplotlib=3.1.1 + - fastp=0.20.0 + - megahit=1.2.7 + - metabat2=2.13 - samtools=1.9 - - bowtie2=2.3.4.3 + - bowtie2=2.3.5 - quast=5.0.2 - - hmmer=3.2.1 - prodigal=2.6.3 - - pplacer=1.1.alpha19 - - diamond=0.9.22 - - python=2.7.15 - - r=3.5.1 - - biopython=1.72 - - krona=2.7 - - conda-forge::r-markdown=0.8 - - checkm-genome=1.0.12 - - refinem=0.0.24 + - diamond=0.9.24 # 0.9.25 conflicts with Busco over boost + - python=3.6.7 # 3.7.x conflicts with Nanolyse + - r=3.6 + - biopython=1.74 + - krona=2.7.1 + - conda-forge::r-markdown=1.0 + - r-ggplot2=3.2.0 + - busco=3.0.2 + - nanoplot=1.26.3 + - filtlong=0.2.0 + - porechop=0.2.3_seqan2.1.1 + - nanolyse=1.1.0 + - spades=3.13.1 + - centrifuge=1.0.4_beta + - cat=4.6 + - kraken2=2.0.8_beta diff --git a/main.nf b/main.nf index cc3dc02a..a1827b43 100644 --- a/main.nf +++ b/main.nf @@ -9,49 +9,74 @@ nf-core/mag Analysis Pipeline. Started 2018-05-22. https://github.com/nf-core/mag #### Authors Hadrien Gourlé HadrienG - hadriengourle.com> +Daniel Straub ---------------------------------------------------------------------------------------- */ def helpMessage() { + log.info nfcoreHeader() log.info""" - ========================================= - nf-core/mag v${workflow.manifest.version} - ========================================= Usage: The typical command for running the pipeline is as follows: nextflow run nf-core/mag --reads '*_R{1,2}.fastq.gz' -profile docker + nextflow run nf-core/mag --manifest 'manifest.tsv' -profile docker Mandatory arguments: --reads Path to input data (must be surrounded with quotes) -profile Configuration profile to use. Can use multiple (comma separated) - Available: standard, conda, docker, singularity, awsbatch, test + Available: conda, docker, singularity, awsbatch, test and more. + + Hybrid assembly: + --manifest Path to manifest file (must be surrounded with quotes), required for hybrid assembly with metaSPAdes + Has 4 headerless columns (tab separated): Sample_Id, Long_Reads, Short_Reads_1, Short_Reads_2 + Only one file path per entry allowed Options: - -name Name for the pipeline run. If not specified, Nextflow will automatically generate a random mnemonic. + --genome Name of iGenomes reference --singleEnd Specifies that the input is single end reads --outdir The output directory where the results will be saved --email 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 + --maxMultiqcEmailFileSize Theshold size for MultiQC report to be attached in notification email. If file generated by pipeline exceeds the threshold, it will not be attached (Default: 25MB) + -name Name for the pipeline run. If not specified, Nextflow will automatically generate a random mnemonic. - Trimming options: + Short read preprocessing: --adapter_forward Sequence of 3' adapter to remove in the forward reads --adapter_reverse Sequence of 3' adapter to remove in the reverse reads --mean_quality Mean qualified quality value for keeping read (default: 15) --trimming_quality Trimming quality value for the sliding window (default: 15) + --keep_phix Keep reads similar to the Illumina internal standard PhiX genome (default: false) + + Long read preprocessing: + --skip_adapter_trimming Skip removing adapter sequences from long reads + --longreads_min_length Discard any read which is shorter than this value (default: 1000) + --longreads_keep_percent Keep this percent of bases (default: 90) + --longreads_length_weight The higher the more important is read length when choosing the best reads (default: 10) + --keep_lambda Keep reads similar to the ONT internal standard Escherichia virus Lambda genome (default: false) + + Assembly: + --skip_spades Skip Illumina-only SPAdes assembly + --skip_spadeshybrid Skip SPAdes hybrid assembly (only available when using manifest input) + --skip_megahit Skip MEGAHIT assembly + --skip_quast Skip metaQUAST + + Taxonomy: + --centrifuge_db [path] Database for taxonomic binning with centrifuge (default: none). E.g. "ftp://ftp.ccb.jhu.edu/pub/infphilo/centrifuge/data/p_compressed+h+v.tar.gz" + --kraken2_db [path] Database for taxonomic binning with kraken2 (default: none). E.g. "ftp://ftp.ccb.jhu.edu/pub/data/kraken2_dbs/minikraken2_v2_8GB_201904_UPDATE.tgz" + --skip_krona Skip creating a krona plot for taxonomic binning + --cat_db [path] Database for taxonomic classification of metagenome assembled genomes (default: none). E.g. "tbb.bio.uu.nl/bastiaan/CAT_prepare/CAT_prepare_20190108.tar.gz" + The zipped file needs to contain a folder named "*taxonomy*" and "*CAT_database*" that hold the respective files. Binning options: - --refinem Enable bin refinement with refinem. - --refinem_db Path to refinem database - --no_checkm Disable bin QC and merging with checkm + --skip_binning Skip metagenome binning --min_contig_size Minimum contig size to be considered for binning (default: 1500) - --delta_cont Maximum increase in contamination to merge compatible bins (default: 5) - --merged_cont Maximum total contamination to merge compatible bins (default: 15) - --delta_compl Minimum increase in completion to merge compatible bins (default: 10) - --abs_delta_cov Minimum coverage ratio to merge compatible bins (default: 0.75) - --delta_gc Maximum %GC difference to merge compatible bins (default: 3) - --ssu_evalue Evalue threshold to filter incongruent 16S (default 1e-6) + + Bin quality check: + --skip_busco Disable bin QC with BUSCO (default: false) + --busco_reference Download path for BUSCO database, available databases are listed here: https://busco.ezlab.org/ + (default: https://busco-archive.ezlab.org/v3/datasets/bacteria_odb9.tar.gz) AWSBatch options: --awsqueue The AWSBatch JobQueue that needs to be set when running on AWSBatch @@ -63,7 +88,7 @@ def helpMessage() { * SET UP CONFIGURATION VARIABLES */ -// Show help emssage +// Show help message if (params.help){ helpMessage() exit 0 @@ -74,6 +99,8 @@ params.name = false params.multiqc_config = "$baseDir/conf/multiqc_config.yaml" params.email = false params.plaintext_email = false +params.manifest = false +params.busco_reference = "https://busco-archive.ezlab.org/v3/datasets/bacteria_odb9.tar.gz" ch_multiqc_config = Channel.fromPath(params.multiqc_config) ch_output_docs = Channel.fromPath("$baseDir/docs/output.md") @@ -97,93 +124,185 @@ if( !(workflow.runName ==~ /[a-z]+_[a-z]+/) ){ } /* - * trimming options + * short read preprocessing options */ params.adapter_forward = "AGATCGGAAGAGCACACGTCTGAACTCCAGTCA" params.adapter_reverse = "AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT" params.mean_quality = 15 params.trimming_quality = 15 +params.keep_phix = false +// params.phix_reference = "ftp://ftp.ncbi.nlm.nih.gov/genomes/genbank/viral/Enterobacteria_phage_phiX174_sensu_lato/all_assembly_versions/GCA_002596845.1_ASM259684v1/GCA_002596845.1_ASM259684v1_genomic.fna.gz" +params.phix_reference = "$baseDir/assets/data/GCA_002596845.1_ASM259684v1_genomic.fna.gz" /* * binning options */ -params.refinem = false -params.no_checkm = false +params.skip_binning = false +params.skip_busco = false params.min_contig_size = 1500 -params.delta_cont = 5 -params.merged_cont = 15 -params.delta_compl = 10 -params.abs_delta_cov = 0.75 -params.delta_gc = 3 -params.refinem_db = false -params.ssu_evalue = 1e-6 +/* + * assembly options + */ +params.skip_spades = false +params.skip_spadeshybrid = false +params.skip_megahit = false +params.skip_quast = false + +/* + * taxonomy options + */ +params.centrifuge_db = false +params.kraken2_db = false +params.skip_krona = false +params.cat_db = false + +/* + * long read preprocessing options + */ +params.skip_adapter_trimming = false +params.keep_lambda = false +params.longreads_min_length = 1000 +params.longreads_keep_percent = 90 +params.longreads_length_weight = 10 +// params.lambda_reference = "ftp://ftp.ncbi.nlm.nih.gov/genomes/genbank/viral/Escherichia_virus_Lambda/all_assembly_versions/GCA_000840245.1_ViralProj14204/GCA_000840245.1_ViralProj14204_genomic.fna.gz" +params.lambda_reference = "$baseDir/assets/data/GCA_000840245.1_ViralProj14204_genomic.fna.gz" + +// Stage config files +ch_multiqc_config = Channel.fromPath(params.multiqc_config) +ch_output_docs = Channel.fromPath("$baseDir/docs/output.md") /* * Create a channel for input read files */ - if(params.readPaths){ +if(!params.skip_busco){ + Channel + .fromPath( "${params.busco_reference}", checkIfExists: true ) + .set { file_busco_db } +} else { + file_busco_db = Channel.from() +} + +if(params.centrifuge_db){ + Channel + .fromPath( "${params.centrifuge_db}", checkIfExists: true ) + .set { file_centrifuge_db } +} else { + file_centrifuge_db = Channel.from() +} + +if(params.kraken2_db){ + Channel + .fromPath( "${params.kraken2_db}", checkIfExists: true ) + .set { file_kraken2_db } +} else { + file_kraken2_db = Channel.from() +} + +if(params.cat_db){ + Channel + .fromPath( "${params.cat_db}", checkIfExists: true ) + .set { file_cat_db } +} else { + file_cat_db = Channel.from() +} + +if(!params.keep_phix) { + Channel + .fromPath( "${params.phix_reference}", checkIfExists: true ) + .set { file_phix_db } +} + +def returnFile(it) { +// Return file if it exists + inputFile = file(it) + if (!file(inputFile).exists()) exit 1, "Missing file in TSV file: ${inputFile}, see --help for more information" + return inputFile +} + +if(params.manifest){ + manifestFile = file(params.manifest) + // extracts read files from TSV and distribute into channels + Channel + .from(manifestFile) + .ifEmpty {exit 1, log.info "Cannot find path file ${tsvFile}"} + .splitCsv(sep:'\t') + .map { row -> + def id = row[0] + def lr = returnFile( row[1] ) + def sr1 = returnFile( row[2] ) + def sr2 = returnFile( row[3] ) + [ id, lr, sr1, sr2 ] + } + .into { files_sr; files_all_raw } + // prepare input for fastqc + files_sr + .map { id, lr, sr1, sr2 -> [ id, [ sr1, sr2 ] ] } + .into { read_files_fastqc; read_files_fastp } + +} else if(params.readPaths){ if(params.singleEnd){ Channel .from(params.readPaths) .map { row -> [ row[0], [file(row[1][0])]] } .ifEmpty { exit 1, "params.readPaths was empty - no input files supplied" } .into { read_files_fastqc; read_files_fastp } + files_all_raw = Channel.from() } else { Channel .from(params.readPaths) .map { row -> [ row[0], [file(row[1][0]), file(row[1][1])]] } .ifEmpty { exit 1, "params.readPaths was empty - no input files supplied" } .into { read_files_fastqc; read_files_fastp } + files_all_raw = Channel.from() } } else { Channel .fromFilePairs( params.reads, size: params.singleEnd ? 1 : 2 ) .ifEmpty { exit 1, "Cannot find any reads matching: ${params.reads}\nNB: Path needs to be enclosed in quotes!\nNB: Path requires at least one * wildcard!\nIf this is single-end data, please specify --singleEnd on the command line." } .into { read_files_fastqc; read_files_fastp } + files_all_raw = Channel.from() } // Header log info -log.info """======================================================= - ,--./,-. - ___ __ __ __ ___ /,-._.--~\' - |\\ | |__ __ / ` / \\ |__) |__ } { - | \\| | \\__, \\__/ | \\ |___ \\`-._,-`-, - `._,._,\' - -nf-core/mag v${workflow.manifest.version} -=======================================================""" +log.info nfcoreHeader() def summary = [:] -summary['Pipeline Name'] = 'nf-core/mag' -summary['Pipeline Version'] = workflow.manifest.version -summary['Run Name'] = custom_runName ?: workflow.runName -summary['Reads'] = params.reads -summary['Data Type'] = params.singleEnd ? 'Single-End' : 'Paired-End' -summary['Max Memory'] = params.max_memory -summary['Max CPUs'] = params.max_cpus -summary['Max Time'] = params.max_time -summary['Container Engine'] = workflow.containerEngine -if(workflow.containerEngine) summary['Container'] = workflow.container -summary['Current home'] = "$HOME" -summary['Current user'] = "$USER" -summary['Current path'] = "$PWD" -summary['Working dir'] = workflow.workDir -summary['Output dir'] = params.outdir -summary['Script dir'] = workflow.projectDir -summary['Config Profile'] = workflow.profile +if(workflow.revision) summary['Pipeline Release'] = workflow.revision +summary['Run Name'] = custom_runName ?: workflow.runName +summary['Reads'] = params.reads +summary['Fasta Ref'] = params.fasta +summary['Data Type'] = params.singleEnd ? 'Single-End' : 'Paired-End' +if(params.centrifuge_db) summary['Centrifuge Db'] = params.centrifuge_db +if(params.kraken2_db) summary['Kraken2 Db'] = params.kraken2_db +if(!params.skip_busco) summary['Busco Reference'] = params.busco_reference +summary['Max Resources'] = "$params.max_memory memory, $params.max_cpus cpus, $params.max_time time per job" +if(workflow.containerEngine) summary['Container'] = "$workflow.containerEngine - $workflow.container" +summary['Output dir'] = params.outdir +summary['Launch dir'] = workflow.launchDir +summary['Working dir'] = workflow.workDir +summary['Script dir'] = workflow.projectDir +summary['User'] = workflow.userName if(workflow.profile == 'awsbatch'){ - summary['AWS Region'] = params.awsregion - summary['AWS Queue'] = params.awsqueue + summary['AWS Region'] = params.awsregion + summary['AWS Queue'] = params.awsqueue +} +summary['Config Profile'] = workflow.profile +if(params.config_profile_description) summary['Config Description'] = params.config_profile_description +if(params.config_profile_contact) summary['Config Contact'] = params.config_profile_contact +if(params.config_profile_url) summary['Config URL'] = params.config_profile_url +if(params.email) { + summary['E-mail Address'] = params.email + summary['MultiQC maxsize'] = params.maxMultiqcEmailFileSize } -if(params.email) summary['E-mail Address'] = params.email -log.info summary.collect { k,v -> "${k.padRight(15)}: $v" }.join("\n") -log.info "=========================================" +log.info summary.collect { k,v -> "${k.padRight(18)}: $v" }.join("\n") +log.info "\033[2m----------------------------------------------------\033[0m" +// Check the hostnames against configured profiles +checkHostname() def create_workflow_summary(summary) { - def yaml_file = workDir.resolve('workflow_summary_mqc.yaml') yaml_file.text = """ id: 'nf-core-mag-summary' @@ -204,10 +323,15 @@ ${summary.collect { k,v -> "
$k
${v ?: ' + if (filename.indexOf(".csv") > 0) filename + else null + } output: file 'software_versions_mqc.yaml' into software_versions_yaml + file "software_versions.csv" script: """ @@ -218,24 +342,141 @@ process get_software_versions { fastp -v 2> v_fastp.txt megahit --version > v_megahit.txt metabat2 -h 2> v_metabat.txt || true + NanoPlot --version > v_nanoplot.txt + filtlong --version > v_filtlong.txt + porechop --version > v_porechop.txt + NanoLyse --version > v_nanolyse.txt + spades.py --version > v_spades.txt + run_BUSCO.py --version > v_busco.txt + centrifuge --version > v_centrifuge.txt + kraken2 -v > v_kraken2.txt + CAT -v > v_cat.txt + quast -v > v_quast.txt - # fake checkm data setRoot so we can read checkm version number - chd=\$(readlink -f checkm_data) - printf "\$chd\\n\$chd\\n" | checkm data setRoot - - checkm -h > v_checkm.txt - refinem -h > v_refinem.txt scrape_software_versions.py > software_versions_mqc.yaml """ } +/* + * Trim adapter sequences on long read nanopore files + */ +if (!params.skip_adapter_trimming) { + process porechop { + tag "$id" + + input: + set id, file(lr), sr1, sr2 from files_all_raw + + output: + set id, file("${id}_porechop.fastq"), sr1, sr2 into files_porechop + set id, file(lr), val("raw") into files_nanoplot_raw + + script: + """ + porechop -i ${lr} -t "${task.cpus}" -o ${id}_porechop.fastq + """ + } +} else { + files_all_raw + .into{ files_porechop; pre_files_nanoplot_raw } + pre_files_nanoplot_raw + .map { id, lr, sr1, sr2 -> [ id, lr, "raw" ] } + .set { files_nanoplot_raw } +} + +/* + * Remove reads mapping to the lambda genome. + * TODO: add lambda phage to igenomes.config? + */ +if (!params.keep_lambda) { + Channel + .fromPath( "${params.lambda_reference}", checkIfExists: true ) + .set { file_nanolyse_db } + process nanolyse { + tag "$id" + + publishDir "${params.outdir}", mode: 'copy', + saveAs: {filename -> filename.indexOf(".fastq.gz") == -1 ? "QC_longreads/NanoLyse/$filename" : null} + + input: + set id, file(lr), file(sr1), file(sr2), file(nanolyse_db) from files_porechop.combine(file_nanolyse_db) + + output: + set id, file("${id}_nanolyse.fastq.gz"), file(sr1), file(sr2) into files_nanolyse + file("${id}_nanolyse_log.txt") + + script: + """ + cat ${lr} | NanoLyse --reference $nanolyse_db | gzip > ${id}_nanolyse.fastq.gz + + echo "NanoLyse reference: $params.lambda_reference" >${id}_nanolyse_log.txt + cat ${lr} | echo "total reads before NanoLyse: \$((`wc -l`/4))" >>${id}_nanolyse_log.txt + zcat ${id}_nanolyse.fastq.gz | echo "total reads after NanoLyse: \$((`wc -l`/4))" >>${id}_nanolyse_log.txt + """ + } +} else { + files_porechop + .set{ files_nanolyse } +} + + +/* + * Quality filter long reads focus on length instead of quality to improve assembly size + */ +process filtlong { + tag "$id" + + input: + set id, file(lr), file(sr1), file(sr2) from files_nanolyse + + output: + set id, file("${id}_lr_filtlong.fastq.gz") into files_lr_filtered + set id, file("${id}_lr_filtlong.fastq.gz"), val('filtered') into files_nanoplot_filtered + + script: + """ + filtlong \ + -1 ${sr1} \ + -2 ${sr2} \ + --min_length ${params.longreads_min_length} \ + --keep_percent ${params.longreads_keep_percent} \ + --trim \ + --length_weight ${params.longreads_length_weight} \ + ${lr} | gzip > ${id}_lr_filtlong.fastq.gz + """ +} + + +/* + * Quality check for nanopore reads and Quality/Length Plots + */ +process nanoplot { + tag "$id" + publishDir "${params.outdir}/QC_longreads/NanoPlot_${id}", mode: 'copy' + + input: + set id, file(lr), type from files_nanoplot_raw.mix(files_nanoplot_filtered) + + output: + file '*.png' + file '*.html' + file '*.txt' + + script: + """ + NanoPlot -t "${task.cpus}" -p ${type}_ --title ${id}_${type} -c darkblue --fastq ${lr} + """ +} + + /* * STEP 1 - Read trimming and pre/post qc */ process fastqc_raw { tag "$name" - publishDir "${params.outdir}/fastqc", mode: 'copy' + publishDir "${params.outdir}/", mode: 'copy', + saveAs: {filename -> filename.indexOf(".zip") == -1 ? "QC_shortreads/fastqc/$filename" : null} input: set val(name), file(reads) from read_files_fastqc @@ -252,7 +493,8 @@ process fastqc_raw { process fastp { tag "$name" - // publishDir "${params.outdir}/fastp", mode: 'copy' + publishDir "${params.outdir}/", mode: 'copy', + saveAs: {filename -> filename.indexOf(".fastq.gz") == -1 ? "QC_shortreads/fastp/$name/$filename" : null} input: set val(name), file(reads) from read_files_fastp @@ -262,32 +504,81 @@ process fastp { val trim_qual from params.trimming_quality output: - set val(name), file("${name}_trimmed*.fastq.gz") into (trimmed_reads_megahit, trimmed_reads_metabat, trimmed_reads_fastqc) + set val(name), file("${name}_trimmed*.fastq.gz") into trimmed_reads + file("fastp.*") script: - if ( !params.singleEnd ) { + def pe_input = params.singleEnd ? '' : "-I \"${reads[1]}\"" + def pe_output1 = params.singleEnd ? "-o \"${name}_trimmed.fastq.gz\"" : "-o \"${name}_trimmed_R1.fastq.gz\"" + def pe_output2 = params.singleEnd ? '' : "-O \"${name}_trimmed_R2.fastq.gz\"" + """ + fastp -w "${task.cpus}" -q "${qual}" --cut_by_quality5 \ + --cut_by_quality3 --cut_mean_quality "${trim_qual}"\ + --adapter_sequence=${adapter} --adapter_sequence_r2=${adapter_reverse} \ + -i "${reads[0]}" $pe_input $pe_output1 $pe_output2 + """ +} + +/* + * Remove PhiX contamination from Illumina reads + * TODO: PhiX into/from iGenomes.conf? + */ +if(!params.keep_phix) { + process phix_download_db { + tag "${genome}" + + input: + file(genome) from file_phix_db + + output: + set file(genome), file("ref*") into phix_db + + script: """ - fastp -w "${task.cpus}" -q "${qual}" --cut_by_quality5 \ - --cut_by_quality3 --cut_mean_quality "${trim_qual}"\ - --adapter_sequence=${adapter} --adapter_sequence_r2=${adapter_reverse} \ - -i "${reads[0]}" -I "${reads[1]}" \ - -o ${name}_trimmed_R1.fastq.gz -O ${name}_trimmed_R2.fastq.gz + bowtie2-build --threads "${task.cpus}" "${genome}" ref """ } - else { - """ - fastp -w "${task.cpus}" -q "${qual}" --cut_by_quality5 \ - --cut_by_quality3 --cut_mean_quality "${trim_qual}"\ - --adapter_sequence="${adapter}" --adapter_sequence_r2="${adapter_reverse}" \ - -i ${reads} -o "${name}_trimmed.fastq.gz" - """ + + process remove_phix { + tag "$name" + + publishDir "${params.outdir}", mode: 'copy', + saveAs: {filename -> filename.indexOf(".fastq.gz") == -1 ? "QC_shortreads/remove_phix/$filename" : null} + + input: + set val(name), file(reads), file(genome), file(db) from trimmed_reads.combine(phix_db) + + output: + set val(name), file("*.fastq.gz") into (trimmed_reads_megahit, trimmed_reads_metabat, trimmed_reads_fastqc, trimmed_sr_spadeshybrid, trimmed_reads_spades, trimmed_reads_centrifuge, trimmed_reads_kraken2, trimmed_reads_bowtie2) + file("${name}_remove_phix_log.txt") + + script: + if ( !params.singleEnd ) { + """ + bowtie2 -p "${task.cpus}" -x ref -1 "${reads[0]}" -2 "${reads[1]}" --un-conc-gz ${name}_unmapped_%.fastq.gz + echo "Bowtie2 reference: ${genome}" >${name}_remove_phix_log.txt + zcat ${reads[0]} | echo "Read pairs before removal: \$((`wc -l`/4))" >>${name}_remove_phix_log.txt + zcat ${name}_unmapped_1.fastq.gz | echo "Read pairs after removal: \$((`wc -l`/4))" >>${name}_remove_phix_log.txt + """ + } else { + """ + bowtie2 -p "${task.cpus}" -x ref -U ${reads} --un-gz ${name}_unmapped.fastq.gz + echo "Bowtie2 reference: ${genome}" >${name}_remove_phix_log.txt + zcat ${reads[0]} | echo "Reads before removal: \$((`wc -l`/4))" >>${name}_remove_phix_log.txt + zcat ${name}_unmapped.fastq.gz | echo "Reads after removal: \$((`wc -l`/4))" >>${name}_remove_phix_log.txt + """ + } + } +} else { + trimmed_reads.into {trimmed_reads_megahit; trimmed_reads_metabat; trimmed_reads_fastqc; trimmed_sr_spadeshybrid; trimmed_reads_spades; trimmed_reads_centrifuge} } process fastqc_trimmed { tag "$name" - publishDir "${params.outdir}/fastqc", mode: 'copy' + publishDir "${params.outdir}/", mode: 'copy', + saveAs: {filename -> filename.indexOf(".zip") == -1 ? "QC_shortreads/fastqc/$filename" : null} input: set val(name), file(reads) from trimmed_reads_fastqc @@ -301,265 +592,566 @@ process fastqc_trimmed { """ } +/* + * STEP - Taxonomic information + */ + +process centrifuge_db_preparation { + input: + file(db) from file_centrifuge_db + + output: + set val("${db.toString().replace(".tar.gz", "")}"), file("*.cf") into centrifuge_database + + script: + """ + tar -xf "${db}" + """ +} + +trimmed_reads_centrifuge + .combine(centrifuge_database) + .set { centrifuge_input } + +process centrifuge { + tag "${name}-${db_name}" + publishDir "${params.outdir}/Taxonomy/centrifuge/${name}", mode: 'copy', + saveAs: {filename -> filename.indexOf(".krona") == -1 ? filename : null} + + input: + set val(name), file(reads), val(db_name), file(db) from centrifuge_input + + output: + set val("centrifuge"), val(name), file("results.krona") into centrifuge_to_krona + file("report.txt") + file("kreport.txt") + + script: + def input = params.singleEnd ? "-U \"${reads}\"" : "-1 \"${reads[0]}\" -2 \"${reads[1]}\"" + """ + centrifuge -x "${db_name}" \ + -p "${task.cpus}" \ + --report-file report.txt \ + -S results.txt \ + $input + centrifuge-kreport -x "${db_name}" results.txt > kreport.txt + cat results.txt | cut -f 1,3 > results.krona + """ +} + +process kraken2_db_preparation { + input: + file(db) from file_kraken2_db + + output: + set val("${db.baseName}"), file("${db.baseName}/*.k2d") into kraken2_database + + script: + """ + tar -xf "${db}" + """ +} + +trimmed_reads_kraken2 + .combine(kraken2_database) + .set { kraken2_input } + +process kraken2 { + tag "${name}-${db_name}" + publishDir "${params.outdir}/Taxonomy/kraken2/${name}", mode: 'copy', + saveAs: {filename -> filename.indexOf(".krona") == -1 ? filename : null} + + input: + set val(name), file(reads), val(db_name), file("database/*") from kraken2_input + + output: + set val("kraken2"), val(name), file("results.krona") into kraken2_to_krona + file("kraken2_report.txt") + + script: + def input = params.singleEnd ? "\"${reads}\"" : "--paired \"${reads[0]}\" \"${reads[1]}\"" + """ + kraken2 \ + --report-zero-counts \ + --threads "${task.cpus}" \ + --db database \ + --fastq-input \ + --report kraken2_report.txt \ + $input \ + > kraken2.kraken + cat kraken2.kraken | cut -f 2,3 > results.krona + """ +} + +process krona_db { + output: + file("taxonomy/taxonomy.tab") into file_krona_db + + when: + ( params.centrifuge_db || params.kraken2_db ) && !params.skip_krona + + script: + """ + ktUpdateTaxonomy.sh taxonomy + """ +} + +centrifuge_to_krona + .mix(kraken2_to_krona) + .combine(file_krona_db) + .set { krona_input } + +process krona { + tag "${classifier}-${name}" + publishDir "${params.outdir}/Taxonomy/${classifier}/${name}", mode: 'copy' + + input: + set val(classifier), val(name), file(report), file("taxonomy/taxonomy.tab") from krona_input + + output: + file("*.html") + + script: + """ + ktImportTaxonomy "$report" -tax taxonomy + """ +} + /* * STEP 2 - Assembly */ process megahit { tag "$name" - publishDir "${params.outdir}/", mode: 'copy' + publishDir "${params.outdir}/", mode: 'copy', + saveAs: {filename -> filename.indexOf(".fastq.gz") == -1 ? "Assembly/$filename" : null} input: set val(name), file(reads) from trimmed_reads_megahit output: - set val(name), file("megahit/${name}.contigs.fa") into (assembly_quast, assembly_metabat, assembly_refinem) + set val("MEGAHIT"), val("$name"), file("MEGAHIT/${name}.contigs.fa") into assembly_megahit_to_quast + set val("MEGAHIT"), val("$name"), file("MEGAHIT/${name}.contigs.fa") into assembly_megahit_to_metabat + file("MEGAHIT/*.log") + + when: + !params.skip_megahit script: - if ( !params.singleEnd ) { + def input = params.singleEnd ? "-r \"${reads}\"" : "-1 \"${reads[0]}\" -2 \"${reads[1]}\"" """ - megahit -t "${task.cpus}" -1 "${reads[0]}" -2 "${reads[1]}" -o megahit \ - --out-prefix "${name}" + megahit -t "${task.cpus}" $input -o MEGAHIT --out-prefix "${name}" """ - } - else { +} + + +/* + * metaSpades hybrid Assembly + */ + + files_lr_filtered + .combine(trimmed_sr_spadeshybrid, by: 0) + .set { files_pre_spadeshybrid } + +process spadeshybrid { + tag "$id" + publishDir "${params.outdir}/", mode: 'copy', pattern: "${id}*", + saveAs: {filename -> filename.indexOf(".fastq.gz") == -1 ? "Assembly/SPAdesHybrid/$filename" : null} + + input: + set id, file(lr), file(sr) from files_pre_spadeshybrid + + output: + set id, val("SPAdesHybrid"), file("${id}_graph.gfa") into assembly_graph_spadeshybrid + set val("SPAdesHybrid"), val("$id"), file("${id}_scaffolds.fasta") into assembly_spadeshybrid_to_quast + set val("SPAdesHybrid"), val("$id"), file("${id}_scaffolds.fasta") into assembly_spadeshybrid_to_metabat + file("${id}_contigs.fasta") + file("${id}_log.txt") + + when: + params.manifest && !params.singleEnd && !params.skip_spadeshybrid + + script: + def maxmem = "${task.memory.toString().replaceAll(/[\sGB]/,'')}" """ - megahit -t "${task.cpus}" -r ${reads} -o megahit --out-prefix "${name}" + metaspades.py \ + --threads "${task.cpus}" \ + --memory "$maxmem" \ + --pe1-1 ${sr[0]} \ + --pe1-2 ${sr[1]} \ + --nanopore ${lr} \ + -o spades + mv spades/assembly_graph_with_scaffolds.gfa ${id}_graph.gfa + mv spades/scaffolds.fasta ${id}_scaffolds.fasta + mv spades/contigs.fasta ${id}_contigs.fasta + mv spades/spades.log ${id}_log.txt + """ +} + + +process spades { + tag "$id" + publishDir "${params.outdir}/", mode: 'copy', pattern: "${id}*", + saveAs: {filename -> filename.indexOf(".fastq.gz") == -1 ? "Assembly/SPAdes/$filename" : null} + + input: + set id, file(sr) from trimmed_reads_spades + + output: + set id, val("SPAdes"), file("${id}_graph.gfa") into assembly_graph_spades + set val("SPAdes"), val("$id"), file("${id}_scaffolds.fasta") into assembly_spades_to_quast + set val("SPAdes"), val("$id"), file("${id}_scaffolds.fasta") into assembly_spades_to_metabat + file("${id}_contigs.fasta") + file("${id}_log.txt") + + when: + !params.singleEnd && !params.skip_spades + + script: + def maxmem = "${task.memory.toString().replaceAll(/[\sGB]/,'')}" + """ + metaspades.py \ + --threads "${task.cpus}" \ + --memory "$maxmem" \ + --pe1-1 ${sr[0]} \ + --pe1-2 ${sr[1]} \ + -o spades + mv spades/assembly_graph_with_scaffolds.gfa ${id}_graph.gfa + mv spades/scaffolds.fasta ${id}_scaffolds.fasta + mv spades/contigs.fasta ${id}_contigs.fasta + mv spades/spades.log ${id}_log.txt """ - } } process quast { - tag "$name" - // publishDir "${params.outdir}/quast/$name", mode: 'copy' + tag "$assembler-$sample" + publishDir "${params.outdir}/Assembly/$assembler", mode: 'copy' input: - set val(name), file(assembly) from assembly_quast + set val(assembler), val(sample), file(assembly) from assembly_spades_to_quast.mix(assembly_megahit_to_quast).mix(assembly_spadeshybrid_to_quast) output: - file("quast_${name}/*") into quast_results + file("${sample}_QC/*") into quast_results + + when: + !params.skip_quast script: """ - quast.py --threads "${task.cpus}" -l "${name}" "${assembly}" -o "quast_${name}" + metaquast.py --threads "${task.cpus}" --rna-finding --max-ref-number 0 -l "${assembler}-${sample}" "${assembly}" -o "${sample}_QC" """ } +bowtie2_input = Channel.empty() + +assembly_all_to_metabat = assembly_spades_to_metabat.mix(assembly_megahit_to_metabat,assembly_spadeshybrid_to_metabat) + +(assembly_all_to_metabat, assembly_all_to_metabat_copy) = assembly_all_to_metabat.into(2) + +bowtie2_input = assembly_all_to_metabat + .combine(trimmed_reads_bowtie2) + +(bowtie2_input, bowtie2_input_copy) = bowtie2_input.into(2) /* * STEP 3 - Binning */ +process bowtie2 { + tag "$assembler-$sample" + + input: + set val(assembler), val(sample), file(assembly), val(sampleToMap), file(reads) from bowtie2_input + + output: + set val(assembler), val(sample), file("${assembler}-${sample}-${sampleToMap}.bam"), file("${assembler}-${sample}-${sampleToMap}.bam.bai") into assembly_mapping_for_metabat + + when: + !params.skip_binning + + script: + def name = "${assembler}-${sample}-${sampleToMap}" + def input = params.singleEnd ? "-U \"${reads}\"" : "-1 \"${reads[0]}\" -2 \"${reads[1]}\"" + """ + bowtie2-build --threads "${task.cpus}" "${assembly}" ref + bowtie2 -p "${task.cpus}" -x ref $input | \ + samtools view -@ "${task.cpus}" -bS | \ + samtools sort -@ "${task.cpus}" -o "${name}.bam" + samtools index "${name}.bam" + """ +} + +assembly_mapping_for_metabat = assembly_mapping_for_metabat.groupTuple(by:[0,1]).join(assembly_all_to_metabat_copy) + +assembly_mapping_for_metabat = assembly_mapping_for_metabat.dump(tag:'assembly_mapping_for_metabat') + process metabat { - tag "$name" - publishDir "${params.outdir}/metabat", mode: 'copy' + tag "$assembler-$sample" + publishDir "${params.outdir}/", mode: 'copy', + saveAs: {filename -> (filename.indexOf(".bam") == -1 && filename.indexOf(".fastq.gz") == -1) ? "GenomeBinning/$filename" : null} input: - set val(_name), file(assembly) from assembly_metabat - set val(name), file(reads) from trimmed_reads_metabat + set val(assembler), val(sample), file(bam), file(index), val(sampleCopy), file(assembly) from assembly_mapping_for_metabat val(min_size) from params.min_contig_size output: - set val(name), file("bins/") into metabat_bins - set val(name), file("${name}.bam") into (mapped_reads_checkm, mapped_reads_refinem) + set val(assembler), val(sample), file("MetaBAT2/*") into metabat_bins mode flatten + set val(assembler), val(sample), file("MetaBAT2/*") into metabat_bins_for_cat + set val(assembler), val(sample), file("MetaBAT2/*") into metabat_bins_quast_bins + + when: + !params.skip_binning script: - if ( !params.singleEnd ) { - """ - bowtie2-build --threads "${task.cpus}" "${assembly}" ref - bowtie2 -p "${task.cpus}" -x ref -1 "${reads[0]}" -2 "${reads[1]}" | \ - samtools view -@ "${task.cpus}" -bS | \ - samtools sort -@ "${task.cpus}" -o "${name}.bam" - samtools index "${name}.bam" - jgi_summarize_bam_contig_depths --outputDepth depth.txt "${name}.bam" - metabat2 -t "${task.cpus}" -i "${assembly}" -a depth.txt -o "bins/${name}" -m ${min_size} - """ - } - else { + def name = "${assembler}-${sample}" """ - bowtie2-build --threads "${task.cpus}" "${assembly}" ref - bowtie2 -p "${task.cpus}" -x ref -U ${reads} | \ - samtools view -@ "${task.cpus}" -bS | \ - samtools sort -@ "${task.cpus}" -o "${name}.bam" - samtools index "${name}.bam" - jgi_summarize_bam_contig_depths --outputDepth depth.txt "${name}.bam" - metabat2 -t "${task.cpus}" -i "${assembly}" -a depth.txt -o bins/"${name}" -m ${min_size} - """ - } + jgi_summarize_bam_contig_depths --outputDepth depth.txt ${bam} + metabat2 -t "${task.cpus}" -i "${assembly}" -a depth.txt -o "MetaBAT2/${name}" -m ${min_size} + #if bin folder is empty + if [ -z \"\$(ls -A MetaBAT2)\" ]; then + cp ${assembly} MetaBAT2/${assembler}-${assembly} + fi + """ } -process checkm_download_db { - output: - file("checkm_data") into checkm_db +process busco_download_db { + tag "${database.baseName}" - when: - params.no_checkm == false + input: + file(database) from file_busco_db + + output: + set val("${database.toString().replace(".tar.gz", "")}"), file("buscodb/*") into busco_db script: """ - mkdir -p checkm_data && \ - cd checkm_data && \ - curl -L -O https://data.ace.uq.edu.au/public/CheckM_databases/checkm_data_2015_01_16.tar.gz && \ - tar xzf checkm_data_2015_01_16.tar.gz && \ - cd .. && \ - printf "checkm_data\ncheckm_data\n" | checkm data setRoot + mkdir buscodb + tar -xf ${database} -C buscodb """ } -process checkm { - tag "$name" - publishDir "${params.outdir}/checkm", mode: 'copy' +metabat_bins + .combine(busco_db) + .set { metabat_db_busco } + +/* + * BUSCO: Quantitative measures for the assessment of genome assembly + */ +process busco { + tag "${assembly}" + publishDir "${params.outdir}/GenomeBinning/QC/BUSCO/", mode: 'copy' input: - set val(name), file(bins) from metabat_bins - set val(_name), file(bam) from mapped_reads_checkm - val(delta_cont) from params.delta_cont - val(merged_cont) from params.merged_cont - val(delta_compl) from params.delta_compl - val(abs_delta_cov) from params.abs_delta_cov - val(delta_gc) from params.delta_gc - file("checkm_data") from checkm_db + set val(assembler), val(sample), file(assembly), val(db_name), file(db) from metabat_db_busco output: - file("${name}_stats/lineage") into checkm_merge_results - file("${name}_stats/plots") into checkm_merge_plots - file("${name}_stats/qa.txt") into checkm_merge_qa - set val(name), file("${name}/") into checkm_merge_bins + file("short_summary_${assembly}.txt") into (busco_summary_to_multiqc, busco_summary_to_plot) + val("$assembler-$sample") into busco_assembler_sample_to_plot + file("${assembly}_busco_log.txt") + file("${assembly}_buscos.faa") + file("${assembly}_buscos.fna") - when: - params.no_checkm == false + script: + if( workflow.profile.toString().indexOf("conda") == -1) { + """ + cp -r /opt/conda/pkgs/augustus*/config augustus_config/ + export AUGUSTUS_CONFIG_PATH=augustus_config + + run_BUSCO.py \ + --in ${assembly} \ + --lineage_path $db_name \ + --cpu "${task.cpus}" \ + --blast_single_core \ + --mode genome \ + --out ${assembly} \ + >${assembly}_busco_log.txt + cp run_${assembly}/short_summary_${assembly}.txt short_summary_${assembly}.txt + + for f in run_${assembly}/single_copy_busco_sequences/*faa; do + [ -e "\$f" ] && cat run_${assembly}/single_copy_busco_sequences/*faa >${assembly}_buscos.faa || touch ${assembly}_buscos.faa + break + done + for f in run_${assembly}/single_copy_busco_sequences/*fna; do + [ -e "\$f" ] && cat run_${assembly}/single_copy_busco_sequences/*fna >${assembly}_buscos.fna || touch ${assembly}_buscos.fna + break + done + """ + } else { + """ + run_BUSCO.py \ + --in ${assembly} \ + --lineage_path $db_name \ + --cpu "${task.cpus}" \ + --blast_single_core \ + --mode genome \ + --out ${assembly} \ + >${assembly}_busco_log.txt + cp run_${assembly}/short_summary_${assembly}.txt short_summary_${assembly}.txt + + for f in run_${assembly}/single_copy_busco_sequences/*faa; do + [ -e "\$f" ] && cat run_${assembly}/single_copy_busco_sequences/*faa >${assembly}_buscos.faa || touch ${assembly}_buscos.faa + break + done + for f in run_${assembly}/single_copy_busco_sequences/*fna; do + [ -e "\$f" ] && cat run_${assembly}/single_copy_busco_sequences/*fna >${assembly}_buscos.fna || touch ${assembly}_buscos.fna + break + done + """ + } +} + + +process busco_plot { + publishDir "${params.outdir}/GenomeBinning/QC/", mode: 'copy' + + input: + file(summaries) from busco_summary_to_plot.collect() + val(assemblersample) from busco_assembler_sample_to_plot.collect() + + output: + file("*busco_figure.png") + file("BUSCO/*busco_figure.R") + file("BUSCO/*busco_summary.txt") + file("busco_summary.txt") into busco_summary script: + def assemblersampleunique = assemblersample.unique() """ - # re-run setRoot in case checkm has forgotten where the databases are located - chd=\$(readlink -f checkm_data) - printf "\$chd\\n\$chd\\n" | checkm data setRoot + #for each assembler and sample: + assemblersample=\$(echo \"$assemblersampleunique\" | sed 's/[][]//g') + IFS=', ' read -r -a assemblersamples <<< \"\$assemblersample\" - mkdir -p stats - checkm lineage_wf -t "${task.cpus}" -x fa "${bins}" stats/lineage > stats/qa.txt - checkm bin_qa_plot -x fa stats/lineage "${bins}" stats/plots + mkdir BUSCO - samtools index "${bam}" - checkm coverage -t "${task.cpus}" -x fa "${bins}" stats/coverage.txt "${bam}" - checkm profile stats/coverage.txt > stats/profile.txt - checkm tree_qa stats/lineage > stats/tree_qa.txt + for name in \"\${assemblersamples[@]}\"; do + mkdir \${name} + cp short_summary_\${name}* \${name}/ + generate_plot.py --working_directory \${name} + + cp \${name}/busco_figure.png \${name}-busco_figure.png + cp \${name}/busco_figure.R \${name}-busco_figure.R - samtools view "${bam}" | awk '{if (NR <=1000) print length(\$10)}' > stats/read_length.txt + summary_busco.py \${name}/short_summary_*.txt >BUSCO/\${name}-busco_summary.txt + done - checkm taxon_set domain Bacteria bacteria.ms - checkm merge -t "${task.cpus}" -x fa --delta_cont "${delta_cont}" --merged_cont "${merged_cont}" \ - bacteria.ms "${bins}" stats/merger/ + cp *-busco_figure.R BUSCO/ - mkdir -p ${name} - mkdir -p ${name}_stats - merge_bins.py --delta_cont "${delta_cont}" --merged_cont "${merged_cont}" \ - --delta_compl "${delta_compl}" --abs_delta_cov "${abs_delta_cov}" --delta_gc "${delta_gc}" \ - --profile stats/profile.txt --tree stats/tree_qa.txt \ - --length stats/read_length.txt \ - --merger stats/merger/merger.tsv "${bins}" "${name}" - checkm lineage_wf -t "${task.cpus}" -x fa "${name}" ${name}_stats/lineage > ${name}_stats/qa.txt - checkm bin_qa_plot -x fa ${name}_stats/lineage "${name}" ${name}_stats/plots + summary_busco.py short_summary_*.txt >busco_summary.txt """ } +process quast_bins { + tag "$assembler-$sample" + publishDir "${params.outdir}/GenomeBinning/QC/", mode: 'copy' + + input: + set val(assembler), val(sample), file(assembly) from metabat_bins_quast_bins -process refinem_download_db { - publishDir "${params.outdir}/db" output: - file("refinem_databases/") into refinem_db + file("QUAST/*") + file("QUAST/*-quast_summary.tsv") into quast_bin_summaries when: - params.refinem == true && params.refinem_db ==false && params.no_checkm == false + !params.skip_quast script: """ - wget https://storage.googleapis.com/mag_refinem_db/refinem_databases.tar.gz - tar xzf refinem_databases.tar.gz + ASSEMBLIES=\$(echo \"$assembly\" | sed 's/[][]//g') + IFS=', ' read -r -a assemblies <<< \"\$ASSEMBLIES\" + + for assembly in \"\${assemblies[@]}\"; do + metaquast.py --threads "${task.cpus}" --max-ref-number 0 --rna-finding --gene-finding -l "\${assembly}" "\${assembly}" -o "QUAST/\${assembly}" + if ! [ -f "QUAST/${assembler}-${sample}-quast_summary.tsv" ]; then + cp "QUAST/\${assembly}/transposed_report.tsv" "QUAST/${assembler}-${sample}-quast_summary.tsv" + else + tail -n +2 "QUAST/\${assembly}/transposed_report.tsv" >> "QUAST/${assembler}-${sample}-quast_summary.tsv" + fi + done """ } -if (params.refinem_db) { - refinem_db = file(params.refinem_db) +process merge_quast_and_busco { + publishDir "${params.outdir}/GenomeBinning/QC/", mode: 'copy' + + input: + file(quast_bin_sum) from quast_bin_summaries.collect() + file(busco_sum) from busco_summary + + output: + file("quast_and_busco_summary.tsv") + file("quast_summary.tsv") + + script: + """ + QUAST_BIN=\$(echo \"$quast_bin_sum\" | sed 's/[][]//g') + IFS=', ' read -r -a quast_bin <<< \"\$QUAST_BIN\" + + for quast_file in \"\${quast_bin[@]}\"; do + if ! [ -f "quast_summary.tsv" ]; then + cp "\${quast_file}" "quast_summary.tsv" + else + tail -n +2 "\${quast_file}" >> "quast_summary.tsv" + fi + done + + combine_tables.py $busco_sum quast_summary.tsv >quast_and_busco_summary.tsv + """ } -mapped_reads_refinem.into {mapped_reads_refinem_1; mapped_reads_refinem_2} -assembly_refinem.into {assembly_refinem_1; assembly_refinem_2} -process refinem_pass_1 { - tag "${name}" +/* + * CAT: Bin Annotation Tool (BAT) are pipelines for the taxonomic classification of long DNA sequences and metagenome assembled genomes (MAGs/bins) + */ +process cat_db { + tag "${database.baseName}" input: - set val(name), file(bins) from checkm_merge_bins - set val(_name), file(bam) from mapped_reads_refinem_1 - set val(__name), file(assembly) from assembly_refinem_1 - file(refinem_db) from refinem_db + file(database) from file_cat_db output: - set val(name), file("bins_pass_1") into refinem_bins_pass_1 - - when: - params.refinem == true && params.no_checkm == false + set val("${database.toString().replace(".tar.gz", "")}"), file("database/*"), file("taxonomy/*") into cat_db script: """ - # filter by GC / tetra - samtools index "${bam}" - refinem scaffold_stats -x fa -c "${task.cpus}" "${assembly}" "${bins}" refinem "${bam}" - refinem outliers --no_plots refinem/scaffold_stats.tsv refinem - refinem filter_bins -x fa "${bins}" refinem/outliers.tsv new_bins_tmp_1 - # filter by taxonomic assignment - refinem call_genes -x fa -c "${task.cpus}" new_bins_tmp_1 gene_calls - refinem taxon_profile -c "${task.cpus}" gene_calls refinem/scaffold_stats.tsv \ - "${refinem_db}/gtdb_r86.dmnd" "${refinem_db}/gtdb_r86_taxonomy.2018-09-25.tsv" \ - taxon_profile - refinem taxon_filter -c "${task.cpus}" taxon_profile taxon_filter.tsv - refinem filter_bins -x fa new_bins_tmp_1 taxon_filter.tsv bins_pass_1 - rename 's/.filtered.filtered.fa/.fa/' bins_pass_1/*.filtered.filtered.fa + mkdir catDB + tar -xf ${database} -C catDB + mv `find catDB/ -type d -name "*taxonomy*"` taxonomy/ + mv `find catDB/ -type d -name "*CAT_database*"` database/ """ } -process refinem_pass_2 { - tag "${name}" - publishDir "${params.outdir}/", mode: 'copy' +metabat_bins_for_cat + .combine(cat_db) + .set { cat_input } + +process cat { + tag "${assembler}-${sample}-${db_name}" + publishDir "${params.outdir}/Taxonomy/${assembler}", mode: 'copy', + saveAs: {filename -> + if (filename.indexOf(".names.txt") > 0) filename + else "raw/$filename" + } input: - set val(name), file(bins) from refinem_bins_pass_1 - set val(_name), file(bam) from mapped_reads_refinem_2 - set val(__name), file(assembly) from assembly_refinem_2 - file(refinem_db) from refinem_db - val(ssu_evalue) from params.ssu_evalue + set val(assembler), val(sample), file("bins/*"), val(db_name), file("database/*"), file("taxonomy/*") from cat_input output: - set val(name), file("refinem") into refinem_bins - - when: - params.refinem == true && params.no_checkm == false + file("*.ORF2LCA.txt") + file("*.names.txt") + file("*.predicted_proteins.faa") + file("*.predicted_proteins.gff") + file("*.log") + file("*.bin2classification.txt") script: """ - samtools index "${bam}" - # filter incongruent 16S - # first we need to re-run taxon profile on the new bin dir - refinem scaffold_stats -x fa -c "${task.cpus}" "${assembly}" "${bins}" stats "${bam}" - refinem call_genes -x fa -c "${task.cpus}" "${bins}" gene_calls - refinem taxon_profile -c "${task.cpus}" gene_calls stats/scaffold_stats.tsv \ - "${refinem_db}/gtdb_r86.dmnd" "${refinem_db}/gtdb_r86_taxonomy.2018-09-25.tsv" \ - taxon_profile - # then we can identify incongruent ssu - refinem ssu_erroneous -x fa -c "${task.cpus}" "${bins}" taxon_profile \ - "${refinem_db}/gtdb_r80_ssu" "${refinem_db}/gtdb_r86_taxonomy.2018-09-25.tsv" ssu - # TODO inspect top-hit and give e-value threshold to filter a contig - filter_ssu.py --evalue ${ssu_evalue} ssu/ssu_erroneous.tsv ssu/ssu_filtered.tsv - refinem filter_bins -x fa "${bins}" ssu/ssu_filtered.tsv refinem - rename 's/.filtered.fa/.fa/' refinem/*.filtered.fa + CAT bins -b "bins/" -d database/ -t taxonomy/ -n "${task.cpus}" -s .fa --top 6 -o "${assembler}-${sample}" --I_know_what_Im_doing + CAT add_names -i "${assembler}-${sample}.ORF2LCA.txt" -o "${assembler}-${sample}.ORF2LCA.names.txt" -t taxonomy/ + CAT add_names -i "${assembler}-${sample}.bin2classification.txt" -o "${assembler}-${sample}.bin2classification.names.txt" -t taxonomy/ """ } -// TODO next releases: -// good bins channels (from checkm or refinem) + annotation -// multiqc modules for checkm/refinem - /* * STEP 4 - MultiQC */ @@ -572,10 +1164,12 @@ process multiqc { file (fastqc_raw:'fastqc/*') from fastqc_results.collect().ifEmpty([]) file (fastqc_trimmed:'fastqc/*') from fastqc_results_trimmed.collect().ifEmpty([]) file ('quast*/*') from quast_results.collect() + file ('short_summary_*.txt') from busco_summary_to_multiqc.collect() output: file "*multiqc_report.html" into multiqc_report file "*_data" + file "multiqc_plots" script: rtitle = custom_runName ? "--title \"$custom_runName\"" : '' @@ -590,7 +1184,7 @@ process multiqc { * STEP 5 - Output Description HTML */ process output_documentation { - publishDir "${params.outdir}/Documentation", mode: 'copy' + publishDir "${params.outdir}/pipeline_info", mode: 'copy' input: file output_docs from ch_output_docs @@ -634,10 +1228,25 @@ workflow.onComplete { if(workflow.repository) email_fields['summary']['Pipeline repository Git URL'] = workflow.repository if(workflow.commitId) email_fields['summary']['Pipeline repository Git Commit'] = workflow.commitId if(workflow.revision) email_fields['summary']['Pipeline Git branch/tag'] = workflow.revision + if(workflow.container) email_fields['summary']['Docker image'] = workflow.container email_fields['summary']['Nextflow Version'] = workflow.nextflow.version email_fields['summary']['Nextflow Build'] = workflow.nextflow.build email_fields['summary']['Nextflow Compile Timestamp'] = workflow.nextflow.timestamp + // On success try attach the multiqc report + def mqc_report = null + try { + if (workflow.success) { + mqc_report = multiqc_report.getVal() + if (mqc_report.getClass() == ArrayList){ + log.warn "[nf-core/mag] Found multiple reports from process 'multiqc', will use only one" + mqc_report = mqc_report[0] + } + } + } catch (all) { + log.warn "[nf-core/mag] Could not attach MultiQC report to summary email" + } + // Render the TXT template def engine = new groovy.text.GStringTemplateEngine() def tf = new File("$baseDir/assets/email_template.txt") @@ -650,7 +1259,7 @@ workflow.onComplete { def email_html = html_template.toString() // Render the sendmail template - def smail_fields = [ email: params.email, subject: subject, email_txt: email_txt, email_html: email_html, baseDir: "$baseDir" ] + def smail_fields = [ email: params.email, subject: subject, email_txt: email_txt, email_html: email_html, baseDir: "$baseDir", mqcFile: mqc_report, mqcMaxSize: params.maxMultiqcEmailFileSize.toBytes() ] def sf = new File("$baseDir/assets/sendmail_template.txt") def sendmail_template = engine.createTemplate(sf).make(smail_fields) def sendmail_html = sendmail_template.toString() @@ -670,7 +1279,7 @@ workflow.onComplete { } // Write summary e-mail HTML to a file - def output_d = new File( "${params.outdir}/Documentation/" ) + def output_d = new File( "${params.outdir}/pipeline_info/" ) if( !output_d.exists() ) { output_d.mkdirs() } @@ -679,6 +1288,67 @@ workflow.onComplete { def output_tf = new File( output_d, "pipeline_report.txt" ) output_tf.withWriter { w -> w << email_txt } - log.info "[nf-core/mag] Pipeline Complete" + c_reset = params.monochrome_logs ? '' : "\033[0m"; + c_purple = params.monochrome_logs ? '' : "\033[0;35m"; + c_green = params.monochrome_logs ? '' : "\033[0;32m"; + c_red = params.monochrome_logs ? '' : "\033[0;31m"; + + if (workflow.stats.ignoredCountFmt > 0 && workflow.success) { + log.info "${c_purple}Warning, pipeline completed, but with errored process(es) ${c_reset}" + log.info "${c_red}Number of ignored errored process(es) : ${workflow.stats.ignoredCountFmt} ${c_reset}" + log.info "${c_green}Number of successfully ran process(es) : ${workflow.stats.succeedCountFmt} ${c_reset}" + } + + if(workflow.success){ + log.info "${c_purple}[nf-core/mag]${c_green} Pipeline completed successfully${c_reset}" + } else { + checkHostname() + log.info "${c_purple}[nf-core/mag]${c_red} Pipeline completed with errors${c_reset}" + } + +} + + +def nfcoreHeader(){ + // Log colors ANSI codes + c_reset = params.monochrome_logs ? '' : "\033[0m"; + c_dim = params.monochrome_logs ? '' : "\033[2m"; + c_black = params.monochrome_logs ? '' : "\033[0;30m"; + c_green = params.monochrome_logs ? '' : "\033[0;32m"; + c_yellow = params.monochrome_logs ? '' : "\033[0;33m"; + c_blue = params.monochrome_logs ? '' : "\033[0;34m"; + c_purple = params.monochrome_logs ? '' : "\033[0;35m"; + c_cyan = params.monochrome_logs ? '' : "\033[0;36m"; + c_white = params.monochrome_logs ? '' : "\033[0;37m"; + + return """ ${c_dim}----------------------------------------------------${c_reset} + ${c_green},--.${c_black}/${c_green},-.${c_reset} + ${c_blue} ___ __ __ __ ___ ${c_green}/,-._.--~\'${c_reset} + ${c_blue} |\\ | |__ __ / ` / \\ |__) |__ ${c_yellow}} {${c_reset} + ${c_blue} | \\| | \\__, \\__/ | \\ |___ ${c_green}\\`-._,-`-,${c_reset} + ${c_green}`._,._,\'${c_reset} + ${c_purple} nf-core/mag v${workflow.manifest.version}${c_reset} + ${c_dim}----------------------------------------------------${c_reset} + """.stripIndent() +} +def checkHostname(){ + def c_reset = params.monochrome_logs ? '' : "\033[0m" + def c_white = params.monochrome_logs ? '' : "\033[0;37m" + def c_red = params.monochrome_logs ? '' : "\033[1;91m" + def c_yellow_bold = params.monochrome_logs ? '' : "\033[1;93m" + if(params.hostnames){ + def hostname = "hostname".execute().text.trim() + params.hostnames.each { prof, hnames -> + hnames.each { hname -> + if(hostname.contains(hname) && !workflow.profile.contains(prof)){ + log.error "====================================================\n" + + " ${c_red}WARNING!${c_reset} You are running with `-profile $workflow.profile`\n" + + " but your machine hostname is ${c_white}'$hostname'${c_reset}\n" + + " ${c_yellow_bold}It's highly recommended that you use `-profile $prof${c_reset}`\n" + + "============================================================" + } + } + } + } } diff --git a/nextflow.config b/nextflow.config index dfb5a76b..44c543b0 100644 --- a/nextflow.config +++ b/nextflow.config @@ -3,52 +3,63 @@ * nf-core/mag Nextflow config file * ------------------------------------------------- * Default config options for all environments. - * Cluster-specific config options should be saved - * in the conf folder and imported under a profile - * name here. */ // Global default params, used in configs params { - pipelineVersion = "1.0.0" // Pipeline version - container = 'nfcore/mag:1.0.0' // Container slug. Stable releases should specify release tag! - help = false + // Workflow flags reads = "data/*{_R1,_R2}.fastq.gz" singleEnd = false outdir = './results' + + // Boilerplate options + name = false + multiqc_config = "$baseDir/assets/multiqc_config.yaml" + email = false + maxMultiqcEmailFileSize = 25.MB + plaintext_email = false + monochrome_logs = false + help = false igenomes_base = "./iGenomes" - clusterOptions = false tracedir = "${params.outdir}/pipeline_info" + awsqueue = false + awsregion = 'eu-west-1' + igenomesIgnore = false custom_config_version = 'master' + custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" + hostnames = false + config_profile_description = false + config_profile_contact = false + config_profile_url = false } -// load base config by default for all pipelines +// Container slug. Stable releases should specify release tag! +// Developmental code should specify :dev +process.container = 'nfcore/mag:1.0.0' + +// Load base.config by default for all pipelines includeConfig 'conf/base.config' // Load nf-core custom profiles from different Institutions -includeConfig "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}/nfcore_custom.config" +try { + includeConfig "${params.custom_config_base}/nfcore_custom.config" +} catch (Exception e) { + System.err.println("WARNING: Could not load nf-core/config profiles: ${params.custom_config_base}/nfcore_custom.config") +} profiles { conda { process.conda = "$baseDir/environment.yml" } - docker { - docker.enabled = true - process.container = params.container - } - singularity { - singularity.enabled = true - } - awsbatch { - includeConfig 'conf/awsbatch.config' - includeConfig 'conf/igenomes.config' - } - test { - includeConfig 'conf/test.config' - } - none { - // Don't load any config (for use with custom home configs) - } + debug { process.beforeScript = 'echo $HOSTNAME' } + docker { docker.enabled = true } + singularity { singularity.enabled = true } + test { includeConfig 'conf/test.config' } + test_hybrid { includeConfig 'conf/test_hybrid.config' } +} +// Load igenomes.config if required +if(!params.igenomesIgnore){ + includeConfig 'conf/igenomes.config' } // Capture exit codes from upstream processes when piping @@ -56,28 +67,29 @@ process.shell = ['/bin/bash', '-euo', 'pipefail'] timeline { enabled = true - file = "${params.tracedir}/pipeline_info/nf-core/mag_timeline.html" + file = "${params.tracedir}/execution_timeline.html" } report { enabled = true - file = "${params.tracedir}/pipeline_info/nf-core/mag_report.html" + file = "${params.tracedir}/execution_report.html" } trace { enabled = true - file = "${params.tracedir}/pipeline_info/nf-core/mag_trace.txt" + file = "${params.tracedir}/execution_trace.txt" } dag { enabled = true - file = "${params.tracedir}/pipeline_info/nf-core/mag_dag.svg" + file = "${params.tracedir}/pipeline_dag.svg" } manifest { name = 'nf-core/mag' - description = 'Assembly, binning and annotation of metagenomes' + author = 'No author provided' homePage = 'https://github.com/nf-core/mag' - version = '1.0.0' + description = 'Assembly, binning and annotation of metagenomes' mainScript = 'main.nf' - nextflowVersion = '>=0.32.0' + nextflowVersion = '>=19.01.0' + version = '1.0.0' } // Function to ensure that resource requirements don't go beyond