From 5d9b5874a813b31555681dda9a67eb8ce03216c5 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 29 Jul 2021 13:58:41 +0100 Subject: [PATCH 001/238] Bump versions to 2.3dev --- CHANGELOG.md | 6 ++++++ nextflow.config | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd587a8..f0f13c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unpublished Version / DEV] + +### Enhancements & fixes + +### Parameters + ## [[2.2](https://github.com/nf-core/rnaseq/releases/tag/2.2)] - 2021-07-29 ### Enhancements & fixes diff --git a/nextflow.config b/nextflow.config index 0c310aab..d40f1842 100644 --- a/nextflow.config +++ b/nextflow.config @@ -233,7 +233,7 @@ manifest { description = 'Assembly and intrahost/low-frequency variant calling for viral samples' mainScript = 'main.nf' nextflowVersion = '!>=21.04.0' - version = '2.2' + version = '2.3dev' } // Function to ensure that resource requirements don't go beyond From 16975abbfcc1ef66fa6dc759936deb72bb7198ec Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Tue, 14 Dec 2021 16:43:16 +0000 Subject: [PATCH 002/238] Template update for nf-core/tools version 2.2 --- .gitattributes | 2 + .github/CONTRIBUTING.md | 38 ++------ .github/ISSUE_TEMPLATE/bug_report.md | 63 ------------- .github/ISSUE_TEMPLATE/bug_report.yml | 52 ++++++++++ .github/ISSUE_TEMPLATE/config.yml | 1 - .github/ISSUE_TEMPLATE/feature_request.md | 32 ------- .github/ISSUE_TEMPLATE/feature_request.yml | 11 +++ .github/workflows/awsfulltest.yml | 8 +- .github/workflows/awstest.yml | 10 +- .github/workflows/ci.yml | 23 +++-- .github/workflows/linting_comment.yml | 1 + CHANGELOG.md | 2 +- CITATIONS.md | 2 +- README.md | 13 ++- assets/multiqc_config.yaml | 2 +- assets/nf-core-viralrecon_logo.png | Bin 18781 -> 0 bytes assets/nf-core-viralrecon_logo_light.png | Bin 0 -> 11416 bytes assets/sendmail_template.txt | 4 +- bin/scrape_software_versions.py | 36 ------- conf/base.config | 3 + conf/modules.config | 55 ++++++----- conf/test.config | 4 +- docs/images/nf-core-viralrecon_logo.png | Bin 35628 -> 0 bytes docs/images/nf-core-viralrecon_logo_dark.png | Bin 0 -> 76656 bytes docs/images/nf-core-viralrecon_logo_light.png | Bin 0 -> 76539 bytes docs/output.md | 2 +- docs/usage.md | 36 ------- lib/NfcoreSchema.groovy | 26 +++-- lib/NfcoreTemplate.groovy | 30 ++---- lib/Utils.groovy | 7 -- lib/WorkflowMain.groovy | 6 +- modules.json | 9 +- modules/local/functions.nf | 68 ------------- modules/local/get_software_versions.nf | 33 ------- modules/local/samplesheet_check.nf | 24 ++--- .../custom/dumpsoftwareversions/main.nf | 21 +++++ .../custom/dumpsoftwareversions/meta.yml | 34 +++++++ .../templates/dumpsoftwareversions.py | 89 ++++++++++++++++++ modules/nf-core/modules/fastqc/functions.nf | 68 ------------- modules/nf-core/modules/fastqc/main.nf | 39 ++++---- modules/nf-core/modules/fastqc/meta.yml | 7 +- modules/nf-core/modules/multiqc/functions.nf | 68 ------------- modules/nf-core/modules/multiqc/main.nf | 31 +++--- modules/nf-core/modules/multiqc/meta.yml | 7 +- nextflow.config | 33 +++---- nextflow_schema.json | 29 ------ subworkflows/local/input_check.nf | 8 +- workflows/viralrecon.nf | 44 +++------ 48 files changed, 410 insertions(+), 671 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 assets/nf-core-viralrecon_logo.png create mode 100644 assets/nf-core-viralrecon_logo_light.png delete mode 100755 bin/scrape_software_versions.py delete mode 100644 docs/images/nf-core-viralrecon_logo.png create mode 100644 docs/images/nf-core-viralrecon_logo_dark.png create mode 100644 docs/images/nf-core-viralrecon_logo_light.png delete mode 100644 modules/local/functions.nf delete mode 100644 modules/local/get_software_versions.nf create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/main.nf create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py delete mode 100644 modules/nf-core/modules/fastqc/functions.nf delete mode 100644 modules/nf-core/modules/multiqc/functions.nf diff --git a/.gitattributes b/.gitattributes index 7fe55006..050bb120 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ *.config linguist-language=nextflow +modules/nf-core/** linguist-generated +subworkflows/nf-core/** linguist-generated diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d1b57a92..b4bff9b6 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -68,16 +68,13 @@ If you wish to contribute a new step, please use the following coding standards: 1. Define the corresponding input channel into your new process from the expected previous process channel 2. Write the process block (see below). 3. Define the output channel if needed (see below). -4. Add any new flags/options to `nextflow.config` with a default (see below). -5. Add any new flags/options to `nextflow_schema.json` with help text (with `nf-core schema build`). -6. Add any new flags/options to the help message (for integer/text parameters, print to help the corresponding `nextflow.config` parameter). -7. Add sanity checks for all relevant parameters. -8. Add any new software to the `scrape_software_versions.py` script in `bin/` and the version command to the `scrape_software_versions` process in `main.nf`. -9. Do local tests that the new code works properly and as expected. -10. Add a new test command in `.github/workflow/ci.yml`. -11. If applicable add a [MultiQC](https://https://multiqc.info/) module. -12. Update MultiQC config `assets/multiqc_config.yaml` so relevant suffixes, name clean up, General Statistics Table column order, and module figures are in the right order. -13. Optional: Add any descriptions of MultiQC report sections and output files to `docs/output.md`. +4. Add any new parameters to `nextflow.config` with a default (see below). +5. Add any new parameters to `nextflow_schema.json` with help text (via the `nf-core schema build` tool). +6. Add sanity checks and validation for all relevant parameters. +7. Perform local tests to validate that the new code works as expected. +8. If applicable, add a new test command in `.github/workflow/ci.yml`. +9. Update MultiQC config `assets/multiqc_config.yaml` so relevant suffixes, file name clean up and module plots are in the appropriate order. If applicable, add a [MultiQC](https://https://multiqc.info/) module. +10. Add a description of the output files and if relevant any appropriate images from the MultiQC report to `docs/output.md`. ### Default values @@ -102,27 +99,6 @@ Please use the following naming schemes, to make it easy to understand what is g If you are using a new feature from core Nextflow, you may bump the minimum required version of nextflow in the pipeline with: `nf-core bump-version --nextflow . [min-nf-version]` -### Software version reporting - -If you add a new tool to the pipeline, please ensure you add the information of the tool to the `get_software_version` process. - -Add to the script block of the process, something like the following: - -```bash - --version &> v_.txt 2>&1 || true -``` - -or - -```bash - --help | head -n 1 &> v_.txt 2>&1 || true -``` - -You then need to edit the script `bin/scrape_software_versions.py` to: - -1. Add a Python regex for your tool's `--version` output (as in stored in the `v_.txt` file), to ensure the version is reported as a `v` and the version number e.g. `v2.1.1` -2. Add a HTML entry to the `OrderedDict` for formatting in MultiQC. - ### Images and figures For overview images and other documents we follow the nf-core [style guidelines and examples](https://nf-co.re/developers/design_guidelines). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 429da641..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -name: Bug report -about: Report something that is broken or incorrect -labels: bug ---- - - - -## Check Documentation - -I have checked the following places for your error: - -- [ ] [nf-core website: troubleshooting](https://nf-co.re/usage/troubleshooting) -- [ ] [nf-core/viralrecon pipeline documentation](https://nf-co.re/viralrecon/usage) - -## Description of the bug - - - -## Steps to reproduce - -Steps to reproduce the behaviour: - -1. Command line: -2. See error: - -## Expected behaviour - - - -## Log files - -Have you provided the following extra information/files: - -- [ ] The command used to run the pipeline -- [ ] The `.nextflow.log` file - -## System - -- Hardware: -- Executor: -- OS: -- Version - -## Nextflow Installation - -- Version: - -## Container engine - -- Engine: -- version: - -## Additional context - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..f332a0b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,52 @@ + +name: Bug report +description: Report something that is broken or incorrect +labels: bug +body: + + - type: markdown + attributes: + value: | + Before you post this issue, please check the documentation: + + - [nf-core website: troubleshooting](https://nf-co.re/usage/troubleshooting) + - [nf-core/viralrecon pipeline documentation](https://nf-co.re/viralrecon/usage) + + - type: textarea + id: description + attributes: + label: Description of the bug + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + id: command_used + attributes: + label: Command used and terminal output + description: Steps to reproduce the behaviour. Please paste the command you used to launch the pipeline and the output from your terminal. + render: console + placeholder: | + $ nextflow run ... + + Some output where something broke + + - type: textarea + id: files + attributes: + label: Relevant files + description: | + Please drag and drop the relevant files here. Create a `.zip` archive if the extension is not allowed. + Your verbose log file `.nextflow.log` is often useful _(this is a hidden file in the directory where you launched the pipeline)_ as well as custom Nextflow configuration files. + + - type: textarea + id: system + attributes: + label: System information + description: | + * Nextflow version _(eg. 21.10.3)_ + * Hardware _(eg. HPC, Desktop, Cloud)_ + * Executor _(eg. slurm, local, awsbatch)_ + * Container engine: _(e.g. Docker, Singularity, Conda, Podman, Shifter or Charliecloud)_ + * OS _(eg. CentOS Linux, macOS, Linux Mint)_ + * Version of nf-core/viralrecon _(eg. 1.1, 1.5, 1.8.2)_ diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 0481d615..040a0ddb 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,3 @@ -blank_issues_enabled: false contact_links: - name: Join nf-core url: https://nf-co.re/join diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 2a55ff47..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for the nf-core/viralrecon pipeline -labels: enhancement ---- - - - -## Is your feature request related to a problem? Please describe - - - - - -## Describe the solution you'd like - - - -## Describe alternatives you've considered - - - -## Additional context - - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..b29e283d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,11 @@ +name: Feature request +description: Suggest an idea for the nf-core/viralrecon pipeline +labels: enhancement +body: + - type: textarea + id: description + attributes: + label: Description of feature + description: Please describe your suggestion for a new feature. It might help to describe a problem or use case, plus any alternatives that you have considered. + validations: + required: true diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 55fbb49b..ac4daba8 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -14,14 +14,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Launch workflow via tower - uses: nf-core/tower-action@master + uses: nf-core/tower-action@v2 # TODO nf-core: You can customise AWS full pipeline tests as required # Add full size test data (but still relatively small datasets for few samples) # on the `test_full.config` test runs with only one set of parameters with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - bearer_token: ${{ secrets.TOWER_BEARER_TOKEN }} + access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} pipeline: ${{ github.repository }} revision: ${{ github.sha }} @@ -30,5 +30,5 @@ jobs: { "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/viralrecon/results-${{ github.sha }}" } - profiles: '[ "test_full", "aws_tower" ]' - + profiles: test_full,aws_tower + pre_run_script: 'export NXF_VER=21.10.3' diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml index 4350a2b8..2206ce65 100644 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -11,18 +11,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Launch workflow via tower - uses: nf-core/tower-action@master + uses: nf-core/tower-action@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} - bearer_token: ${{ secrets.TOWER_BEARER_TOKEN }} + access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} pipeline: ${{ github.repository }} revision: ${{ github.sha }} workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/viralrecon/work-${{ github.sha }} parameters: | { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/viralrecon/results-${{ github.sha }}" + "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/viralrecon/results-test-${{ github.sha }}" } - profiles: '[ "test", "aws_tower" ]' - + profiles: test,aws_tower + pre_run_script: 'export NXF_VER=21.10.3' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb78c6b8..73360662 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,8 +8,9 @@ on: release: types: [published] -# Uncomment if we need an edge release of Nextflow again -# env: NXF_EDGE: 1 +env: + NXF_ANSI_LOG: false + CAPSULE_LOG: none jobs: test: @@ -17,20 +18,26 @@ jobs: # Only run on push if this is the nf-core dev branch (merged PRs) if: ${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/viralrecon') }} runs-on: ubuntu-latest - env: - NXF_VER: ${{ matrix.nxf_ver }} - NXF_ANSI_LOG: false strategy: matrix: - # Nextflow versions: check pipeline minimum and current latest - nxf_ver: ['21.04.0', ''] + # Nextflow versions + include: + # Test pipeline minimum Nextflow version + - NXF_VER: '21.10.3' + NXF_EDGE: '' + # Test latest edge release of Nextflow + - NXF_VER: '' + NXF_EDGE: '1' steps: - name: Check out pipeline code uses: actions/checkout@v2 - name: Install Nextflow env: - CAPSULE_LOG: none + NXF_VER: ${{ matrix.NXF_VER }} + # Uncomment only if the edge release is more recent than the latest stable release + # See https://github.com/nextflow-io/nextflow/issues/2467 + # NXF_EDGE: ${{ matrix.NXF_EDGE }} run: | wget -qO- get.nextflow.io | bash sudo mv nextflow /usr/local/bin/ diff --git a/.github/workflows/linting_comment.yml b/.github/workflows/linting_comment.yml index 90f03c6f..44d72994 100644 --- a/.github/workflows/linting_comment.yml +++ b/.github/workflows/linting_comment.yml @@ -15,6 +15,7 @@ jobs: uses: dawidd6/action-download-artifact@v2 with: workflow: linting.yml + workflow_conclusion: completed - name: Get PR number id: pr_number diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cdfd5a3..44fb15b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v2.2 - [date] +## v2.3dev - [date] Initial release of nf-core/viralrecon, created with the [nf-core](https://nf-co.re/) template. diff --git a/CITATIONS.md b/CITATIONS.md index 99231a14..2e969e0f 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -12,7 +12,7 @@ * [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) -* [MultiQC](https://www.ncbi.nlm.nih.gov/pubmed/27312411/) +* [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. ## Software packaging/containerisation tools diff --git a/README.md b/README.md index 4d35ac90..1c3cde14 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# ![nf-core/viralrecon](docs/images/nf-core-viralrecon_logo.png) +# ![nf-core/viralrecon](docs/images/nf-core-viralrecon_logo_light.png#gh-light-mode-only) ![nf-core/viralrecon](docs/images/nf-core-viralrecon_logo_dark.png#gh-dark-mode-only) [![GitHub Actions CI Status](https://github.com/nf-core/viralrecon/workflows/nf-core%20CI/badge.svg)](https://github.com/nf-core/viralrecon/actions?query=workflow%3A%22nf-core+CI%22) [![GitHub Actions Linting Status](https://github.com/nf-core/viralrecon/workflows/nf-core%20linting/badge.svg)](https://github.com/nf-core/viralrecon/actions?query=workflow%3A%22nf-core+linting%22) [![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/viralrecon/results) [![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) -[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A521.04.0-23aa62.svg?labelColor=000000)](https://www.nextflow.io/) +[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A521.10.3-23aa62.svg?labelColor=000000)](https://www.nextflow.io/) [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/) [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) @@ -33,18 +33,21 @@ On release, automated continuous integration tests run the pipeline on a full-si ## Quick Start -1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.04.0`) +1. Install [`Nextflow`](https://www.nextflow.io/docs/latest/getstarted.html#installation) (`>=21.10.3`) 2. Install any of [`Docker`](https://docs.docker.com/engine/installation/), [`Singularity`](https://www.sylabs.io/guides/3.0/user-guide/), [`Podman`](https://podman.io/), [`Shifter`](https://nersc.gitlab.io/development/shifter/how-to-use/) or [`Charliecloud`](https://hpc.github.io/charliecloud/) for full pipeline reproducibility _(please only use [`Conda`](https://conda.io/miniconda.html) as a last resort; see [docs](https://nf-co.re/usage/configuration#basic-configuration-profiles))_ 3. Download the pipeline and test it on a minimal dataset with a single command: ```console - nextflow run nf-core/viralrecon -profile test, + nextflow run nf-core/viralrecon -profile test,YOURPROFILE ``` + Note that some form of configuration will be needed so that Nextflow knows how to fetch the required software. This is usually done in the form of a config profile (`YOURPROFILE` in the example command above). You can chain multiple config profiles in a comma-separated string. + + > * The pipeline comes with config profiles called `docker`, `singularity`, `podman`, `shifter`, `charliecloud` and `conda` which instruct the pipeline to use the named tool for software management. For example, `-profile test,docker`. > * Please check [nf-core/configs](https://github.com/nf-core/configs#documentation) to see if a custom config file to run nf-core pipelines already exists for your Institute. If so, you can simply use `-profile ` in your command. This will enable either `docker` or `singularity` and set the appropriate execution settings for your local compute environment. - > * If you are using `singularity` then the pipeline will auto-detect this and attempt to download the Singularity images directly as opposed to performing a conversion from Docker images. If you are persistently observing issues downloading Singularity images directly due to timeout or network issues then please use the `--singularity_pull_docker_container` parameter to pull and convert the Docker image instead. Alternatively, it is highly recommended to use the [`nf-core download`](https://nf-co.re/tools/#downloading-pipelines-for-offline-use) command to pre-download all of the required containers before running the pipeline and to set the [`NXF_SINGULARITY_CACHEDIR` or `singularity.cacheDir`](https://www.nextflow.io/docs/latest/singularity.html?#singularity-docker-hub) Nextflow options to be able to store and re-use the images from a central location for future pipeline runs. + > * If you are using `singularity` and are persistently observing issues downloading Singularity images directly due to timeout or network issues, then you can use the `--singularity_pull_docker_container` parameter to pull and convert the Docker image instead. Alternatively, you can use the [`nf-core download`](https://nf-co.re/tools/#downloading-pipelines-for-offline-use) command to download images first, before running the pipeline. Setting the [`NXF_SINGULARITY_CACHEDIR` or `singularity.cacheDir`](https://www.nextflow.io/docs/latest/singularity.html?#singularity-docker-hub) Nextflow options enables you to store and re-use the images from a central location for future pipeline runs. > * If you are using `conda`, it is highly recommended to use the [`NXF_CONDA_CACHEDIR` or `conda.cacheDir`](https://www.nextflow.io/docs/latest/conda.html) settings to store the environments in a central location for future pipeline runs. 4. Start running your own analysis! diff --git a/assets/multiqc_config.yaml b/assets/multiqc_config.yaml index b9dfaee7..a9c1f583 100644 --- a/assets/multiqc_config.yaml +++ b/assets/multiqc_config.yaml @@ -1,7 +1,7 @@ report_comment: > This report has been generated by the nf-core/viralrecon analysis pipeline. For information about how to interpret these results, please see the - documentation. + documentation. report_section_order: software_versions: order: -1000 diff --git a/assets/nf-core-viralrecon_logo.png b/assets/nf-core-viralrecon_logo.png deleted file mode 100644 index 4fa3bed77fac8e300ee2445f13fff8a99279285f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18781 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

SQ?8Wg8aATdubeawebdw;^}h=MIBni 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 -Content-Disposition: inline; filename="nf-core-viralrecon_logo.png" +Content-Disposition: inline; filename="nf-core-viralrecon_logo_light.png" -<% out << new File("$projectDir/assets/nf-core-viralrecon_logo.png"). +<% out << new File("$projectDir/assets/nf-core-viralrecon_logo_light.png"). bytes. encodeBase64(). toString(). diff --git a/bin/scrape_software_versions.py b/bin/scrape_software_versions.py deleted file mode 100755 index fa933f47..00000000 --- a/bin/scrape_software_versions.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function -import os - -results = {} -version_files = [x for x in os.listdir(".") if x.endswith(".version.txt")] -for version_file in version_files: - - software = version_file.replace(".version.txt", "") - if software == "pipeline": - software = "nf-core/viralrecon" - - with open(version_file) as fin: - version = fin.read().strip() - results[software] = version - -# Dump to YAML -print( - """ -id: 'software_versions' -section_name: 'nf-core/viralrecon Software Versions' -section_href: 'https://github.com/nf-core/viralrecon' -plot_type: 'html' -description: 'are collected at run time from the software output.' -data: | -

-""" -) -for k, v in sorted(results.items()): - print("
{}
{}
".format(k, v)) -print("
") - -# Write out as tsv file: -with open("software_versions.tsv", "w") as f: - for k, v in sorted(results.items()): - f.write("{}\t{}\n".format(k, v)) diff --git a/conf/base.config b/conf/base.config index d7acd5e0..7d647342 100644 --- a/conf/base.config +++ b/conf/base.config @@ -54,4 +54,7 @@ process { errorStrategy = 'retry' maxRetries = 2 } + withName:CUSTOM_DUMPSOFTWAREVERSIONS { + cache = false + } } diff --git a/conf/modules.config b/conf/modules.config index 0b1bfdec..a0506a4d 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -1,32 +1,41 @@ /* ======================================================================================== - Config file for defining DSL2 per module options + Config file for defining DSL2 per module options and publishing paths ======================================================================================== Available keys to override module options: - args = Additional arguments appended to command in module. - args2 = Second set of arguments appended to command in module (multi-tool modules). - args3 = Third set of arguments appended to command in module (multi-tool modules). - publish_dir = Directory to publish results. - publish_by_meta = Groovy list of keys available in meta map to append as directories to "publish_dir" path - If publish_by_meta = true - Value of ${meta['id']} is appended as a directory to "publish_dir" path - If publish_by_meta = ['id', 'custompath'] - If "id" is in meta map and "custompath" isn't then "${meta['id']}/custompath/" - is appended as a directory to "publish_dir" path - If publish_by_meta = false / null - No directories are appended to "publish_dir" path - publish_files = Groovy map where key = "file_ext" and value = "directory" to publish results for that file extension - The value of "directory" is appended to the standard "publish_dir" path as defined above. - If publish_files = null (unspecified) - All files are published. - If publish_files = false - No files are published. - suffix = File name suffix for output files. + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. ---------------------------------------------------------------------------------------- */ -params { - modules { - 'fastqc' { - args = "--quiet" - } - 'multiqc' { - args = "" - } +process { + + publishDir = [ + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + + withName: SAMPLESHEET_CHECK { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: FASTQC { + ext.args = '--quiet' } + + withName: CUSTOM_DUMPSOFTWAREVERSIONS { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: 'copy', + pattern: '*_versions.yml' + ] + } + } diff --git a/conf/test.config b/conf/test.config index 1c930c37..855f960c 100644 --- a/conf/test.config +++ b/conf/test.config @@ -16,8 +16,8 @@ params { // Limit resources so that this can run on GitHub Actions max_cpus = 2 - max_memory = 6.GB - max_time = 6.h + max_memory = '6.GB' + max_time = '6.h' // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets diff --git a/docs/images/nf-core-viralrecon_logo.png b/docs/images/nf-core-viralrecon_logo.png deleted file mode 100644 index 2f41904f9bbf8120c5789cc8df7915d0cdfb761a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35628 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%0D6Aha9ZZvy3Owj0R@{kHIA1_W(q z4cub$!_rc(sJ~F)B3e6iLcjrRvPV}76Ez;B(jxHQ$MuxJO9R0Jd3uw8A1n~*9o_jj zu;x=$PXC=GOJ&ALTCbu%0`*%r{5T~HsSemFF;Z^ys5+rBkV0wxr)fKo1AVz27Fw&8 z3>s>9w=Q65-IagvI70e14JomP*P16TVrML25M!p?NCYo)AlI6azR#Lz6Ai-IzRR?( z4+agr(5xfTKs3peZ)u(gG`169WT^In|8Lr19pT}kL>j}s9dTEo6RwyHaHvC1OYW2# z%*U}0mMYktxyYpzJK?o1WDm5dST8Xb7-TZ*Fozk!zyOgU^EXcjhbS$xX8R-oOK`l`(pIO-}*r1to^ zyoS-;qLpAVg-A06KDKWvRf+zz>Etb}FEkQp z&?Y-#gxOsu4=7RV0hlPJlHrA+N4ipRxEG7x^<#oju*?AKN`CLM5^zU0)YNWpAHD~x8*;i6hn7?O762%8|>0#8?9E6Zt>!AKdA zGYia($+%%IWyVaiW^zK8+03y7#BpphZPSFYRU!>SV|ANol+nKd#!}=Uzy=fDz?4N^ zEC+mc_VGiM3avQpCWMv-RE8u9JFPOr?X-Se-xpeuI*jk@O}>l_rzk*F%*P4d?F(uykl(@_0BwS z6pW!{!@xhsi-R0_&0HD5rUE{y&g#GO<4W$CNT$Kd|J=h?1315cZK6>Oz>u&9k`S`b zyMeMcABH>fV*dW){AUNYJRp_6LMoX#IgcYICch6Y_Fnl18f;Yx zZ!8)drgi4?f$MfP`Ja>9*+b#Us!~SVDVGxhw#nwrtndJg9Q4!0$bY85{+05E$_zP+ zRbWx}UFa^pqcL(rA9oRSQsoC5?po8V&nXf_(+C))8b_%Cu!@zX(+04`BA|{~c8(`< zWN$wZZ1Sgp;{8opoA{5c{&%EK+rhGE*?TVz+jdDQ0mQ>tfRZ>wVdqG#qxJFCTq4sH z(ru@Dd5r*BZ3{amio+5zkg%vu%2Hy|_Z;Gm441DJu2 zGyTqdaGf-WVKaTz=%m}(X&Xr%K`NC)^N<+VGtb_6n*~dM&!CO!r$Nq|K+FTN zkm}6tCe5@y2{6P`6w8g>V`s79xJiTg7yTn!!#0Ou278Dc zTz3&N&k{RHnv6!M^ zvX$722lC#wnVH{0a)SLw)Dt(tvS3EvgR3n5PucK&S5JcAMD`00tRTBO{zU= zGD@cO=DpEAluw7n#RteL?Vb9*8&gTg5dxO^0(y6)iS|(NuZ2v%Awe}A8(Opq#Hh>E zjX3Lqbn#o_&`icn)4d(GR3kR4t{weB#H)`l8D{h;V=0%*nUnOZt=8uF3_OyPqa&6*S$#_aDav))vWx_L{XZo4t&A?M)M{HCA{jL4V#eKVT>y!);0mq3p$g(rU zYaG91{)Vs>BR5)u>(*BXHW~&P6hq4F*@aIn9f@yve3lbgCPf@rl$?1yTKWo7U>R8^ z)HfQtaQAhur)$TI{|Ke?<_5NMl_C!CXlN7PUM#Lb-_^!e@Rl6p?i7O@*AzSgW4|5c z*bGb;_LJ{{-~s3&#x#IbQ>MonJw4YImVEL&A}!o3r;u@={^l;1yOrhyY0QGlk25@} zyuzG}IItHYYg+y}jI~u_A?MV}q`+*SfYhlRlZrN1flxg!8*GjB+#FpS&zN=|!repv zS)k;&OP%yN56kjUW&y#}(9JnFP!Y^+65ISzbbT*eTWo`QYB5W3Ej`JuhHf@kH>hHtQR0qEVF9#DPEwf{yE)^XDor9t|be-j5 zR}k$hRA3}H>Xj~ly4dVBu&P}4!}Hw3g>TQ!oQ|6&g`O)9eI`n)nN?`i3koSPe&snM zBO*1eguip{%!isPU!|{(zwVGSNf!+O^iN&aM&BVrv92!$^Ov491V9d2Vy$qLiZ+RL zwuPGQ!Xn|mZIe#qTKxEt#?4z;^-J@wp!D7Qj6GMaEH{Ex^|<|I?Se*b-W1xptEU@i zuNb+24jCBs++5o8W&lrNZrv)}~WAi?GMJxqw4 zFwx!C^G<)h9tjPluh^Q|KFmWcHzoYz)?l_E-}3F6l4oCy>?{kfV>R3YI>K?M-fZ}t z{v0OP9;j-AogQ8Tntjot)-vi)gp`_gQCLYKg{b$@e;0s_q+tgi!n9ghT}%gMaFP%A zZCpPpQUC7yHrKucN-!Gbq@P4QJ+@qJEFB4w{1(bdWb2Cr?2Rw4{&R zsCjizvWlqIK&nSsPb+2IdOPT=9pK0SFByPfcTf5aHK8*j(AJ?U*d}Y`;ioRer<4I& zw-*hkh<%`~r7Hs*U(}rH8@=y5gbZuHg2;N?f9@y#;=C}rS%TkjAHUvw3lg$mTqs>p znyaeycM)0?P@*`V5ejczVJYe`Vc>}6xatics7*S)rs!9uypo}^G`g8?m>&ayx6z$1 z2MstI0|4@cocQ>8OK_#&5;xO~0r1fvS4w)6&dPm1^o z^>H~q4}`8rdGoybVhMYf6-lCebov`kMd$wiyzS}8?D5?XuLPRKeWFPl3xD}?H~PAiQ6%hn@v@V8KAY(HV!$9_J!iunqAQSDHPvJudfRO zm{f48*xPy!llb%Wg5E;b#scpKj&J2^3Dzr!{&QkV_0k>>HN|EJ;0rzaVx|+NN+4L!I;HFE3-Q~t zN1bpM=+^~polMa~OG-kJ`jV%Oa08d_R*anZGDh8o_uQXbc?Yj-kFFKn=ekaiyUudw zYx>2QlR_8X>^rfg)e@6stb5^mkwwuni<kp<7fK8 zz!ltfsQ z&^SfLEdev9yni1b-IB3V;$7HpqxA(T}lT@Sl zJ?aH~C#ew^NxjOC3L6d1R((~BnlwLL`l6ERY0ssl@?;@0R`!t?#utlc`!Sr;;>F|c z!KJlg!5lN_A%e$Y7BHUQS;MlfGtVZTJ8;cj;LV;^B#NmtA}EV=Ja_J(wl4!8L&@7! z;kr1{xx-<3mZM`IO~{Q$Ba6+Mxx11x%U%p)LW`OWy$x2X1n}lXL(I+`N@KFwZ0g;` z!^^Ce+bhrT9}0J8v&qP$wBMFps-=rTwPwHk6e+%{8Xk0)^th1~6>fPG`%1B{st*NG z{%DfwAMe7?%^forD>^qwP{H^zWZ!9Pq~G^Brx5eaX|Ij8^;Vg-Sk5v&Fm0)fpdFU_ zWVkU*iB(ZtZ)hTnli66B#fr2vUsaD66)W*wt-@cs^2{m5JZp7!bb8f9G3#^8NYm{tF zva2;UHP;(*Ppu2s?l-&6uf0qfBn0;LY&z}b6R(l)u0JH;TF|aUXlOLu)wJrzGrKPP zsD?4<6icdhE(1FB4Z4!WPtKn>%1gO$G3JZMjn)C7?SrYjneASOW=ov*hVCjo&XStj z5#~^th7Q`hmPIsdVS4wT<5&S_b`FD4`>~YH>N_KCGk#kIpEC?&8otJi=9c~bnJTzJ z*xpi~aP65J$j(_X>S_ye8{N&UcSq5uMqpHz2#kPvcSEi`6kEoSt=tC_V|qv0ME zRSR<;sump4-Tl}%cluwfMo&fZZMttn2~S52$4<3a4W$;92nDCtMzi()=>2+VUTm^i zRLXyBTjhi^BJV{)V%ee9i`gx`wuiMfH66Ul{;IPKi%yLd8>aY?afCFdy#t!4B}e_M zp+2PCZU0?sn`F%%S(&rPW+llku+>AQvMi|*^)DRMOE)Ho6!D6F`cr4vkp-Mid+|(G zXjG9!)(k!QtL!6E+Xume?+p{D&+y%EQJ&xu+K|2>@bYi{1Qx*x>S$DVLF0;kNwW-| zQ#EN80Y#NUl#ed556zbNLE{A3Lcvtw5N(c0ToY_GZ+oHf8*3jr- zcU4l)mL?W8KNi|x8S=q}d7y8;Fm-#n#y6_g$X;)id;AQq)SH>~UpLRso3o#p;k36V5~xWZ^(2qW?@8c7WTDZOgiqOFQnod`K`efz37_H-_&v49Pd6i3pNh`yDHpGheV}_%N^fX{5?$1PJNZID@!vt+qzugC zd(z!@N!s<1mNP4&z@6p2%9uv_9DuW2O*w_k9J2jFJWj);tAS)5CsEQ z{CVt+=-!g0N9+nzN{va7TSI6bI$MeEB2S3s`lSbg+TZZrSD zE&9B~ghcDP!E@n(+P`9q@jD|t#Isi)I^kYvbL$m~vgx3##m zEDld4d2 z1Y-gxyn@1cNuM;0__;VMg7P1Cd=O~4tCCLIawGOeSF8F~S*&G0)h$N!=<@&u&)!?pAt4_o|Sx(@D9E zjlU*83X%7aiy(6a@3$~bob?8-KSaJqxge;QJa`kcyYu<#HP^WTJp>pTCtrYd?p;u?6^%gp67f|dYyfOYcozXpEHPY zbvvi9@vWT5IVz1c+69U%Ph57W7&M$?Kx;TP3 zSLNN$%#KkOCy(S!7ME7CuS(ZR-0SPOEgO0xqJigNpnY5Vvn|MJ^YdGjUjGA8n>=(K zh;3=8;a{MJ=XoFC=H_OF2-8tdFy%Gw&BV$+GGMOe;I~V@pjCT?oK2W$MQ(jN8Y!#f zg#pd+qH=8E(8MXBjXGYP8m7vx%8%K;ohGP*6r4XAIbFyhEIjI^l|6|=;7g#qorUst zz>>MLvi%n7XY^rD9*irUYZo9Le>5~?yfmbD`8U@%lrJXg%LZ&f&^9K$gAt9MdPJ5+vaH}}Nz~x*qUd@M~AQk8a zv~LKG_f3QerlW&$yq|jDL{EcDUWm@EgoSF}5IkrI-)W*UHE!V7mFQPhOyBd3e}2$G z7vOb`ODozuoXh-WYA(Zpay~!1nlGpYJ$+I?gDU5$hHbQS?fTSW#*u*aKu*hmmG@Dd zoTMkUPi$rTPhq>3GcK;(oH_7p$8IGZ! zV}sUx>L8NN*)2)CLO4px-4spl%$wn*$tSw|2evyKaxFrC+v)0OD39v8AyU+p)Hc3A+*FwTUz((r zy=uRe!bzp~r_<9s>H6%*Qyo00i6nV|qYK#vtBP2xQ3W&xCfp_c^ z?uS*vr^gs|hL9fR$C0rY2>1+i4j=d8aVRgKs}dpc_$O3_m}_H$-!XdcA?E4ZMb=d|O-!W9v2ho*so2kFFm;5A1U(<41cEwqu zE$lh)FGrHWmamLEO>266Zey~r=n`xTt63stL{|>qBdHFpeqS)L9Q8OtAL0)#G3`59 z{K`o#VTD18x&Kl~4R_4nSzlOW^5=ZvksRMUA;UsIGxa+PKmmOt*vr#k!(eR>F|tVb z6wVMzmvl4vLOqr<2&FfaN)tf^%~ze?1+4@UJ{yf1zZ2T{rWakY_wt;hZkmWHlWh{AB{YzV$u%)6=Sg3)dA{hoY{1FO(<0%S>xeM zf$SqeXf{ND>wvJIkuRf;A{Eua3`Dg7jc@S8?9}>^ESTW>Mrt zhOdPxDq#vGhrS(Va_><~geaDc59(U8QYGALF`RrMUQ015)6|mC(MNX(+TD+Z8A3PW zJNW$v<{yn2NwXDX7ZKK6`z%C09QyW+um&5l5eemYvHr0)Ml#oZZ;YX@>?|MrG$1xJ zA|f&enzLP7+_Ax$7jb^OXXD>a3fC=|kV(C}RXtjfqIyFEbX!MagB>oFZmiLWc7Hu{ zb&BZRBiPgxYbJhsf!EAEVN$@3U7Q=1t7Y7N+_RNXa=wM-G~Sj=Kxem5Z-UI^o(+15 zPh=`aNWIhP%8&+1;q?=lZG0K+?M@b0X~{6?M~w|nMz1Jc`0rf+qAmw&?3LRlHS0-} z*z2n~GL!X!bfoMnT^TumKYA+t+Tt2#%O>lhn0t*waRThtE_re8D;Un6S;*&FtrS7-Ivw^yPPd zPY1~s99g+oIB+BLLBL#FT#DBh%@20p8(z$4;KKX-#}kB2nT}*X44zc`Nk{z+rJNax zfUjA2S0XSlIjo!3hetsNZ7UVr8#nAaP= zsqSZ`MtMj4y(&SL14Gw8^XhExQ|ts>z7fXM=-~V^y}9y7L<_5Hm9?WKOE``e;P8p)u7u9F`S?rr#=fpotmm%zCwWxfq%4b|wLUV3V)Zd_PlS3g%2 zBmb{JmF=MQMwR~A$U6O9Sp{;}d1l3-PRAxJe~^M@R0s&ZI3U7#}Ly{HD4m?Y} zSzvDP;395SfFhP6t8@;zQbDN5(YQEu4Fr`EnEhr(lDYr7z}QXXR}4k^3L8b=+*r)y8T`efi$l($KF%Sl0_WHgoFyDoc1bUq%20cGWVFiUwz}v!9AJ{ngDFnTnDYl+L&STi2toA z!t~ary;>T&GP1>Tx8DnIM9k)gl5tdG*Z^Zle*3M2SR_0n3UI>hj$*Z(TVf}VK$1_` z;+9Adb`$cD9sfjwW1zj1e6_wOfz7vh^ z{SvjQV)i?;7NEv`P=;o1Z>2pjm)A~Z3B-!d9VB$N*n4mztn!DlJCrna_R|gM$3Ep&J01k_LQ%0MnwU z4kNIR$Xf9A81MZt{-R+s>FAu;k3zyH^@ex}CEtf7$7{sfkf>Va`)mfF0#ZwwCqRMb zoQassTYG>Pd}^@h?+a6mJKVS2z~>5Kg<%2}Yl|CT`32(>$Cqy34Jc-}l6_{ccVCR8 zdV{F8SzcumQTs$;oBD%+gjytLRh^}Bli2~w_NGBl_ngCQ7~l1^d2fm6FPlX1#B9EV z$z)`zJ%$q)j+^_0F3yjA_L}V@u=b?;gmm>K>m07k*K!1W1ujTP=6owM1+m3{`Gf}3 zpgVcNV#-)NkIuwAeC*?X@*xNq^ULGZJ9Pc*l`q?GE$6bxuz>4tl1B*gzJ}*f?W0$H zGS}^)2%F45l4bDy_$5PPu8N^1xrD!bwK_XGd7h7XYmPa@Ik-r`Z;G;bXXJ0^&DPtp zB@8v^ssr36b@YUc--zXEI#>nU!erB}BIJUm=EM4`T56tQ{<$}k^yC{fQX6C=mq1t2 zSX;KYel+ZAWNM?I^UHQ?Z<%NC5|x)RmsU@%Mg%`bj*?d72yl>!{Nj}a5mlKLBHtvZ zxqfaby~Qznlm~$7CRn`qj-l+y04iv_Z^p*^lgCrzIi*q?d{Ro)fbyr8MThhtCdLHT zFzOwN_;-1bJ^Ko;Zh8|O9o`}khOpP#lDE{%<&14OSFiAZca9Oy`-wy~$ z#s1BfU=rAV7NCMKwq!0;a3Sl^d^nZ>=2%0}Zwb6H1qY@8JJXt+MDI9MqAf9Opkpr4=mWIQn|}}GpnI-VvsCpK34Rq-FnIWl{e-{ zAJ_es0GegBxs2F7@Ps}(E0sw5T}QG*eear+u7;rIXnjvJcX+an94UB!jT+|7!n#xT zZnyAokH?P?${<9VZ#qzWl5Pr|e{;QgS91Yq*VMMK6^n4L7&U<3=~*$O z1>QcH>JQz{k-$OxeviGQjyV0D9>MnIaz_AN<#a?{l=bgG;`w)n{boO>ri5&~z=fN5 zAF@2`w+-}jF(>`-eUsEKSGfduo0P%FY?S+;0t~GiOE<_rdLy@TdAMU<>Ec$|$Sb*? z5k`+J13eyibHr?VIVF5@rUB^p2-B(wa^@uK(()q)X0)$8-qeZkYx13mu3r?of9jTd+_IE0(% zR=d zOW^$dpxmTLx)q@)!$7*h*mDP*ZvPd*_5)ViPfn~;?mvjr#d{i&-NbE{BMAdrxsG~j zvp*iqQtU>48dzdQPgr}$BVU3F}LH=gUUkdJYeHIs;IkBZGuxhzIX?}HrgME|d zVeB9xsF(MRrY4il13~W0#|mc5TyROpfh% z`#V40h}e+JaM30b-hi6;OS&7jt9g4^o*ZK*FiEN7P@D5NT>bfoXVBheb1m};Gjsp( z%ICKjX3fEmS2$^BEZ34iWfVeR1RAy|TE-%ep7&s!=XWo`gX+g-?cg#wU&&b+_;;aF zNN?zBu14dQr?ZN-%Y}szXl34@pg*O1cerD&;FRTjmwi-sYf#6J*NqDuY!ju-V|BD4 z1a0$ba5^RA-TI#6sF2Vw;Ljd2P}qFzid@39u>JI3vX4x`eVM7UkN6nyADT~VbrZB~ zaT{hFk?GXP;uUF&Q9W1A+A(XI(JG;GU++oGSJ@x4l>cgf1jCG>Wz)sr`(Tac!!h<- z_#$X~>qW-$iyEh8F~h*ZY!L>ik&5>$jtKA`D0`axtPGPF~`6RK~}Y!+h23d9noJ#FO~eCb0uRU6-p zq)41QAX3%)T;-g#BqA4KeRgZrY&&_`#_-ODH_eap`m%kF21gJmy54>FTk6jVAX!qE zDA_09(Ya5s59p!%(YxAuPY}d0;EBfC(t#<-g65V~@xRBdgoK#3_V9-Ms<_e?tm*KR zCeq9O=>Ull7q=@@sKg*{{{r&~gz!yfcb-`nG6(rpi2g2?+FL4X+3cP1l3RPAkTo@K zO9Y-+<0%&3@F55I=_Y&kZ;4UUW`~e6K_4owwK; z&&o=SI(Ol&%Wb}^`o%j4KBhOcn|x)0zu>V1CAx4rZKKqgVGSoKqY2UBOmaxJXGBs& z@B@exh-w~_otHP4R3|9$K40vJuaU^Rh{a=mll{|{I%9;;U zwX9G2n{a7$S{SUurmu2o6;jl7w=h)yeC0szgipYCjek9XT+hBIowX5sNt2D%XJ~Cv zA5-4Z`rA8@{=c>1$YR;xupdh)zVhor9z`s^xV%Jyungj~Q_KQJ4@W~kx5?OM2CDB) zI*WSr3n`mkKA@kHkO=b_5bfMCEi3xAMcxsI{1{6gQ}a7QWAc=`hx)$=y7l#-B^9_~ zMi8Vhga6epcy4QTV)kQ@c+vdf4!V=B%Kr96W3>BlaMc}-kr+amlI#*2BROG|i?3I55TtQ) zW%s}Bn$UU3d&UVaUMRf??#zsstqV2|7dsey+a#7%5wxL*?~yU~idLfhiiwY~*Pe6Y zL<7}Lp*61rT;t<&1)*_t4G2yP=C1IH{I!{&lS>I(%PecS#Z-q>SPmc+PcK;1eEMNA zZkw%=b(NX??r7xt{ERi6P&XPhyZ-E1YDGSz=GSHtSc#qd0tQtfs3UMVA3TqZQ0?-W zCo19DcFyHD_TF#NCyT=|utxRoi8?}lappOU_BY|K@vtM9*W zcs*_wQCj=;uI;WhPj+!ks!ph+q@_cwhoIz9lBusH#s<#%O(u@g)=ZauWV0veh+nw( z5n(r=3C`RMDI%(hqyD@T|Ngk5%p@Z-qa-9`U9&PPWJIht~eS3T%`U+?oe=XsvzdCu!~0ZzFw<|LdY zG}^=-nwp*VLs21bZ~s=EY%V9MAPJ!MrS)vGX9<^aTt$zwVL(?QU13>^YPU%wW%c9J zUHz&B)#UJaC8S}6a{*H}roJU0Cv`w32}c`VIrg5DgIjnDU+WW4zoD*Ip)z}Q%Uv9T z_hu8PLhMb&-Q{{4;j4UB9@9RUTPIcK3!eUM!3q}vV`9ZD6LDr%Jtw+3o%yvhXGrmY zJL%Pgug++k`9O6A)}>+KTXU;IBXK>EY-26E+;*%_n>aY)hb0bTvop`Mv(kDlP=39j z9^*;2&4d%3GsrO0kpG>tcC4VvMfgu8hTt2YAd1IFl>IdeG^bCytLhFYeGdm(y(6%_ zL{djNLV}0FLVvT*+p*-QWRR0)dwV{ye+)ieC&kUEl8I;l@D+k&vW0UQw*PBCNdSq7 z0)1~P^I$JFBs``5{b(n`*B|Q7lIx^OR`@=LXL-jbGyisyr4HI( zIkeSlmA8fxzy^45s;^6XaC0_eb80~`0i`pWHQc=-A(fWUc`7)=>Xso%Ot#8b(?8RO z=xD#noI);3?J4BU{;^#m;pIT)9j)g{K}@fV2eVH;)0R1&sTFnTScXMvJ+J9Ykv$zH zuO72~cMX+@LLr<-bK?Cdt}^cj;*UK{tVk>AHPWq{WQF`PV>dWOcw;#&{(vtLCW%j#t zJhLj+#9iQcCOfPS3b-O-tz-VwZxKJn8Fts98-B(41uBA~G88}g$l<*sq(b4HFdB)w zHn}0|geEhkq{w_hzjXM*S#!$IeMq(X&m=uwiYc0}*;Q-#?%dH#{N5;LbPg-woCV}1 zY6r+>MNE-wQKpk2@MG=Ys#dqas01mC8`psOB`4iWe#9iSAPvyebRkurV@dJ8CshKj zToT~hp1X-65*tk!%)NXSA1Ei~CG{an2Dg+1x$T){5Afdc!z8|o!;2_;AI@vR`{C$fmI z8sP~kYb<|4DaQZ!*O}C_;SU0Bh?aSTUxmZO(RG~LJll)%==f241w*mpnbl1L1AV4g zN@s5JHjNhhyVIAQg)=3+CKXLc9)Tjj+xT7`^6xXE6`HjtOF5OSm93Bvn#@KXvSR}K zy5rmhHH+;T)f6L}^BP(>h5y$^6e$rgkO% z^c4O03;FCFt?k!41}9YjK|glfTd=k}Wo&cu49dx>RI_QTRT*Uz$?~j=5qEK(?K+xY z;fIO#zF)tzx7%kk3odR3gT}9&$pi<6{DyWs2=ujnHiIq*3Hhti<5yLVaH9D-lYuO{ z#1eZp9Bcs1u2Cws6Ky2(k938aZbDnO3L*DnG-V4Bc{liS=_#on;29wwCeY)?*u)Q2 zpToN@A~{10z$rV3_3r|y%*04S;SsXX4pruPi>Igvt|}Yo{nZl0T*38EX4Ro2CS&r;CG>Ak ze9duSN83ZaM}tS+o~}=dhmSsU$8oVMw_lp7*G1IzqzY@`!va^T<}-Gu2D~obXMh4d zUDcr9ZZ19~ycDUi(}fhK%cTOxw13}cdmclc@2ZD6byVMqngwx4Uaweitwz1Bf=^<9 z)};YhNqvj>um`D|AY+FLMbYr2(GVDSNFET0OSn()skOZ@nsfYv<-Mgp5GtiK_hxkF ziLRF%m->cOG9?UhWo}fW$Ty6<__#9*G@qxj61?;@Gzz#yeZd_Hp*fY5vo*S+429gB z#hmb{KLyXO)q|fy_`mE5gWlhRMkAKJcF0pz|6C0u=djw55#90{TdxY(Jo}Ajr7Ep4 z)79&7Qo`P8Gk?QBLFQG0uFLBl*rMhUlpL*Sr$_vBymbTWW@CpeEoT#6R_!r1-IdDL z$6v(to6>-+rXIXF$(<*PawnBjA40Ue3qxfp;K*;Px#c=OQ5s4!ZD&0&8>~t0EA3^UlOy@KrXO0dI z1jz>88ET%~B3j+$uUhO{QXzMx5S8DNN<}4yzghyY8kBGsW8oSj@1Coe@VFRy-!kM?1WEBcjU4cZqMs?BZIrI2m2WIJjb@DRgfyq zt2C@6s#SNg-B&?)`}8?(kGAsZi9HUx$G)AJ*c~IzH%iM~=1yV~-MpJ!$RY{n@)r3L zCY0$k3WQ76g6n$Fsb_`8BGn4lydc>d5I2)cr@Wx}T!@iC(hty>Or0+fBUK9-USe`wP&@J zQqi@mHu!~da@v}OiFScK9P0f-PU!U5fhIQ@#45~343>gtAXwTO%ht}}bRgvJA%+bOSmg_@eV zTD(Jxb=v(`2K@;c6TitF&*`s%5X@1pSJyn?;kE65PUsR@|GF-%NeL_@$j_g=1B51NH@_-xy>}5rN$4$J z`fr#nL@iR{+E&>U%LQ_RfYL(n&0kZ%Yvl756{X%n4+cZidw;mOgCGX0sY4ca*(tQxz#d!ayH1V--}sjN&-QDA2GX zI~0^w#3WEhKDK;1;)mzg&JPeg-lS6IAIPjYp{ll+eArK{@{=jIF{XY!1==yEpgQ6C zlLa2MFb}ompJDhLx==v7pytHeQkt%(s)5UQA+%;2K*3L1Oi9VX7tab9;YdoS2=wNc~m?1lG-xI$f{`D1SHw{V3eHxqt%2-G9#17}85{9%b} zE)VNgs$zVR^WSPrIY}QBe(y8`ceB-gmtNwNc`tM{+`O;3G-o{Q?)y2pAm=rf}{A%R1aKg2;}hEsP& z%9pPX`yHtF_=SFB>~Tk?32Pj@Gg%30+wu@%juU10@E!S~W@JnE7JrLuTJN&vd5Tby z?rrH|WMEVQAU2nkRqx}kofY|r@Q=$xN%_~AJqroJty6(A3wXeuo-rr-YL|DONL<9e z$FFWY)Cr0NTUd1>SUWBgI#h~&+4z|;#VD4W0c&C$%Ql0J3}$O3;mZ{AX4$xyE}`Mu z7kk>O-*v~PF69-ByY3KiNk%osJx9{}`PRG2r5i7-Vz$;v=u%o`@ih;6n+G;!Ytr|; zI@giMcsl+3-i&<|U~g}gd;lInnrxTytx5IC<-Ca#UbEWJliLu$m*!3z<<&XIL=~2G z+)H=dqK`tXg5Y@lJ<6=wBL)@7JZ$E!lLz>e@(}oaBiMPjyza-(HF3c&xI%YKm zeFIDr`1?lg2|n@Ljk)Jgf*<}4r(oRYPhKyNtvSob?Xv~#fa{y6c_Idb;ORU~7kYYY zBG_4yns2Zqe8DwWJb{Y z7XpuH0aw0E5qsES4f!jYd=b z-Eq?!Qf8l@O?dF_m0|}-C$l)~^=K~YdlR32m6kz7YL>ll-7YkesS*QGv6JA9we-cdlN$=Kj1?!@<^Cf9{x%-!wj!4eB zb%WKr`y|?QpLAK)R%IhgW2lGf$m_=1C%sB#=%2gqn-M)(HJV4so-_((E@6dI!U9G} z;P?oab25}Gd#v+B6F`Jf;afRd*j(eQnrJ?8{O2{qJkcE>e~fdhpT+@6E$6sl(r5R{ z`-0G>x6A_pt?r_uJz+{#&j<2TU0V_S<_kpwlzbCq{wg)>n#*Y5aY+?Q19Zb`LqWz7p6L=Z>7OB@$kd zIhI)-A0WSBs@}YXMPVJhu#)w9&TF;!@=ux~cKICo{j2I*MTX?Bqdu_!CW%-*1XCxF z=Od^FiyfaNhb)YW^v+MWC>WA6)1lt3xn>wEobv<1Q9McSucAz1LTJIFIYDU{4!4=L zN!c4!Qo5*Ge=9`oDwL{L`RKzPPqWRPwGCu|QR*s$1Q%M~l3h;|HH4!^f;yf?kiaS^tcd{C;d_38;F;m{B3Rp!~p??b|49-GXUbsz&nno;5jefTa>pdN*^>M)5E@+SrZIrm@zKz+*iZfKs&b~{p zZ`FUVoQvHph6p7rGpOMV&UD{@W1wT2fGIIL}dEV=FI(g6xJ!dMSm;n4+i0-^C7G+ z0g{r{^!+c*b=yox{kExv0zOLa1ki6aYg@uV$@4F9fJj`Kd()b$lQmDL2L9dlFTc?^_g`7K9-%+mg*Cb?Y`vzlQ}P|| zp9_tJ|2RsnoJBQ$i3>zpb8hY6e~77=5~di~pyb(P>L<41?JHQxM49ayG{#_2)ng%l zGc+UXfn(cdqeAGkgHI@=f7Mc`ed=r9ol^;#ST#Wd_&X@2xxM2-z$2dw0v-`sUQzLm z{>hDp4-~D_GA>UXnB3Z$y|uX-c1^?MmHw8>numX`cx?Qm5j~Ez;T4$%u6nVQY?-WV z<{){eU%0A- zyem&6;(Y#fszHx@M3b8L3vd~qpGXXIp5^bOK2g5rbIyWwqe}87^UTZg^$rW8rNA+8 zW;Ltwgc6~R;5bfIx0--tDEVK|m-y7^o>Wy93b{`Q|e zPs9Gf$-V?ZJ$v%3I^zlD;!(;ik(95|%hk<_>*tvkMID-0tEWdJ;bwoUf91|K6&-9S zr9uhKWk-On86B}%^p#Uyjkwq7>4IbYtwQjQQ5|CSDTw^ zYSidY$GFN0r;Yb`nfummq0il&dMV#z3s}c5!JPA z8Vl+&^L1-lR!f_rw9XG}O76RK)kJIID3qsOTVBDETVxSJSfI^1pwB@(3Rut&xS6P* z>qU;^7HiaVmMr?e`_9om^!zz1J)BI2n;m8H3`M)-lvLZG#(!u<$pxe%H^}wEj#({??(XydRl1rjK*XuKCd_tZy6V ztx~t=`lq_=-jW2;IwK=|LbM|6j#-^*wcg1V*%e>T72E2_u<=t%&=fi)DBu?mBtUws z;i9lpJ_LLOY=G4v%zmio!**Q6Q>kG4z5DE*yi^l*8+%=4oN^xVR?nCRhX06Qq&bsZ zCXg@-5kl1@#pqwkbzSM=L%e1e*j>$mDsQ2uIQWiPN-N&w%ms75JGVAxZ;h;0zhduP zE}XpBZ*y$2VlZJ`H}TKY#=;LZc>LG&(gT3aKk+WxOChP`{;vbLosXrv6lX7@27*z1 zd)qsSA|6y@!Y#LUip8oLvP1;;0?$K%u9rfgzzT?TpZ)%={SRRN4?X)TXgF`Gh7}@v zV;s!}IGf^1 zPW+(mn{6^N{2{>PYO;F%+c(NJ;o~civnW!?Z5&;+M5Y=&8s51OXNw(cGA*hW73BP) z%T_ODm-BXL!l%^qpnTcGNFl*k`wg)f&Z9*!Xj$LuSRH&!eBw3BY|zUjoQr@M$w8xp zs1+!4)cZZE4kKWo<=5!hl-zH^*vMULb_>0uQKlFeWioXn5Vt@;8@e;x1OZyfY7&f$ zaMQQ-Y6xK)nM~Cz`Y}&6(i}Q3<8Q;QRlM8kr;*D>zukaZA6@|K=P$1z(;78M*JcZM zTip4#Kh95OH7ibSVVjk_R>?y}bve!pyr=sdt@URw%d)cmwa%d3Exx_eb9&o`tk0rU zPPHE7497-S#p%4rbrh&&MBD^4)cY9)Dd-|}mjmfGFR9E=t_fXb?pqXj9n#x+VLz$n zHi$6*@n@EM#0kew+>zF4e7okG{Q9O0$5N<@jQeNvw_K)}4LVkRg@v0ucd9X z`S5EdF7Exhl_Rx*_TSZgo#w{%m8bWuZg ztgc019ba>K(j%Mbs@W#Lqs+SOlMJ`s9nVMb4!Sqz^sH{byt-+SvcyM3K$#yL$I1yr zsR|pQ#CQ#ufZq0?@RCQR2OJ(gI!B;H28RUcqPXCUHkevPMRuk{OWW-W&pKYSBI_+W zCQjcAb$+fgS30FA`|YT16~|6svb#uM_JvKi8!}jRg*x+#y`FGw^HuGZ1P|ZNt*T?Z zRdQUytx#Lm+>eWI#Z~v2rbwJ>xKZS?+UPo8UDLsGxu!U6Nw*?K1EkcZraa?&zeRww zT|CZ{GghyR+B4)SIay^Mf&o0I+fY%NFP^=QX+Oe_MTo&cQW) zIMhLZ{lebq=Lz|xW*=%?1cbY+Qo@KyB7O5Bs|(s3FfBLCdusWeMi<>#f2AByV{?(7 z92{oT4oX}jswr|aNOdIdJYmfGzPrioEO0;6Eu9UlPV4b ziu|+$=Jy%J{GW4bY|%yCAWUTeI};u)N#S!em3dvFrCMCl8fOt|rtzm!VCM9I(D_l^ z`9oZbguiz0JF!)AkLtQ=`JV|(6HFDxW^s2e?zu%O+a%JOd9*-naV;U~RP5&y%BiiX zJN|sG+MCTc8Zz2iTjdi5gn;PnP2StDYI~k<{5#uGR~#L{XbjY5v7h*d3HHE0-Ux^c z90t2?@fl|EC~p7G_Q)pTGlEQ`|4$u3NJI!b|AOqKWaUH%NmzYY#iQw$%SGpW?q9Aq zjx%`7FyXg$pL+fL!KAM1+Hc9ikO@?nxLGb$?vw{sufb9ZndpLvMC&vl0g0RF6EmN4OVa zQO8iGLiWq%)i-|l?u)|p#%;|CgDKlRhEaD?e@)1@A*a$ErGL0Ci&Z>r&6Fu_q{VA$ zH*VEBHa7~(3A!4F^2^sHk;Zy))QzQF(sO%^Y@BDSsBWPYHlK>~$|{N6Ga2@Cnj+D` zO_y=kzrFa(05ZYzOaf>3o+lD5nbW_UGfbLz@5AuRB0>aiD$my_x!mmLEi}&ct_R3F zCDn*jr5(aB5d8av1skP;(d;?yIQ_I9Ndk`OxI211;j24zx||L^qP_5C!^^kmO5cxp z-?~aiZK5@~$c)DmK8{&$22vJcqO^XlhRt^v4^MdvA+t)fY=oFdNAV=5s{Q?n{qyxU z`6E)^K*d5IwkB@oV6e#_;P<$;ZT&b&kBF*+@fgsCf={6d*HNg zAaWklq8n1v@$B9Gwc9GW+Y=ty$59-MJ6SIk8)Dzxuc9S*icfpwY!9^&>rGI#2WkFi zXk#wxUvryV#uZ;8DP%ikikgW}hi{jHw$ zF5{Q(t)(y62aosDEB%}XM793J-IQ_^)gp5+Aj`Nra3}GW-f3S5%^AjE-^jLtQvW zD3&vgUn7C0sAUlVRQfAOAt+9kT@U z(>AiAH986JWv#QX6ldQ}Dr>b)xl*0KK!L3IG;hT@^QVbO_ySYN8m+F!C*|U7*W(70 z{6!Q3T2d6gXPfHdG1(o~oH+w0_zKpe5!%^%H%>i^GHaK-ixSZ5n>`>T5v>rn+?7vG z!o(4uKzTosm%x^Ai3d3EKMZbbfW7A<1JG|#PU_ARDl*w6uCNd5X!+nmd_ka~uciL| z-XEVGCO7%zLfuEl*RhL=vzA+*UEl`mA>~5B`CD9T{PC!f#w95`ZT*WbJMs<{X8Ayj z;zJe8qyy|)wEi&sw!aB!m+`$O@LqkD;@cJV6IBJ@=?CMH`?u%@evokvXbjRvhb$;T07BT@Lo7B9CYdbH>_BY3NBb3as9>thHxO_39gNd`Im z=4Pj#h>oyaD$ykVA*}IZ)#OCuR@jD_nN74N4ti*wsY%R}iAq`BQCbyE6CS$~*smI& z0Z#bVrZu*jNA$Ryr#Lx9g@q5TvhG-#;d(CVhyCsk-oZ|IP-y97$9%ka&>ED}^XwFZ zKbh8Z&M5C5f~N}9ZY~|2>XJX6*?7d^yL5i67h@;k*kISXE#!xz0xIOC)jSPH{nQwl z*(NbE>n&B0SQL3Dk3hVc#lUzZ)N}Gn-tacxYT`^&NFo%R?%)3WePX$UjH>h7H}-nH z;}^9~Toi3LpXzg`fAJ^&Mb)`vOA4#H>4Wek~NvKFASd6^{u(Tf2U$W_Y!2{z>#NX$6CW=BeeR3h*6jRB02Y# z?k1$gKMv)NTyBxYj%!x@ygn=1De(U9lwC{`uw8G2L?d!|V#?8*$V2KJpHc zzHx8ki#90aR%FNg%y$N!WzAkqXl1J$UTtdC@OyoEh|gi(y~GaY*zTQFd5JoCMjf>) zr?VCH<-`B%uU*k-!R-TsdyZ)J1;p~FjxHVoS3?qb@%O+~Teu`7D#)&OvvodbLu>c@ z!_N*g_mnmZBlwcukR-u$x!Mrqm5{m8xz3ggwMO-vb8d%ph9O9KGWtP!27mYEJ`8l5 z*#iVq`9m}=oMQ3gIaE#&KM1pO!K9E-R!SVC={L=0K@>nQ@Z#*BJzvUrc%%L zG>m|G5<1{=zi51$2{ZFimOxT|O2sEAS$+TSF(fk3+Wh&9bI=@h5#RfGci~oP&#S#AevWICIxCN_hI29A7eF)k2F5yB9dXNR#5NI%o-W!na34b-Q-rjSy9os65X89V#sUFRn+g%>S8Tq$6)qWWB z1fd}I!J)q1f%R>SdjqvX_x^);WjOv+D7cmxWSJpD_K|V>uG=%-W2_t03bLCZhLlQU>Bhz5{~)TYvc&tR78nKRh99XE3tXQ?n?~d851Sx3YSH_-9``3c;64 z%p~OgynWFK1$YUaEC;4!nwK=UEz2A%?wPq^LLLqq{Vn&yt5tWGH=1`BmU`o|1=o}@ z>#&T=$C5eOxzxYVyk|GBXWNd_yZJv$m^vfTf@>18T$!^2`c5N$@%yhblR#;L+u@=6!EAKlovC}?-v zLz$e)a9H9e)$rGuKrUl;_OpGrhB*S%tGkD$=UchcC~MAFSt?GekgMrXvvIIr=A}J) zj@_cvJwCg{+ zB8p?}qKno;M82Y7jy~5#99=5sa6onq9yHV~*F{V`RN`*f^TC1jhOds(pVC#=+r#qy z@KzHT6tO4iYj<><@K2#IHeB0G1aV3{+R*JO@AE{Hy3@FsnFy}c&&29wlbA?e^=Li2 ziDTKSD&#p)OA$28B@#4QRIPMf#Mj|V1V3qX(do38uC5N8$K6)oRHJp4ZTB{DmR!jN zL*)fMwe5E=uYHUI|8r%M|GV(2iz~V`OTX^EB4LdY5@ycGi%7-x7_{Nl>KFbr{9z+Z zt!?4u)a>5L@udY49JAuIoKMw_9}c;vZAq8%sVn-F?~P2qo*_~nO8n0YULh37-ze8f zA+NpaeQ)_vC(l60ewQ6AYvr(88RdR6`hjk#>&*1C8KT@vE!+wxnZuTtG3Rkl2Q0DGId zr9M_}ih<>RBWl$j3J?l>fGA%y$!H=Xm@yRTOb~(MYrkd!iPi%V89cmp{{`VzH9zuf zQ&Ff6F^gF?|a~Sn5cEm3t6J~fM3XY7+V)=nB&sKZ&}$ztRx|{#Rq-_aRwSFldD}#EXPh~z=h1}I#b&- zfr`QK)9yF$Jn**N-J;9&(p5Z7H(R(P9dCasb>Qh2%S5O&QDz`Wq*izqM`$w;PvAxn ze`mL6S8iEHEmPK9Avg;>SEpT;3wic6E5Kpszl}wdIfVR{4<=GoR?X2K+AMZ?WH`B} zs(QurP^zeSCW2_}70qk=!UDtqkq!L+>zno#W{@Q=-R#bP)i`*gyCeA|`^hGP6OSJ` z@n1#4=KlQwui3Jdi6AL`eA~Jzg;X#k;yCUFr``0BH<}3}u*a%}jxu2MqDZF0ikz+3 zQ|o3tA1`dGR2^)#nPyT~`f9A_Mjrpa*aBqlj~0?E5?s5U!PCp^fWPad4`%c92aln~ zuoG~kfJj0`l{A4enUS4~x|!Kok&wrZ9p$k^X?bKi*^ulCZ|;MC=Htj|SG_@$%+8+v zqmueU2G5IyXK;1g%Lv;%h_3DqA$7^~b`szleTIjY=yqfOju}p`3$Loc!enla(GMUW z>Pc#a)8r5na>|Q;Mr4e>VoaGIx^(>Scu5nJs%p+b^p# zT3z&tQ=s!DjqOJXj_G47wSY@pbqwq$7pSeHR3+8Ka_xrwL3wE!br zA4J=KIZE_)#si3l89YzqSUmmQp4k@_41bjEf9RYIkoCD^LJ=KQ>et%ok5QE!x|s=i zSQ;}A+1ky_Es(db9g+qSpOQzf5(V37h)3L!UzWb&ZGRsqkRfA_bFhvQj)=JJch1gT za&g3L%|!C@LS0EyQ zw{mZBN3#^w7t9@H&ZMKv+1It&MJzsHDXw1j-%9<=&;@L?$*k<}jP~r^6kjU{hxv}p zgj5NH;iZPEH~miC1veL04`>~??qV0e%(QKFc!4*!IU*Bzv66p@ME(dbH@?pVCQ4sH zDn?`e8MHe3N)l;d8v6GZbph z+Fjea&!E!>Cz7 zboZ(JGWi=sfF`O}HKlapZjoQ0hpK9?cEHKGnwba-14tzrvFJf$-s0mtU%={^3?8)H z3NjJ3|9kx4je&Z1xn=}S&O5)reMl|2>a>-RqR{Vdbi-*QRxodg@X7{+DJOe3Be=~H z?Jk~K3LH4Ol*a&e*W1o;vQT2Ie@8D(Y{=d)`T=~j8ugzy4SVf~Iz7gn^^EI(6#;+^ z7Txq~s`$fvIaU&ZIhFa{HjH-v!w9T2l6X%B~588?4m93 zew^t;Gp;OsFS3x;#CJK_>1GhtQ3DS1I&Vvww1_odEgg6FatrKrHV05jIKAy=IGL>+ zTxx1K`Kgs#Ay;s%6V9|QlhT~%$jfBXb&$6W{Sb2nQs=ekIa`;VZofdwOd0!@MxyAi z|69Ry;Hxe=ak2P0&zJEk!QqJ|O|*hxzRC7Kr^R~%ev$|Bwo~BpW+D{H5l*ina-PDE zN#t3u+6eOqn%9Z>_13HXGZ8G_0fv(S0P$|B^RV;a6ZM%xRoe`>!7G!Ghdyo``lm8r zMbce?u;l*G#LPF#xY&Xbc1{=(7~V@>nA50*r`!mNe4~8O1)!?bcm>P&R9=C^OhZig zte-gY>@Rc3?sEka`<=%QuU1E{k-Q}>42yWJ5@_sSzFdIuvX0uTS)vjMlNAcX$Cfl5 zBDPQTE$@%x^3O}1xO)m9hyjyD2x&`>=`u@=U2i@+rJYyrAi2+6Bx7U5>L57hoob^?J>&z;4csS% zbI6M1G+oI4M(XEc_!mZ@7vBP5#_(W0+IVVtpRcnrKoHpt z6r#PSvdfnharF>DNldBk1%PeVQ3}xGoH|@lB;E=2jmZ&1Haw8j|+zX_>HG-(<~sV6I|htqA$I3j5zGcCl;u0uG{uM z|Gj~Eeq?~)iGgn^($3%w((v!0_J4@Mw421z@oFjO^B`u7q_veh0A)D&iqj%N(>h9* zaYx%as-AY6f5l5>y>FcJB~4BcBJfZ!tcF1=bW?J#hI3n?OGx~&L-d0RB~>+^$u#il zB}E)1CLWwpsxs>_a9C5uoeB6CXJyRX>Y(};Lk$lVH%#EA1O*xgnFxDFiJw)C z7Aa<1Xw4!Y3%6i&OOD)UK`>Q&EM*YjG4;pe%;f&JA<%Ze5f$8F+{u7_X4!^YH*lRn zewH5|=+UlZsH5`xB;j)|Ag#`Cknh2b7_ebH6J5mJ7u35Kym1hWWKs!p#RJ2zCMI@W z8)K@Snn?Sm>3Ph2(Ek0_M4%qWFwoN>bMq%8m=WC!1dD~b8|I^%w<)rA$(-2y3zuE4 zgwg(FHNkrTX^m;|58YIsvXS_*Y-<`$7D4#>>X>yj($;#`xi z6I|QUbuhvs`%b!lh`_NuF(A?KZVRvjV*~!^m}*=yM{-JCs>&obTJ_b+9!R-f*0)pF z_$QLOEvod`KQe&zkpIY!%(xdpDo=$k6_5(~s3pZ!(I{=+X(mKj;<%-@mzTI!{qTdf#V+vp^O5V=d=79&`&sawRPYJ!v z=w-yXbHX|*AP{W%P>*$XWKs;VYb@6pzQp`mRpMrM2fLQ|?feuUPdCi1`M*_^L5Gt; zN%)ADxlyHM67oBQeO`>>ZC@g}6tQHRvxE5%bod_WGVTO>nU|~Tzd`Q%2I6q8q)CC| z7SR}bH^AhiG1C$&ffI_0a&CJXP9F9v%w-hs1}q=at_bA`nR%ZD3ibdhBV9~&rVPsr zDUMR_`oJ<|)){TKs!^SQrs?lOFMJQ^J*jDSsJ`**4O1t<6@+;NP9+_=J|xFe`#Za5 zo`c3W*r7s>y?++Py3ZDT6x^^+?kLUGE|Il>Mbv?&KMK+q>B5 zfF&I{$<-DJ|!X zrt@b4iBkpw+AgXO6+;-Y-2azC#WH&4Q?G4&g(Ji6vi2d;(e7t~+{3yPdDTA|w{q9N zwvP$h9IeBw?@G3Cn_or!#-GRe?C%Zl6)kZl9-Mz;8K{uy&Ct_@8M!BMWuyUPoXKNv zE3#sZgA~!J*}X6{lWsaoJ6;=S|FNBkdhu5!6HQh^40`Yf{5X?^xj$9r+1%C&@GociCCZHLy$~R^{#>$ z_HY$x(bI8Be)*Fd>`4Z0SVxT=7ev$z*Vms@|HOR} z^sCspkZ_(L1+s{l!(S7h=ce0WFZ?b;oD-1NwKIao_U*awCJZ4eX?;mtn$>N!W8IZ!i3X{K%0 zk*|_euD>=gC~d|azi$f0w1}(XrE*09N!1PeeyZl%)4zO+-tAU*pH92k{pb$pgixzV z6`9uIZqPd0x?u>*<3ps8Zl1x#FKVIOc3RH7j4n0)SF>yqbP?EA@@;3gFg;cEMEH!P zpdzJ;D@~7Z-w&u^U4_RA$^CDLo46Pp9ynSoE;;I72=k@sd+ZfVM2?6gjUcBv(6_x80ZXi8Pwbz5 z#S1y^>D113Xi)S#WN^Of_N@1s6BkCN(V5Z$bZYsT7Ri3>m`)$awJl+RhdCF zsZbz{J*SoP(Qs>8B6te1xyABf4QoJhLa$q0l@@{EQK~0mNt4;JTz$W$^o;${+AcDE zZzR=4WB|Po#4nUp{m5@kRY;^g0J+l0-h~=H$9B?o`GE^9=qrL zqMF4gIpNC1|Mqv(C-4Jo3-U%c6$BA`_Z1)4=m*O18v6T1LfHt`C%Wizq!_%!^7?#v zFH2a^XABb|i@SScXHtIvEproDf%C6qJ}z#U!v8Y7uEV`io5ayaJdt8CDlh+`j@gaw zM--lCK1x(6=jFBr5?sN~MXb-APCW<+z=>pfELWL^;bd6gw2B9n1<%SCNFvuiRR$=q zdP=TDX%@}XEmTjx^2B;NB};)$?e>iZ<&8atu?g}Jtqtg)l&M|%YUk?{O*YBrNZ^Jn z!22jxvMjsZ|4NGB{oI1DXml*ldfy1$5|+Ask$QMO@Cx!i5ff-!XV|&yY#E(4I)$^5 z;}7ct-yFRZ*UB8=sPQe03M@E;Wze#iE26p#fB0tTpFD4u$ZNML4G_n!!w!|PG_VgE zJK_C@(GNI&U+7Z`*e;}$dd5e6)o{`^sOP_88~q#hfd8Yd%;D@^oD9&kCN4hl<^(;} zcXrFkZ0sMiG>ekJtcR3%reT_I8rkQ>w#&~g{#Q2tMI!Fffy~l-#`AAV;yeuwB;jZd zU_`7z9?BkSLaWy-pz0+yi9-*z!%tp3pp&Uv@*-LpOEIfZS3O130A(RLaR5{i+?oW< zh;d*PY<3^_5M%sy$1Cu<&GzEWp_fL!-2}}{MLGJxd|*6Ipgqfh*Up{ES>G+VG}!D7 zdTR zKqe2GxHkLnv#AdCIlFRVPY2k=ae$LZ2;4=CHQ_DoJt76YFKH(BZCSF#7$Pu*el-GY@IZ;GROP949E|>oHec z=kt0mkyEX>(vsEz$r&4)MNN0!)+!(n%QXAy&hWQS;tUSQM<1phetSu+b=aj9vER09 z&S~aYcrdKhlItbByE@lc@6%u^sqrN*!Txv&vy{WH<!rYPQ~4)b$t1 z|CPP%A-wGp#KK?Sk=d~V+7NX_EWLLBcDhg7%>Pn+BHT0xf`wggtg`>fm2jw?^AVTA zBVJ!|tFIuh|8phJL4i-h;})VO?#m0byXxxt*yKR1&A5%E{N^7S#`=33otyza+Do4@ z)9^2SB-upVTSNT}9&NYhIOy*09VqsN1Ga<#1WX47kTX(G_&GK{%Hu++6aNF02Wmj8 zNwml%lq=N4|C-^dU?ohWap;5qt*jR_v?dB+_G0nqS=Xc51hqg>A^w>i+WkPg>b;W zK+sFgqH5TmB^mass4Jjgf5E6`|JHCw9?>&7Jc#oU_QAJ%2qG=}u#t!GZ3wGB8_AbH zZ{+ip$p{01;^5`vKY*IxZrkF}M;19X+^zXjfd1>Qi_#G{2E z1S$J|M(Rll1ZDHtm5U&ix^dB0;2H+_s;L`2zo&Yq?~gBS-#>H>L~kZUblzDHtbfOX z_ZrTjK7d{Vl6G6x=fJ0Ib-KQ}sCv7@+{B|rNZ@@cUO^!8;HTU?=l(o)4F#DTYwzdw+2{Y+t*=tj02Yuk!gh$G8!s#mdf_0{?$iZ zkk)lE4>M)ZRZu0~$FW0rF!mE`K~B``Z@yUoq_GThGeZV_Uf};*9+2!$KR^c4Pa^_u zif^y?gGoED)GeOepU4`eWfQ>(V^=+;(C z*DQ)y^PM+G@zUX8Jrn!cvbcXa^yE3a%|a;Infe48c!|{$HGukZ$d0!C3Kk86O|>^Ld62e#x_av+Ajw5%%X(J52!2M?mzPVMpf5o zCkFm27P#)d=_(;Mzm(>PZ(lS|Fp2lyP4$VydtqL8{vUhq9n{p;wvDGy6eO6Vf|P(1 z#Rez|Ei{#8REmgzQWXpYq=}(O3l@md1Vji`=|!3p=|lxl30;bzMNmL$6zT0-(Q}^X zy#M~@H}lOm^Ug5C0GpkA-RrK`eeJz^KejUKt%rSou{+IwyUZAtXB`MvICPo%ozGKKUs+SaaQ&S`H%3-WX(mp&gpc`jOw$%?XP?_ z;H>AF;U7vjx#Js+=Le8xg4OTx(kWM_Ojy)hLSFaE*AS=_s~q@iMGJ%pLv+a(dnb<^ zg>H3tf5&%X!w$c{D*eDJx_1w6Q&}a|qr4x`ESp6GMVG5TX3HB!^N7>@)~}9eJR7uV zpKoeuWxn>EQ=57_J@+k>dtBCc@g9F0C6P9qZ~GE9!|CS_5FJx#zsb-1uJcmA6xWh= z)FPD#Wan-Gvj4M+$BVM6$+$(kPc8+Qp3)ECN@D?R#JwW8s2XFHK7ENg0&FBf0D!+! zf&QzmQ-Q*Fjrr}t_;d0uh}d9_T=HQ-snV@*{n?RTkn7Lqa5xpF>=%*^9(-vDOhATX z3m&h?o^P^wr~Bh{-`1mJeFYlKfkZH1Ph&I&JxTl`vHT+NS95Q{!~@>7WFc!0>U?`j z9L=TcDFd9JBo!y?&*s(!%t(zLDCiQy$M~MQF2~Yr_~%Zjk@=r2%RVW3?bp$~*zWd- zxu_}N3QjHJ^DxnHmy1po@LKS5?&NM9S_*_fur&6Sqi$}uBaQ6=EPKAxD&7z>9~tp@ zuguSc33V`-dKI2*qFozky z&fGiIjKb>#S@S|tJE#Y-*fU0zi=W2Ko>x&zHV6FY=6+Qx!j6$dm*?kehSW|f)DOt2 zPl~RWk*3Hw4>s4~LAn>6LzI)Y7DocDJla#Xtuc*Kb3<;8QZ83sFO0X>tR4)koUi8G zUUEg1_$-XCc3P^Gd;w9`b#3Zv^`<(py-p*8zE_^h4;sF=@i$6%#BiJZkl(OwYI>*= zW(ouLvkc%~zLgUJ77loe3+11iO~ubiM`wHn5J4)N9~)<3rt=f+6qvo1*RJNF3BBfd4Ee5=jcwfg(9nB|Prx{1F-)MSt~B%K;3_AFExDSiuBr=Y3xkVep8I9W1{DS~>u9B75?uwht-k<|Up?Aij8C z4w@AJ!sPPx>T=FJ|BP|Uzgn>A=eUrxymf1+bAIz1cFShzMYwGL#OXebAIp4u*V~gz zy_XCd>RYeY{OoI}T78*Xl)U`iH;~v^o!is9@7dckEohP|Qm2V;a%)9xx&8VB)M8uzCq$UD6) zYRTOf)9|5hxa5ms6K`O-`|xr=o%^=y#47YsqB-&Y3&34C@TYc^2O9{+quXIq52w4+ zd^4~hIReNeb%&pT+3unAcL^}7#Ej1&X_}HfV7M&`NB{?!K6$os8NC3}={yyqu=@o} z=3&HRV&N#WBn!>VOV-|u-0d3`RDb9`EN}D8;-WO^}7omA|BH} zUM>flX`~05CnU2WD7NIFksA0k38oUGVvxrBAf?}!{~+*7>ynKQ-cTscte1>AK;ICxd>1ng-_XzrZM))zqKc6{D#|h z;}!wA$dO?XbUxiY1CrhSVh>dPX$ED0P15-Ti1BTYz?it;Z*2fL2*>z9P0Y3WaJ2j? z=!ar3zU)kxpONXnE2uYn&q*t%bX!RlJ7GE~`;tH6$3n9^ZSAjAuhuTV1@AFQ4UNWxx0K~wKpP4}b4)?h$i2yEynXueqNm%a8k{&iDeY#$|CQw)fMjV~ufjsQ`;Ad&E4 z5q7XfK~dKRw$90?@GW(^B_T?N5f_Gn(=^M?RzzP zAzR8WRXH4*GoV5OS9&Z+7dFrSh#}0=tizb4zFi9h>;MUX5u|2(rciBjgIzN(c2>@+ zgYpM}9eJ5VklD@`kbz0jeuvoHFKhhI+BifI7Jm(;6D~ve;gMiGi333*3QUkM6@U+? zWPBE&N|0oAmK}8b!@!^}NwU>Xz9{24(D5C{MXIvW&4P2bk;TB^PM+W!I!PiLTW=pE z_|Ab(x$4N;Q2d__l>pgtA1*2~DO$2*MFAPw+w2Z7f2qdsB4H_$YrgEZhz zwU-^PTJop@CyN8!>`|u@SRND;;hteXdco~Z+o5xT|ZjbzN* zmk#MH`poXcB`7x@JYoTQvWq-dw~x3H+#>M9dwY=iMUX9!eI~%oaMAeiK_;koRT(|N zbmL)dAj2D5@39?=p(acsa&OG}`bqv%|Lg$!eH2%E9yo&_QRVE2Zp1x9j~5(PoT_QtKGM~?(OapKtw6`1XH`Qf?XBD$xh5#z z2e&ukvC}w$D+T6nE`*ZjvErIxQ67MM90T=iPYM9MzErz_I&ZHCKQ83yQ-X^hjonA;7z;!3>?{g2o3Mb_un>WPHviA(#kisAwxuh>2%_j!O+x(Se|YDV)st4w4G!Y_kq2vz%FCP!L!! z%@k}%#h(Vv5hNl7P?3XgBAlv!RYJ@}lX`SoNFF3jpq*RMc+=_zcJBIXTW;E>c3M(VcBC@F%%5D1$z=y6-+9c z!K#;leSf=Fymj(;-ys6%0tz%HAo+Jo=~D`Mkx?2Kx@LZXF#8Y@ol(~#dcvQVX_zM+ zs>mTlFn1#HLJgkXNm>e)Y<*g8vw!{ftYqM>#vWYvTZy`iFIV8KflIk1P!2H8Bl@gp z$o)~g0iwj^eU!PE;ouiU9qvE1codwE3|LS%_1q#o#c-uHL9f7oKhju~Gy78A+R%KW zecPS9$QWLTOLa8nb}ZocvYBvDGy?xCvkvibSO_epi?;alvi zYEY}b+6eH%`01{bBU>P5`{&4qH=jUOMGCb*@{t|V8HbJ69QAhT+3Wv8Bc zbXft5T##~F9`eh(*04&7E3Zg-*_m0@D;>)FNpz}VPdxw7%S5ic^j^Qtl#C$&RW*c; z6ye@78K89U0n?g+KOF+$0GLT;R_|W4%bS%7>5K?9n$D3Cx8(K9-yXY+kS)7({e<R7#Haw)z`ml;!+hfmoL!-+qI2YKIoT23VN23ob`6D<27hyz(?Bo>sB)Zs zl7ux@0o}|p;}=9Fw;i+lD}txYp6;WfHU}ctmS4eNRa&|)FTUC*!-?3XFVJWUJQjfJ zxmN%hRYFw+hz4@`E6@)s7X7WB;9nsJ5R9!ask%3RjBSp6BsrS54;k}6cH!L04h&Q& z23aB8oglam^i1uWZ#5#HUp>|kKW>#_$urov`BqI4l0S6oAnt9CXodQH$CU{r7$W6z z6eq68iwi>T%t1`BpU`Sxf}D;Gt~gQ;iiT9sJUW6WuvLUsl#5+&hhoxP*b&M5 zg(Bx3-~XLfqhSE4N}c-fYL$9-YxAjw6-3nmbO|!W$o{e(=jp^i7Rs+wk9Z1ke> ziNVZK!C&TT)a>#D)gfm?k`!>|pB*o6Z%LVNE;!0oo-?XjjI^{oQ@(RD z3XH+ZNb91yJWdB3_VgTR50;+myU1his@&nhdEz^Bk_P6&)5gs|6`DLitsx2G6dFME z2C!@$ud@9$0efGs6v>HzNcT@E zh^`E6MNZt>?u+pCVKCd)q_+low_WO*pY@)<@>RF%#kJH}DnG#YoRqNBi4+Qc z8fyPtH{hZjGXkT2&28yLXAn9^(mX~Uw^^3R3%*3NsEqZA><4EvThief$i-e@4Lra| z=lUAa$t;R4>#Iu!G;+JWuK~+efQSO>*QcAT3_4?zo|mEKslqG*{~*F*e*U>UTBn;p zO=1D|02!l$x=!NDc|frsD?5Qa2c}3FoPSzg*3}tEker1X#J}sxuJ@J4v8M#ZOrdeW zCg|Ylg1>Jm8J;o$17y@)jGrK!18xsS?kO}Mpy)_Lh`oMTxUSb(I^uV!{99r{u^UnF zq!7;B|lnzFtPhQ45YBpLo9KM0_;8Noj5K-|kf zT+ad=@7jMh1Ofbk-&+sdI*YUb_Bp)pVTn7b0XU4{*INwM@;oE|C3G-9d|e0V4zOiR zfL;ZC)EHpckqa$^rO2ODu+j=SOIQ1^ET7z1$`kmTlNDaS^)wZ5@iG$DIhenIB3TT4 z&(#0kU4CRI^$Sp*8HWWRkmsVn>Wb3nR^lw;ra|IpBM6s&}JzX7_myG_neQ*>GwZw2(--?jqk zPF`T&kjg#rq9BDRKzTvYG6!&?SumG~gl`?I*5rD*Q^_BM?>FGLzU@5!F-rNq7D8Jw zFI7r-DQYhPTp&dC1)7572Wb%dbp=uXS~TGPwR5zQ=KyF4`gFG1os}oCX9UxnNV=di zu@(g?TKhAKWjT=%T5aENV75<^FwX9Y`vP>T2x*7)f(}HJDZ+RNl;F}IqeFYqny3gU zzke?z0}4RX%Zx_QMyejem0E#u_z>Wzr0noxkCSx34DGrNYKWpxL=*M!OOIM(NK&0pj%t?iSgFQd2x( zXTABEZlJmr>%vs-@*V;ze#FAq+5wsE?|9(&7_h3@=h((|Ud?1JQt!oJ%=uB`xB&px z+QUWJt%QffXP@^_1*iwXvO1-sgB$;);T9(cmWH)K!<<(Nb~_hLw76^#%-YG6;DH?$ z+=Ij0KREJ6`Xts~ZH{*7bV<6&Fo%INH1wX69t-|YND>5;(!DDu0Ux{tA_B7WEO3#s zV*p2ISLaogsLs5`#SjNCwIu=x5gR|0^4S@Py+TT26~%a z|BW+_WB`3|`hf>9AfWSEDg(ieZV6yR1T%mU+bPZCj$}RsLhrk`^+vyl+fA6s#W1!R zz8xH%V@~pdrGk;UY?`fP)4$?j@Zr#nL~03$hpZOV6nJabfwg;w1NG$1p3j2wa2%3wOz-n3GuH(_r15g?7p??=WuKjC>DR=ef;Zytnes^_!~I+D5JavP?Jt zAl(^FEcpPVkCpW@>cGS^5~j{Ayg$i^zl9_P?pKuj!K>>#kssm;+4?J9BW6B7*aVsi zQ&$JJ#cHY7{$UUjf{a z-^h90N9}~Y*}NiEkbyQ9$}C*$m%-J%{crMd1k4+7U;~Rw?*%$QPv8W4*`T)I2!nH{ zo5L)4Rej>$s++Ue2oP5eD}9e(Yp)w{x1CHJ;}KAxc%`n<+kdSWRIBQ7jx`3CWKI}B zlR^B{4msO(8_bK=FY;^x%eg@=%%;zj%3w198Ei-GL&pK5J&IdVmP`Y99Ns9q50vQfMF%^j(KFb*R`oA`1Pmo9!m^{d9ujSWwdcM_dk4VKFi;4MQw_hKjjmSKi884nmexmb;ES&Mg=v>rM z1Mx>?j{Z3YJ&wM@fxTDH7nGmPJs0(;eRPkyn9+G8^5T(OyN_g^J<@bmSvdH^2fIqS z@AHx66`va)&&<@#6AOq%%TD7dzUdLGkmb4tC;z_1IvLh(!>_+)J-GRwFU$eH-%tMj z=Qsa*!5>xpU(v()eE9EjtU8ub1W&n=oGy_{CTrv(#Qxr~&<}Timp+22sW3!7qBF|% zGUwlpp3yXZmIb!{=ZkP^20U*f5li8R%D!mpEGMtX_6Ypz&tj}U|Lb}lGW&lDVAk)u zNM`?g!T;*vKNj-;s6D*znu0`U2qM9edD=b7|FwTyO*7(u?HxG!{|CQ6UVvah=iuvm z$&K-IHtfY=cn#Mj6{^InQ_G7~MV-d+x$bk(?+&5kAOl60ORdk73NX{3X8I(kcrcef zzx)AosllzRScY+=RB`q1G1D$pmS3O;+~Z1U&86_PFxBuxQ;wUks2gmMu0| zzhkjpuh?qu5xL~QZ(*h@nGy-%?Xuuh{j}vL%XFPxucN9_hpZ$30nl#TiYk~ubSzq0 z9I@Z5l^Q5aHR&A8m-*E`xJ925V{9!gzFqQ%yNR||6SvQ7Utm2$Sju9(>18h6{w!z{ zn81^l>Nv8T=|g^5?Jw;cwjKIlYZc9FD)Q{}ljO(8c$H~vQp4nqOGER zh&Jthb6t22w{XgJ+`sAmf~r<2?#}j$Wzi1RnHp53gt$nG9S1I9Z(rb|9)1og3=`h9 zvCHk$Iz7^d@5fBmjhi)F{geWV55JpG>mt=F$UMk=rt;az546mi>uodW>4{VubLjBQ z_1bT$)n>QHCkk#Jt53B&zLt)#xM?mc!nS4GYu#QuN383RyfGd?4WeQh_JqRcy&W$u za?3C%A>U@57A>}#or5}t${yrXmmPXb%q2$aKWIJ4I*$ipCnp{mPh4GXC@Fa6+VdjS z^ylPThtfXktcp+TMlFG}h;rsv2x+@}OVwk$4BF3-&yj3x4O|7sAhu8K0`{};lyb~L zAqN2qh`^^i1}5z3);#P|ABWeRQedc1Uy%mHuR9Tr&OW6L#yO|fHJ3W4-{xu6`OtCm zne-N5Y1E-AKkRwZErABid^3z|WPwa8uRl#tYMQcSw^g2{Sd3u5ZNBY-$ zRi|DFIYbJ4{yDpPX!8h;{_eNA@1I=>*CZ(920Y*Nk=~>~w%nCSP)IL?Z$T@2!v_Q2 z)5nJ|?F!OQ}g_64v83;5TV*Vb($VwMSVp;7!FQ} z#AciXpC2u`Xo8)MrO_>>Y2ttx#eZ__5qJBa(ef$_>+6H@&|o*|U!?Vf}LvBv98wWBn}P zd^gK^xF#U7IbjBN=C8Wdj(IH7Brr}RH0Y!0T7_5)2dmJ{7?JJ ztPi7kA#H5>nLd?isKg^%tB08-WbYiMNS$Y>%Vi?q30Ut0eRV$UMrA8zh}Q*D&oRg- zUn?%RRMSs8a!M`u$L-|gjBsi!HQQm%-m>{qmFLV?D26@Xz;T54^_!H{YEdb)MxEO}_V)JAkxv~a(|C5w+RMSZe?@B}_K_$Ox zfX#1*x3AWq6$*cr9@C%;H*gx^ZRCW0MM5vK#;vV=2%~++I_-1mOYSr6<$Z?PoqL}b zHv9JD5{_KoJfVloZ|c2z+u6N62s1Jv)xCFF_&&Q|%u9hvjukdN&K^#uv9n#H#SHt8 zH^$dV9(f*ns};mkOnxsUjx+{-?q}QJH3)W?p$mOZchI*M3C)(}Z!uwTF?f*f=EQjQ zkF*;%*Jn_=r9o*fLof>tKWca5;_;P>aTeQB?-m1LatjWOOpN88r@dJe!B z)rD9PTI%E4v%Y^tkHaHkiet0P0<*Z^@I*kSLzb+TBoknt#so&l)$08hJX#lDQDc!M ziCO%*jXphM8CD$%^`(X$mFrgKc@Mn44GJGDg^-+IkLOx9uro`*b=czi` z^_FT%YVWWpT~D4h!_RTn?(l{dS1UzSe1=$Pc|^Jg68A5fs@(c%$k@Tion0T!eE!Q! z0h-FTbx6GMNSipWruA+r@;%M<#4z$4B#;kT1lo%CH2&_SDWPz+2P#Ml)0i>cy$i=#fJwq4V+b?3;zaBS6r3?gl!Q$u?qb@@; zCK?9ZL9gaRNp&jzNwl*==#6pHAc=gBy_M?X0lC=52)l7t=zL*Y=W{9DM!g=ftjoS@ zGnfRv=@7o=i(~B)qnCTc4oRbVpB=GMh4(ioMU2loGG>wPH;9iza2bbxs%AwpyBGnr;_eql(JQ#5+1=MaM%)1+a|yf!)dfwqUFC^vVn zMw5tQW|1qavE=G*z7^q}swk!4ajW;@b>Dv+VD@3wjd@K%qB4?881`&*<%aFHO7lQ< zw7B{e@WwKFcl|ck@!U2e293O8NF<|57`og&5mPF$S^c9s-mRkMOI@`aWsO7P?=NX> zeWrSl-aZ<0a39h~&y8)WpNbde4k+7QxLv1MKD~XWoELA!Q_Ep?MOb%WaA$3=f8vji zv_;HFON_go063%Vd{C(PXvFC8d$APYv1|R$Jy!~|k8D)sVaa^fDrrt?%v}kv)I~D} znnQ?yy_DkmB{HM_Cx;`O_$ZPQJtf@KmXA1~Z=2NMF_$fHIW1uSn7v!)jq#5()nn*} z9UCOP=W5hSyD92DO+s$Lm7wlB-*`QhP!^H3e{n=^A@D{PuU)2y+h$)!Ie6HBl9e9j zbiQGR*H0YX(C^99eb#RL3uW~nW14!TGHX|>2z4Gd(n6E9AOAr4-tboWBTWLmAVkeB z?zg`x4D~yLZg@)ZbWDIzssi!#N@xx44Qqg@Zl_i8zU;RbHh~qv>r*L3PD01e8Wnj% z)kXNB=J}d?v4yiI(y{g?oQH3f6nv1E#_zkjvAc4Yl8CEIPY}(5u5P!5V9F=7skemM z5`LO=t3KHbz}I8Zn_e>eSd!luU!!d(wB{`M=Pfv7M{w3BQe^WHX&*k)?x7dZR7DWl z793tbg*12VE!=RzZ=D{vsHgIn;)Grp!#sfVJ-Co9L2|Vl=kz~x@;IgHI6AV0=7k^E z=-wM@y(5XtK4$xh%ljD9fU^2x>I8VP2fKy@66w`VGZo1{NL|R+47-dMp(+{V-MnTo z_7d|z&LfiMcLg61=lp=rWiWRSrAi!az0)ssTx{WWQob% zUnCxrmo*|l)vzB=oZ;%So~>*DOj+H@*oIictaQ4A4C%vyjC8PdDwobpadQW}`}SrE z($rRfIC0%}c5b1HT$zo%j}cYbpLpBGT{8UaK<7jgwE^Oq{TcIg>G}7os|FMw6XJe? zVAYn>RlD&Y7+z!RU|(xZq7pNb;-f>{Ep1^w3Eu3XztjH9vXMj#MHeyx-GUS|Z7rU5 zhmqFw*Od)<%h+>d+h`5&t$SH}z8>js()I8X*0cW@XUrKT=~}2GDPG7nm)CaDp~17; zU`V9^dn&4g|MOzh^sv6&_)nVOY5bO}aAJly#fOV&fN9WNmWfCYxXNwulrqvrdz<(6 ztmed85uqy}WrW)g$!wfu7QocwDS>0&&%S&FVPL*1>^1Xro=)bhf|pq9icF12_yCrX zPw@$Ix(bTKA->fOICCDeuHVe4yxux;j&ZkQpxvtHnq+t!uX?ln4Ev6W%bfcYX{vjb zo?&%^RK!Q8rgnE(lx1HXhHVHjtr*Wn&k8=LStL@d+0;*8>JXd#_?)UJxA2B$aT%o} zDA*UM2mU!8f1!gruhgMaO>_m1FU&=xRXOQqfYd3{oaS&}Zee!<@q5#Xuuk|v>UoFi zyTYJc*hRn2MAJ93K!MKVc@hYwT*>j2d|-UE?>*2* zVRe=I@W&KRyYUgoHdn0(b2iBzG2 z8{@-N(;#$SY0jkSuD)!r-DWmsTfeI zWYDr~Tgh4dZGlhomdvU4a^0E=(bz9KBRjMk^{C-J%3P=pzBGrQZ7&dpU++#qx^B*f z&r6Y9;Zi0OQ5ieU4bVTw7*hv~4gymttte&;P`~tCF&;0VtUjIE?PxcCET{b|XIn@q6B{W>bkJZpxx_n$S!~PC$}M;^L`nW& z@q^Ro1yD-8dx5h4b%imjbkqpImf6riU9_iRLQyORb=t}jl`${3P)XZ3+PcVixRP{f zlekN-Gg3n2pxzViFW&yUyq{sUo?k+DuNK4*g~w_;QNAaW?;JqKRS2(u8fHFLmB8THAK08*lDn;I6c;40(oyV4qT)f?RF*mBY>O;3=yEe6>i^0ap({zyjX`eR704xfvsGqbb)WB}UIDen;QnTxAzlkB z)EO1|#c3Pz_toC$O99GQ* zAtF@b`g!7x@JqgV+kT8`B{blmnsfPe=xZeC6mAQKEohvX%MdfAN&09{L!> zo`P=R3|U>b&A9wj(aU)VaY8jH7;hon_qo{mDg~m0dL-+xSHJ!8qGy{2&r(Y8nDSdXJX~ zMN`S#J~~1=?o0oH*7jkH<>u4NoC4J}3z%O#{zVw(Dy0gsa=7q(-ohHKjA@jBkg^{K zW+kb|%N@YxwzD`FnD|H`;=~P>Z&T;g0eIJPg|fOawL3aPG!EEr`-%E{l2nCY&(spEomU~5Xi{iGjp3(p=TlmLKVd{m4f!HOB3DP0|~8{>|Q z_$gr&U|?KDfwzUuR&dLCG_Y>(Ea$y&^@%s%n3MDRbFz0O_Wn4HXb_FDWsg{5 zwd1;+{&(@e(#p(a%BWt;H^v`Om3x#k94V`Z7%Nl4UB`W4sjakF5c{S3KT~|nh&ym^ zC+-B^I?VW#~WcqF;&51 zyP8eTUApFvr{nay?wT>vKdW;LTBFh)+t%@oW8N*R+@NXS0_sr)NegvWdjz=k~o#02{a2!uzl~cP^G`zZQ7@fC){Zvfu?RUpEfA8vdgAV zGu&!`I5z|IEBpecKGT1Wz(l-iiOhJwJacNGW=H>3RNDK$fM3ea9;wpI^SC`TO6~{2 zJQ`R|EL8|>ch`YyneVwr!nrb#yYov_#kp|a{WwLhOQ%Xxo`m12@cB7i#Qs=Xz zmf;Tgbr|W+%dDCbe$ZAT9$@vZcgUWbIR|V@l5nx^0D76_g|<-?&TH#;i1Qn?vh+{s zb^Da^0K~D!m8}=3-m^O~$>IRda0#WV5PLu4+RNh8DstnOcCEuI+6MfyFLO@z??bw~ z0t<;Q%Q1n;zF^((G39(vL#9BfB#|HwV#RL%D2f!Us1O+L>GfxevB1=P%sy16em~ni zn%M)DK3XvJYW>_tj|3l_EHz0P(zY zt-uqrngbF!=7HC+)cJ6)>tcDq(~UIMav&qS@-eh!h~%t->*OypnLLEyWFu`5#0FBB zb5h?d@<;6N<%=&t^$UTp-N(^=5Z9cx=NHYnY74MhytVvpwG=kH@sm_{ASnBtnkHD+ zvjUiY9zu-Z4gAZtYHz z;D@U3dRpP*L4**Hvk%iRWr~?|BpI@92nG+&wjPla(F+_l{=}!^6VN_mmQ`E8lCA=8 zwUxOWBT2ZBvU5WlSV4lEEJrUuGUD}EAph*N7-PG9d-YHPCwP8xn_fS!+5Nz)VQJ!q zC8x+U4LdDg4T_Jdn-Pc*vT_S{HkL#LA*xOiJtBe>%DZM*Y)pl&zcxsFMs)c3t%L`{}hAy1vd})v%}VB5M09jB&I5cr+P!qgL06 z-ybXN%~@tNqpzyRS5{BXwtkMkddd|7z>YJa*KtG$uFGcQ zD8OX9?%o(rq2}j#{GypKJg5WWqicm*9Tm?e+k-UPX_}-scy_0-w_xcWIS)$~uxV$+ z!g=2u!VUCq+AR-p44&$KIJY*mSto!@3~I$ZI1_mU9oKO?SLLsj%q9PZRW^~5%hdBE z4OTf|74Zh3@E%|98MHQOQ$f9}jbJr(n!h?8hD|?dYMT9i78lLuc#OH*jn|FeO~sAd zZBOg|NJ@8FAsT4FSt z^(@dikA@djK0dx~rd>?c0xT)xOYz%C&m-b2)4IQk8nQm? zB-~9_A^8RcI*rU{5(rxiKRUsFTm<9XwhX3M@D#|50s84RjSUOEH^Y>wL*jFVKyRll zwzK`OYYU@$ENU_E~&Xol!+u)n^Q0#^CQJXJ~%TS~p)HUiQm*XfW7&ly$D} zaT|Hj$C2QBH^iYgb=EqvV@jQgO(sZJY$ z#C|dW@K)xaUMb~{C=;F_)Whk3%}|A?4#u^v(=0w#garD+e9MX`soO#WE(!KlzMX?+ z`Jxpas%14hR}SgfrwmAyTa{^_;e~q5O*Xh{Tnd4KnG>`Z?j#j;<9q2`X+lL}bQrgBZ5Ew=j|3;YW1@IoP#RO^#eFZVzrTVDZN4ko ziqo=4rg(KEiHj!2Vq%#9D*65}Mxc-96MGM|EJw9Y-Y*bo~tIJef0c*vmIJq0_ z8kDN1S&wnt3p-lNm*Rr*NkS573n#s2*Om_q5XD`$)kI;c$@t^5L6-L5xPfA!rlkr< zAgs1PwyDbliCl)rexxbFU0vW~UP4Z@GR)uJv%RCYDFZQUTrvvt}!dAe)CS%v5-eEm%@(=rJbQT}d-S)A13j z*fK0hvM(^WW-FC_<`OEcW0NY&`vYpO0fnpt&Ow8oBF zevO5Jb=1b7L1HUa9=w0**Z}dFDc$AFJm>I;XO2oNesAjfagSw5&>~b(1aBuPzTISPLCfGFKnlQCf@+< z>2f?p(?c$RNl%MJ=Xva+@g*SC1G)X~4;yvLWcaXYjdJM(nHTf)87<IrBPPX~e9Hs@)howM)%Qr8Pbt z3=zg8MnPTKW2m?^Aue9O4>=C!XC`m_u+K;0^7 z533k>TS|Cz;N9pIin6qJr)(2!zUt27ETOJ3@ljKWQQPj@2D_23bQ?u=8)Je}GmgBk zed<2Uo%GRrNx=)hhe;!QkCp9Rr;*8e$R8?H55l`2J569x092|Sy5Qk94J`IE(!J^N zBmWA1q^WM>c80)n^~wl3N+kzmjcZ_zQ8Nu%>j!NKX?m%v*gEW~5cLxol%521pBio% z7Ra?>p#^eRt(dq=9-^n5TzO1fKbyeo=TA&Y@wlfQ?K$EK8uFJrSHPcYuhhZ@VR&)S_`+rk@`o<+$0_17TqxOBVpLXQlt4A;*c&`#CY*o1 zy%5m>=k2=veEl}@G42t0xjU@fw-|fgAdFxgc-4Y)2!(-8&F}&*K=HjD#@74_+56?q)!IDxy?~B4h`V$=N!Mps6@?M7%<$F%{l$s{4VtMq6Pwh$A4*Hpg>| zy}NS`nZPqpve8!A%L(3Du@6rd(U1{O;YarEo%o*VdGDJ+kQDr$sD>OgvIx*@;2wM@ z-o@ku3L`=L8UKvGb1yFJ^$$vwE|JWBhX9V)S>M6DU(wP!K6Sz$Ob9B*k^_RXPK{b# zu(p3v61kA@{fuIsZ#}s(Cab&slNXcz;09&{n(HM#>Y1RH+mc`Ut78kOYH`F&WZ22w ze^+G#Rx2tU?V(2`i`zP`E03os-~_96!85fDCkmV+r81>G{kcv&>Q1S-)&PiAm=o=} z2t$TBH8D@~f|1ruh!yCXCC&%!$=9Tr;zfcba9&^a83IW6J6d5xYY%<#(RXC;BjAMz z7Z~)>v!UPm06>sHm$~iH$)~(|RIjr;Ub#!ZZXYsHd{i+g6i63_3Xq%y7%SZwS@PhA zO!axvLZ0SrFu#K!a@#tic&JkOnhw`3mNUMCgeZk^UTqH}15%`58rgfdH}*s;ipw(& zgmH}SER?d?ql~TCsZ=_2jN*uNKceT}y?;Z8NMcudg+0Y<2~#~OURa2blSKYFtL1lC zJVginbJ{>)Qmlh!!69s!oZjJ-UCTWrqum%zMF-_t7kQ1o_xzRj$sFk}(pp{cZVdFf zQyQXvj`cFvYx$ce?8f`+Rls~{vcvaX@x^1fOLlNDP?VAwT?P!AsZ*ML5enXin6XpG z)vf_bZtD=C2g+4@!*1;2up#Q11gQLfPVKJAmv-Dgw#+QK$eNzLxD$0xfbGs0?J7-$AiZVgTyxO~r~d zcIjwxCI5;AF+zMDA_aPTz0pqxR#U)C;78~b?BynD7JD{yfA%8T7t-G}pnQ%THpRB@+DK)#y18Z7qzbh); z2oeleIQ|7NA1+}-76rBfeP42M_$@}NB@ipV^>S7xeJ7b5LQr@o`l781?h9^`QQtx% zQeT7pT}p`ZWCDPouVi#`vK4I2xnF{lNVD@b==tqnwLWmFu@f6h~(;%y>&>x8%h z(7|&Lg3hgoPO}DX!FW%DDGFw2?HvvD2{m@FBgYGpeTOE5s&H5_bo0xO2tra{d}#l> zH$(%$LTRdX7f_ciw|{k~C((}p&W!|-H`;UdD`>>mO|<-8V6`3~XE{;UC>wyh9S!~* z>rgVprK%<_l7o=rLzbKrAKr&FfCNyoj&1agnrU>+eTn#XV(jLtuG=~J2x}CNBL2e=s`l9A2auXpi@wz5o5JS4EwwDuaXuAgB~#){rNOWO1rT| z;*AR$6-wrXroL#qNbB4WUV7eOf_i4_T7WRTxoiFOdRcOQ@Tiszu$$|A>w81V`QoE} z;-lkJ$4iATqn^DN5>HcnV5;_upK947_DI+gb&Ag8$`0Criw>7r)@1)Wl5fiB-N4q* z!6``0Q;IHh-DG0&{eFdG2MG=$5D?ISEwIP|gQ5H9Oeus!u1s}*b9A^VM_KO_azC|Qey{GMC0W4h3Yym^pMb&~q@343OCRQG(T-nVW zqz_$V&c~j*>z1guN}rpr$BE)p$SzJq$io8Y>eo6UoL@FeceQslwj{;zf?;t zj=ZG;ckLSx-?P{};2@F}*4f`|d$y}E3>c8kJ6|TjSF{Mx5d-flWmR8QxtHtC>cEH%t zjfxrQ{nkCHWJ4s9Bf_d2^rF|k;u$#PwZ3u@M}rI27Rl-FcZ>W2XJd(lt6j}$k+{Aq z{l)kw_l0e4`;lRRt4vZ-uhcWA?ARt*K2DM+Fn56E02Ms4C_32TMh0Z^@@qyWR)B(Do1 zzPrfH)g~~*nseZuYd)dxsjGVb5Fr~tU)QUI`wao~^(lPuBkdcgN|dp3z1QV&Q?X>F zSN;weBA_D=I?4C(Dlogq*-7A#)u7-kKy;#0S$fYl=Ip1?EVy43v|Y40#!OqfR`<&+ z#TdAU^+U2RK!7`Z2?5z*A9h#w;Re=?!5RTlK|caG7Ke2#cJjIz;tPuqCGYjwOfH8^ zQvIpf`I^ffG}{mOCcF8$T>XF{`vz6!jZSwi`k;0s?MO7_z~ zZtVfJmJPRF6nK9)T}`oD)lOiK3V>`707s$@1D*feJ*Kj$VQLCI?^LRaiYPEnsyQIc z5qB&Oi-k?EHixrHLo#$7GgdPX=(3u2UK%)hTfB^SPdIBhaAjwb3F>-O5)1&9ok0e= zjs7g>6-*%ab6Hp?Yes+K4gt7sN9?R%j_C3-urKD5DTqp!r-{QpoSd8^V3Q> z*i+5*W+c)&1q}LA$Wdj+zZsZZiPU!)>L{0lc$h@G=N(pSb;J@4W06A(K@Ao;6se z;V>+LUd~7SpZ31{tEu#R_aG`(&`}YkjT*rcm7M)8* z(-TAxFghp<2k8nzC;f22Y21IKK$T{fpyON?sxBJ zKl|A`Ik}RK!{gNB$GXfFe+yGjb+|dKs8@7lz4wOk-@zv`2fuw%8NJiC1ir>;8S%XD z`tNGvPlgnZH^9&x5dGeI*mnB~^H>!U+VADQjOh93)CYf!3B;ZZlDG}zHAG>!CAZ$@ zWTS>(FYNW;no*&n2V6MHgD1d6W$v9ThUnGrvW6ATzWGzxwAQL~n7=aVyrP5Qr^^fK zNoxW^jzzwdybq0wOJ`o}i-pGiHqxG!w)Mu0@s=S(ay$kM_`a}lj$r>byurUMXuY?o z--@->TdSL@0;}cj>}Lt0V2wXr3l8LAl@#;gty{0|e{U#M9{kmg!s!$m;UwL>qVHMA_Zxr7Q_v8_p$XtOEbcJ-B+4E zVD@B7Ygi@}MSIr=DZ8zQZ*Bfl9KuViNYo$$(aTNk`ok|*wkxc+zWibT9&t3jn#`QO zcvdO$)>>#D?cc8D+#h5jJUTU>A?faFq8O~T1o}+xAI|}bNz-3>uRs2#y1pdw`^L%< zOU1(vrDPtHKHz2N{&3jtaLUDQ&D;^cxFtYd<+nTRk_oSV58>3yXxV1b(BO~u<3Iy= zalNN7Xnv#V`iUV*w!x)ULH;7zDHqRY9go`&=NozaabeMLn8?!H&bRCBsl^plY$tv7 zKMb30=eqJeU5i%-$GP?gulI(IYHR$eU{FVlv7>+do;pKon(~B6p&OncAE!5JLAH5n z2s3aD_^vfVwUE*&ue1Ku--Ndc$v|Qx$CQ4Th{L7t@VMBxZFG3xkK#=V$cXx9;{S2p4!_vdF35#|{wt#bxLIUwZ9(PxPj<*Z)}L4fqsn!o4fd z)hu6TBNMn5rWKV22}!t1FIp`C;3P~I4f6Z>$gKML%lp0#*^4rVkgZ8&Jp=nv^)e() zo`hhK73XuzV{xP~=Zcu(E8D)}r*-#h3PB^p!bU2^sdt=n3sT~5=*QCfuFX2qFo!Jn zP~E24Uf1;VhkKffM_jr&1xp@hTr-q*|3m!Qub2YxO$#2l`FS_+_dixgw>I-TS)dj0 zF*t|nbH`pD5YJ2>v;yz=vi-~61iIe4TaoINRr<=)DMP=q#>EY%S2={xYmO$Wzdmca zToakJYEI?KRgZ_K-$hF4$v-Y#DE?(y|BA=4cEQyJ{Pex2#`4YoY`j!(NPJIdT@0Sf zc6_O)LEX(zZtB`>Jy;Q6vD?=eoMluDX}EVx8N-(Qxw1A%){6G78WX!x%d18+*Ly=- zl=Q*Lx&KbRzN%*Vsz7BOruQb%$Xh}$?fL=9MQu$Y4C6J_t0(*4$C*}t1++dsdv;{1 z8H~i!IWU^l0xw={+peFK^Y)W>N20jTWzw^Sgr-I>BM#L*V^*UHC8^acOtW^Yxb3VmGmG9~}Nt?%Lnu+z)MXyy}I`wWhwLYcI++8l12l zu5%4B73Y+E007tX)jEw;K`j!g?WJ(PB5?lSjW8^)Sn9 zP`@an+1;3oGksaFuc1kl36#0IEhkUs!(F^Yv_$vnxt!5C>Uo!}T@5=)Tcdy8tTSJ= z&BSq58pz?{FwIoYosN4>e)|+}^i21|DfFoX+{bpSTiShwW`2zJ!y5H@ZT6T~#Ji_k zvI0J^e-1KgjFWqp&et}y=*GwJjjaMpm z^EMF$54a00rQ#jLNckV*$(;C@a0|X`1vvY4XLd)+Hh*zP{%HlYK*Upm!-ttQ&e9#? zp<1EFL|p>i$K_i2ucT|S^)}JTlLtM6c1>?F*Iup{{|7DV($h9cv9;pwq=VbwF0s4V zmTlGNL%luyLOVXK{lpM+okLL4i9MmxsXwniQ1jhjmN_;N`8JoG7CPqIpAn!@{5J~n z!6kX+RhLGlGUr}vQqAlyIp35vkXxS!vcZ*eVr~&)0ox!#5B-UHkSmV#kRUtvj~mm; zW>n2pAwIzvM3=r}sf)+@K?}!`Rx=WYua6-i2>w8{<~fnXfaK!CgmTx3!4HXzdfpwCNeZrrHRc8Nr%y=>A(XBo7P*03ZZ840dS!}=U(liP=X2l zP&*$kV>yf~x%792W=jlX6D$5G)-8!zWR{9zKE2EuKDKD6V2Hn2bwyv{=G)I|$08jg zx9(1eJD;Ae8+|F^c6qacvFMOXbeevDUiC=$rQ^qT=}O;AAVepm?^cie<*3n#HS6~r zU8BB6T|qYd;%I)`Le5kYk)JrVTstR3zj3}aTXe0owYV*RX0ppmS@<%0xU6h!c*53X zZYHIle3iEXvXsL#iZy8d1EPibb&vJ?DZWOMC3=Jn_{V(1-JAh*uI|-2TjLtlnP<3=`@Gl+ z3L^J9&aR$2Sov;HcwuMrDIK}jW93@}e(kNp^-U559mW|$aZTG0 z9X0O!9b8B}{r-t~=^M(S2K_JYt`ccnt8D>8B&SHKk&L6HbAF@2KNbB``N_>i%Mh(Q zl`=;yTB=A{Taq0^HQ{R|Qo2i6F{cE)Gh^l3srwkYAxT2}wSGct#+(bf!^l+vQTo`U zsE90=H~2gdB0sl~;Z!Lj<;YM??0ImuYbv1=Y;ar`Y2PJlxaHeOlf-iRBwH99jJ}pC zTl8lYWBa!#E(x7)f8jr>rgjGhgDjo1c4~x@hWPo6xrioRBveu(El*;k zZavq)N24z(`iS##>FT6AtE2CYY8Em_lcMEeX{(59%*nK!MfSeY5=tnKo)O$EKJ2gO z=F9f_y2|dUM`qe5+BMB z_D7k@$&%9VyF90s=&xJ-n_9uRW}4&g6?enb(;VLn#Ew#h;O|=hG1{_G;J% zn{%OluRAX~fhHP<%k}ngOR?3Lnu8EIj1_ZQ;KfidAgosStB59C}=5S@@ItX%?ymiNYW9tvQ0{)+gW&u3hcP^ zgE*N^zHrN6*Oq9jW%056U~h=G28nR?%jImPpV!(_)bbHL!TOrFTEA}$-Sb&2`JvcN ze;WISa_esps`S&B=;vw7SMSEY|Lw7`uw(S30H6VR6~YY)L@#WCql0d-cwt+L*6D+> zKZk>{zf^Xe+?{do|MIKnjC^A$x!xRHc6-0@>Vj6t;yuH&mZu3B zsJn3KlY9MEzkGeKGgh(6Psm{Cd-+PYFIh!I0_xM<5uqz@y9lEFs~GmcvErNGWRnxE z6E6R|+11SFiMp~nr-%4y9?imT-e^e{hMh7BN9=owMLD=Jv5U6*{M_e^o@7tJ09$DH za)bC7#MiRk?R+0_!!Jk*+3;-qb+ImyW1Dhx&G=B}!cA__RdlR-XpCjlZzU}mk3LYP ze;izwbKGQFqW$%s>*D{%w-i$(pTF-aNIw58#YoC3S2v4;u#JA7zL;szVoQk)?(e5? zgSuox8q;=m42>1-=bQLcfTNCd=K0R{i=*2^J?-1}DG*C`&SLuWVXiKLy-HCJ)@42|fv6n$ z9PW3h=C_*HEYV8qAEl4(^9J|L+70*rjm>@#@6eh?)a0ZC-pFc--9y)O^L3G9W#drN zK?1^B-C&MR2;s#O-TOSm{ww%LmLbaP7bmb5*&ya-k0v3-*fmNTVN1?7{alWp zXb@4OLLSL+NQ;tY^Xf01=PatjlHeXl{8DiJv*gj!K781eel{ycH`Xg>Zj70GoZkA* zlw_zySjy-xk?Q=1Wp+~Vu}RlnK7L#>rzS*M5ou+=ZHx=i94|GVu7SLwOWjkIFrFU- zb}RDmMUdWJ%Cu8*sUC-$a`buo8IoP#t)8zehCeGsul>G{9*WQJw4$Q@jedVSiO?t?cqIDxuMMXK zb4}aMjr`yT`;u+FA1_{{DL>$GI`3Xm419_OP9s+4+@SMlcocA38gAw+Dxcq8DadAY zmCT7~xFYI|pR4SKb&xq&7@yC7o&PF}=rA)GFDf~0RaSOwW-_*Kc-!9Do`RJdgORED z=HG@4>>fDP46b{A-)(uBu!|q1kWlq<*e@kaZ3~rPbb5=T{RTgwdP1B3C2HrMUw)=I zw@l2ilWVC;e=~^RJB8u0b#E>5XF9sR&O&-=U-4+wU!xOAO%f`+R@?*Jj18K>urViV zE01Ucz~=9g7`0i*>=X`^TX(ZzH!fNG)29 z^&Rg-T}m>#Y?fIC^c*6vw{|A;KH!v33ev%qd-X{uR;%OA4_*`Ezp78a!6_>${(0#iESX!ngL5V&|M}b9q^zxq4zKGo$2DuV z??&9$Fr?%ET%Ffm?QlApBjM@X%2jpt-r%z#fiIa;9mjBJkV|%4%e~86ODZeI9$Asg zi}yP-rOuMuEL)b5C&S2-Z}A070!XrLPNG8wD?;Cq$$A>)je{}Z#QfYOzD}WAo+-EN z(I~Y}Oc4c;)Bo_i!AvxgLJ;Xkl`?K#y6BHT#d)|zVnKA^t&37K4M$%TC(p;K&YF!! zq=tuZji!Q^u@&m<%9N-kH&b-7?x5|4}%CJk{dAKE;2EDrZt@xsE z=%xS*8qvvjg-Rd5#<{`RCJC+gZNSPLv?HYvL_vSGrb(58YKi_6mU1w%xRB~O;I|-H zdA5C3LqLu}S&(n&A#ZEcQgvn)oBf0AVMBU~`CGPBktbi1vJ{i0LN8`1E816`9^r2= z4i7DTKL7FUK7T9JOJ4C$QiA#CfqM{Qk87gEOb}lvS`LJ=QkC&LxlN_$D)6Bxx7x-$ zxgY;{G-%Xzq(~y;kM$=X*~Iehktg}4_*r|{Oc0z71J|gah)ih|?zScgU<2{7x|@QP z3fSY#Qu1hK7oyZTOlKT5h;>u#(CmPF9N);N-kguV>*a2?@C(V{m;dz#aAxQ z8a@+8`d>-I%gLsgd z4%VzBruhjkr5uG|d^k=$rbQEoR=A46l1ey-*zedqr?#bTRJ}nFh_O^xYX+K76u}bvQUiD2&?Ea#3vM=Tv)a;z zLxle3XfkBk`D|tSe`W#3OH(8dqZTcrBu`~n9F!MqCmSZ6do;Kn9GJHfl5G2-pEoo5 z98OBdtn=DGxt*3iC8(8}9%#HkzPstueO?LBoAv{X@T_AA{z<|1s*jZKi-*{DaRclO zqXD^II(PnV(>D8WpMccrq_YMJPJ`+nD)nl5>=17{fncT`oOR~s%Yq;~V- z9n&LPu5^cG7T&IEmhy2!A=g#ZCX7pt-(TK)m^9Rnj9Z z$bnZ|#U#1#kSx-ELe?+=)0O>=?`IAJaApE-Se|>@-i)CSu=J}}>b_BH#J3&G#DMM~ z?5*)%|6=0kAARa%b z@tf(~`KNfJNWhPJ*-pjIr-;eBqvESK0-HhH`r)y^a)VpA^Y6gS0S}_2)!yGG1qb@B zXzlL0lioPS-(dt3CzkL&dz0Aft&e^VPfr|rmM9jC;T8h^aYr$+D(vr(H0L8jRZzv->%pSe zd7f1BQ#SQ!9$dF~ssz_4r|2MGpIfp_&5wG`XPl1%43|KLpJJY7J}tnhk{|KJP*H|m zc9ng`EF$dT6r~)^Y36zGAlVW)OLHu57u1!jneKnmR67n4mWK0^oaw7WFNxDZR+EHWt=}s4ya>P} zOz}R?0Ici=PkgsuTav*tlT-Y2GQXtf>bF6Kz5Z8GfKv5kb$Pw7jR7NSv0_IEPkT2A zPdeGp4ULIxs5sL3FSh9HqV74eYYt(T1!*Z&(l65%>j&4>o0Effb0)!{{()%EFkjb0 zw|+q+BSsEMZqVG|y5Z+6nNxx|$hbv+0F$&I2AaFOK}j(*Z0hsBU?6>GUsimC?)^?g z`UH72(qkLSQ)P)LuxM+h94uO2E7c>{i>hy|n7~^Lu~oELh2B26E^Mx85JA|jXQnUi zuxQyy8R69bNCJ4s|574l?RMI1LRpi5AkTAUAP+AGsO;nh4GsQU|3MiNf{NWIF42(i zrpr(0$P)FXP)GVENdA%1>TKc|AI{Lqm>cAa7D6t2n^m8`R0@%=t5i*V6U+Qol!xt? z2f-suD{Uol6>-7^(x;^8M8BWyl@P#`Py;z|-CkzVTIYGp=xUKgPuTs35LVM&7Z* zgc#y0J=Lj5>LvoThdX&5?`(#OQ4Z*yQr8&J3?rpDUX1%Q4Lch1BiHMc;6nbi!EAiR zy@KX)0E#Gud%UCE`O2mvS`-@dJ=axr#sS^zTH3bN5Gs|!1OCGGaEM`jji0siyUQ&L zJ8O)^_ijx(;NEUhF!?ZA2)qTQ>dFyu|k}WQkObr8*9BgHFUGpYXx0K*T-Y2FApgGv>f? z-M%E@PZm-Aj+40uVVgNG@qu|~u%S=5%~1^%VV@NH@S&z^g_Vu4f}eP(e+w%%FpTMG zd(D`;J!)|-g{2HW)WHq8Uo~aWSUblLdMKE<-`&bxED$Fqr=emId_Q_JUS)P}0%t58 z?fbZ2zi*(1CDKhf8u^h+M0uOQ@GfE$I!qP4uhSR%uFOy+R_qB8DBa;9&iT#7os*hK zS9uRW_*%t8o(xO$5=i~ue8a!R=gl+`w2`{wc)Z;7o5jE0sTB#hq_X$i7#pqB6;+4y-mfj&r36%ZK?|8R6km8{U0sY~ULjfLyHaRQPLHL$@zF^iJUF{=cdIn6PO90hKhC>5Z-Ln)8dTz3dj6}uZ_YfG;WX$ zZE=FXAVXR>CF>k#yf*q}t5^oIjBLp5KV9z-kU|{hLzS=j$;Yh(bWl*JkE6<*epy_w zD-;G-jL=3u;Yv95v_9G3MMI7^R8`k)&-jBR0-B8xhFRy!?QFQ#@jnFcER-BM4sg)V zAo1xsJBQ9&khOQtycty3Fg}6{Y2uE7Y#1$_f4W#S2+|rYL?dGNjsYC~Y*&r`y41N5 zrm<^oRnz9U8xK0%XPUX;G$Cln7gT2waplRpm0^w^EUb7t=Yi3Hm1LT8ZWWgUD+}ib zpGO;=s1a^bLtn`tQmjMRQ{zVxO#P<@k-!@==UO~~t>ipg@Bwc6=rgGo$AOv!$teg_ z>LG@!&RoYW0zh3d-)tu|rYQrCz2}D9<(0eUPQKO5o8IExYZyPXX+oyz8)c*2f*Y;e z!#vH)Pk50#4@uHYb|=$Jg--9cyfdv0F>~7-og>kRRNuRqd2pIG$%A}zmI$IaCO@7x@d(!#dG`A@YtLs3dsC<;@4u6RH8`7DA~X1tV$sK@ zC(>1UCc-cL1#L=fpdsUU(|CI>=jJspcLRFNSH|2C8k5UqEaVr8^_U{uBCTt&v-2g{ zuj!}#yg$EAHCLl3TUxX@i>=q!UmQ>S4ej~Hr`VYt@LJAPBS;}`qnSX)-d`kv{>0$U z_wj>)L>Ll2o&$at*L*xUm$e9omEV4CDDkUOpZ`_bFV?RVN;LD@YlU^(`3KzjEKr|m z*?!+jSurF0w0&te=jmY3o|A$PPz;zBDnS{CQ}>Bw>&gNa%E zMM#(u7$)N{v__M(2zAGjAM(=BISX3PUEU5-@Bxl9`K6mv&iqUK3ZfQ=f$FPsHlPZJ z4t|WZXo>D?)dES=SyDf6W_B~lpC&v++HWREBW0cNF(q@0hJLSegvaxMh#ggzo#t%U z_DyQI3)cfN`A~OX#J)19EKSKTr(}e_LMmEf5b>3f?F-_QaX93k2N5A!p9KBYGo)aM z_ypDtjtJhj?+i4I7~R}IxQSbuf(2joy}Ky@dQXT+Wg~7TxoMjgn?rG*nRF!i`w9C< z`$>Wt(*8`WbGgeY)K_?hf|q4JI{v*k5*KsA=gnIiUK=`RFS|^mp!y=XW$-cdLC=IX zOB9DM&@0=t z^`%{4@?x7Pt?#KQaNYwa1XI}g43ClrHC4@MiLT15e*6SBlm#V2=xCp$1oevl#$cm> zvFqI*Lp=oL(b99=9-^iD3#Ro(k*7ETZCDs3Ci!K>JFCkTX9)Xq`cTyE36EeVwGZu9~y1IG^Ww1m=VhmF29J$bcL{<~AKj0O$o7g$}3SUrntJ9QMYmj zC3cO!bMCsUjcoz7%fnsIsOe~M)B{SR>l|ut{nogY+!D?beTe@nS6bRkaJMhwdjrte zdTPLs^`YOaPS4OCWvvZzuL<2( z`i>0#x@K~A*SFWD7A=sE_C`4Dk>S6^HCmT_;1;UPxT8cPYa4bILN|=^7sq(=s--v61V;~v_w6^ zW0v-@A{pr3=5oc_;J9UxjJQ>8uJg~X|)r{K5+7oz~CoWm59mp!`Y?~+SaMASAwNy?b` zvT~HH=L#(6Tt5Ty!;`OQNgAuXotr(#C*2=qiN^WCcIW||?%oN!2nX7Sb>AdKpDVNG zcXFW$oja(IbF(|n$x$U=BVg<~mI#N4oHPC$-eJ|tL6Yh;C7>Ef>rDVxg z5}Esmx7m0gbl(*>C_bR{d=8A9N`)kvOO|qB zmxfzkvFHhRp2rXNpfQ`d(1<4H#kcsMq?t8phIL3avO&ivHF6&M> zPFa#VvX9)?NJHWiNFIBUa17f&%HPXKG9=Wk*=3mD=sHe1WV8>uU>F z(e<8rI4o-`H6-q;XdNey4)HBjW>Q{hg>S=#s`-2L3e`QQlM!eSy!9sif~?ttiquy=zDTI4Ia!ZUg3H=sACDF9;|Qy~SA+loi4awr z)U72!F~1a1i-t4y^ViSJwe2n}p|2i(3mO51%td*x#oeQsv*d&|D|6OjLjoL8n;=zB zA={!nQ~1?Gx+B_Y6dpRCqZg4`0@dphB+~ISgMwkhsLeq_&GJvl#}ihC(nT}Sl8Ja_ zx3t~P%hac1aGjbfd2+RwOiMkF==+fY{YVMX=VY=U=nV+lY8A1*gCbUx%`96cfq1B$ zBikmtF;&&7fQPXm?M`Gr8agH}2JmXs1Eep((s)2h2GkMKR}o9x%x$q@zJp=%V^oX0 zeQ&KzIkt>#5bWZM=p55S126lo2sO1NYrCGA1>ItGeT&cP<}ni=(I7s@$d18F1rJsB z$LUfjNy`zJ(-3DE#wCdr+b{qNkOP(|{WYQV`~*2j>-NMF^Qqg|Cw0P7wt_{=Gm_oE z)&V(!wzET*u=;tJxN%X3jwmHB<9{XB5%VPwd?18zblQr-S`g@v9RF@PZR3Qj9w0IH zr?H_nMhP@uHW~;mElI)LwWW}`eE?xlN1hUVZqn6aH{b@u zO4PMw9#M&8&H*nIpW(rES3(=?JQr87SCLz?aEmw&85}r?+@goh2>^tQ>|({?8~<_` z4P-W^BkUlEjR0{Wwbytc{uLm4=sd4PYVSER4L8%&q}z*h0S*CL#b=1EKzmcUXAPO0 z9q~a1H`7UNhK?82SJvfQ9M0@Q_XP>$)#laAoX0sn={rQ0}-Q4JEqkFou=U-C0O?pxhRCM{ed zcMNSrUR_v41jG5O(0ibA@eM#_K;LA~W?-EVPM(o@-FD{WNA1h3Hq>2OWyWz}9k;)W z&ECcl^}wmaFnCy#c638UH7HjiY$!h^3_*rXKTA#N=*e!A`55rPd3XFO7*Y6R<{&Q- z@K*GF0h@*HHqvEHw!O!@P4e8DysZB@1|-MNdu+{F^4JNng4J%N$Ivgt+(EpevbFmXGPju0ck$Zz#Vi{5SM&{~o=h_c6XOEbtoY z1`EJtXYE;bFm&ZVm^CBN4ULd8zD~)&_A&w;(Y^5NrnqO!wiG6oFaWJMe0D~V zh+>|Q0{+WLN^lw&#G8D$PbvdmslF7Ej=y0xeR9A#_obw;(N`If2DD46GM{aWYN*7| z4+$>WAS$Wi4p@v!uIWEjWI6Byi8S(UyGq%voE$Wo*? zboU%P$fCuAWT-=^+1i-^iU1T%TOhsQAFV072F~7>TzA6(QF)FvG=3p`No}IoH(*15 z;(FFu2dYznSD;>ZMlYLi=rjin^)-7H%ANJHgs=`2{{o`cO!;-HL}_;=M+B&7siTFMAJ64K^QdZ7FF zsxOU|pdG|vd7E}4InLmq3#CMkBFJ|M0APsy1RQUE`%M4yGqb#ZI}BkLn~!r9N4Gw?MT!w<3+1Ql#ZlXM-f+fH3)$K29D?GY?!qnZil`b)4*ROs z&_XhV4nWerw4@SEP8)9K8$Pu{&7;s&y@I#+90L~|O_9t5RfdHH?~j=H5NBnVr9I(bTqi_kNA5xyuF@P)1TF^XGe=KF6{|BqXhQ5=2W;MDT zzY=GbjzMD=x=)f~lm}zf?^K2% zMlb@+WREkM8GD>Fk6MtKnV71|OoLcldo97*7eOp9E5L$ZPuU`D3;<#Ql*G{MSwf2? z@^9!eTF1!X&O{Tz{F%9gL`4!5crgz)1nu9dxFYmhI_9=1M62N?#eLro7zT)ioI>uX z^jLxoHDK3_WjBP_ZG0B)M|DLnG&=Zj3N2fVv07$=^>Yl+JBB3aiodM4OnWC#wCr?@ zrqhEUDOS^e%~S2rBD)t%ac5%Wl-P}h&S&Q6B>Wop;=N*Rz-*`-CIz>M7r-+6KES1H zJe3f=mr=4iVtZxVzCzjJi&s>n&yu;N_{#<=oDB?{bv3*bLYt7qd2F@MDh2pmh*nI?Q{RZg(KYSBT?aK4rM{7r0@ za{RA%=Hv$!EvL{xGHR((!Et2-YH(^E zXupuKeJTZcQ1uBgpFp=<+es2JyUTF}oo+}GjSyaGX1 z6_Ab{v53& zpz7&jPbRk47y|98+G5?}9CMNCyBtBRZu1RyaD+Iz3kssMrllTg^N9Ba7ZS@m`}Ms< z{GGmy6#=zGSxRAbG|ti~*SQ(ib(YMkHgY%_*B1_Ln>0$SAbLyUAN#m*yk^pZ8YeAI z=0uJ}+`zK|r!x!mv*?a$m4{E=@^+aAo#4Ry+14A%2;y?7NNqe4XJ#hwAf1-m`iJId z!$+t1vs z+komDah&VTe$-dUAvK&3L{V(a^a+WSCB+62XB%IV7GNap z6IOiDY1h?Xxj~m@+h##4y+}HJ+UI1eP>y@A>d>@bUE71P89y>tM? zJtLFD)~JnF-L?XA8Z+KtAT|5lqn^byoUV&ap2+Xi)W+V02|-(%N>TMjUBuoKnmvep zgs%{`{;F}f9k(bF3?GflPGKm#Y#V;t2a=+!^wdIez|1IZY_eD{V%eXfGJluvKgJBQ zS#jBVjJa|W-)dnt8ZWyUMApO`1Vqk_zSi3Z-H^X)U61*m>41Rw$y|!f0fpSQwjEQm z!%p4?IYPh6vf$Dk5yPQ+Q`hE+0rRtuLi{804s@0C%gVogzlG2GD)h@N+snzqiUW?h z==@&yOP2xDBj!TzJO)l$gTG)rHaVPv1~6FY=q2jKepW z>J#ZWc=3-{~z1o z?qa#E-mhZ3TlZtpPj3nLi8w#~`?I4`=07hZR}la6Qf>SH!OC0z{p0_a)>wMRY>sy< TBdtVyHl*XHk7fO4|L6Y#Ra8JK literal 0 HcmV?d00001 diff --git a/docs/images/nf-core-viralrecon_logo_light.png b/docs/images/nf-core-viralrecon_logo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..dd92a124483e37d473adb58c4305962f722f76ae GIT binary patch literal 76539 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�ev`Q}H7y#R1zKkC5n?0_OP7^FqWWeXhX0t0pNK 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

)bNtXB<>| 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 literal 0 HcmV?d00001 diff --git a/docs/output.md b/docs/output.md index c4c5fec7..072ec8ca 100644 --- a/docs/output.md +++ b/docs/output.md @@ -60,7 +60,7 @@ Results generated by MultiQC collate pipeline QC from supported tools e.g. FastQ * `pipeline_info/` * Reports generated by Nextflow: `execution_report.html`, `execution_timeline.html`, `execution_trace.txt` and `pipeline_dag.dot`/`pipeline_dag.svg`. - * Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.tsv`. + * Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.yml`. The `pipeline_report*` files will only be present if the `--email` / `--email_on_fail` parameter's are used when running the pipeline. * Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`. diff --git a/docs/usage.md b/docs/usage.md index ade029e0..2752a6dc 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -181,42 +181,6 @@ process { > **NB:** We specify just the process name i.e. `STAR_ALIGN` in the config file and not the full task name string that is printed to screen in the error message or on the terminal whilst the pipeline is running i.e. `RNASEQ:ALIGN_STAR:STAR_ALIGN`. You may get a warning suggesting that the process selector isn't recognised but you can ignore that if the process name has been specified correctly. This is something that needs to be fixed upstream in core Nextflow. -### Tool-specific options - -For the ultimate flexibility, we have implemented and are using Nextflow DSL2 modules in a way where it is possible for both developers and users to change tool-specific command-line arguments (e.g. providing an additional command-line argument to the `STAR_ALIGN` process) as well as publishing options (e.g. saving files produced by the `STAR_ALIGN` process that aren't saved by default by the pipeline). In the majority of instances, as a user you won't have to change the default options set by the pipeline developer(s), however, there may be edge cases where creating a simple custom config file can improve the behaviour of the pipeline if for example it is failing due to a weird error that requires setting a tool-specific parameter to deal with smaller / larger genomes. - -The command-line arguments passed to STAR in the `STAR_ALIGN` module are a combination of: - -* Mandatory arguments or those that need to be evaluated within the scope of the module, as supplied in the [`script`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/modules/nf-core/software/star/align/main.nf#L49-L55) section of the module file. - -* An [`options.args`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/modules/nf-core/software/star/align/main.nf#L56) string of non-mandatory parameters that is set to be empty by default in the module but can be overwritten when including the module in the sub-workflow / workflow context via the `addParams` Nextflow option. - -The nf-core/rnaseq pipeline has a sub-workflow (see [terminology](https://github.com/nf-core/modules#terminology)) specifically to align reads with STAR and to sort, index and generate some basic stats on the resulting BAM files using SAMtools. At the top of this file we import the `STAR_ALIGN` module via the Nextflow [`include`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/subworkflows/nf-core/align_star.nf#L10) keyword and by default the options passed to the module via the `addParams` option are set as an empty Groovy map [here](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/subworkflows/nf-core/align_star.nf#L5); this in turn means `options.args` will be set to empty by default in the module file too. This is an intentional design choice and allows us to implement well-written sub-workflows composed of a chain of tools that by default run with the bare minimum parameter set for any given tool in order to make it much easier to share across pipelines and to provide the flexibility for users and developers to customise any non-mandatory arguments. - -When including the sub-workflow above in the main pipeline workflow we use the same `include` statement, however, we now have the ability to overwrite options for each of the tools in the sub-workflow including the [`align_options`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/workflows/rnaseq.nf#L225) variable that will be used specifically to overwrite the optional arguments passed to the `STAR_ALIGN` module. In this case, the options to be provided to `STAR_ALIGN` have been assigned sensible defaults by the developer(s) in the pipeline's [`modules.config`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L70-L74) and can be accessed and customised in the [workflow context](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/workflows/rnaseq.nf#L201-L204) too before eventually passing them to the sub-workflow as a Groovy map called `star_align_options`. These options will then be propagated from `workflow -> sub-workflow -> module`. - -As mentioned at the beginning of this section it may also be necessary for users to overwrite the options passed to modules to be able to customise specific aspects of the way in which a particular tool is executed by the pipeline. Given that all of the default module options are stored in the pipeline's `modules.config` as a [`params` variable](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L24-L25) it is also possible to overwrite any of these options via a custom config file. - -Say for example we want to append an additional, non-mandatory parameter (i.e. `--outFilterMismatchNmax 16`) to the arguments passed to the `STAR_ALIGN` module. Firstly, we need to copy across the default `args` specified in the [`modules.config`](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/modules.config#L71) and create a custom config file that is a composite of the default `args` as well as the additional options you would like to provide. This is very important because Nextflow will overwrite the default value of `args` that you provide via the custom config. - -As you will see in the example below, we have: - -* appended `--outFilterMismatchNmax 16` to the default `args` used by the module. -* changed the default `publish_dir` value to where the files will eventually be published in the main results directory. -* appended `'bam':''` to the default value of `publish_files` so that the BAM files generated by the process will also be saved in the top-level results directory for the module. Note: `'out':'log'` means any file/directory ending in `out` will now be saved in a separate directory called `my_star_directory/log/`. - -```nextflow -params { - modules { - 'star_align' { - args = "--quantMode TranscriptomeSAM --twopassMode Basic --outSAMtype BAM Unsorted --readFilesCommand zcat --runRNGseed 0 --outFilterMultimapNmax 20 --alignSJDBoverhangMin 1 --outSAMattributes NH HI AS NM MD --quantTranscriptomeBan Singleend --outFilterMismatchNmax 16" - publish_dir = "my_star_directory" - publish_files = ['out':'log', 'tab':'log', 'bam':''] - } - } -} -``` - ### Updating containers The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. If for some reason you need to use a different version of a particular tool with the pipeline then you just need to identify the `process` name and override the Nextflow `container` definition for that process using the `withName` declaration. For example, in the [nf-core/viralrecon](https://nf-co.re/viralrecon) pipeline a tool called [Pangolin](https://github.com/cov-lineages/pangolin) has been used during the COVID-19 pandemic to assign lineages to SARS-CoV-2 genome sequenced samples. Given that the lineage assignments change quite frequently it doesn't make sense to re-release the nf-core/viralrecon everytime a new version of Pangolin has been released. However, you can override the default container used by the pipeline by creating a custom config file and passing it as a command-line argument via `-c custom.config`. diff --git a/lib/NfcoreSchema.groovy b/lib/NfcoreSchema.groovy index 8d6920dd..40ab65f2 100755 --- a/lib/NfcoreSchema.groovy +++ b/lib/NfcoreSchema.groovy @@ -105,9 +105,13 @@ class NfcoreSchema { // Collect expected parameters from the schema def expectedParams = [] + def enums = [:] for (group in schemaParams) { for (p in group.value['properties']) { expectedParams.push(p.key) + if (group.value['properties'][p.key].containsKey('enum')) { + enums[p.key] = group.value['properties'][p.key]['enum'] + } } } @@ -155,7 +159,7 @@ class NfcoreSchema { println '' log.error 'ERROR: Validation of pipeline parameters failed!' JSONObject exceptionJSON = e.toJSON() - printExceptions(exceptionJSON, params_json, log) + printExceptions(exceptionJSON, params_json, log, enums) println '' has_error = true } @@ -202,7 +206,7 @@ class NfcoreSchema { } def type = '[' + group_params.get(param).type + ']' def description = group_params.get(param).description - def defaultValue = group_params.get(param).default ? " [default: " + group_params.get(param).default.toString() + "]" : '' + def defaultValue = group_params.get(param).default != null ? " [default: " + group_params.get(param).default.toString() + "]" : '' def description_default = description + colors.dim + defaultValue + colors.reset // Wrap long description texts // Loosely based on https://dzone.com/articles/groovy-plain-text-word-wrap @@ -260,13 +264,12 @@ class NfcoreSchema { // Get pipeline parameters defined in JSON Schema def Map params_summary = [:] - def blacklist = ['hostnames'] def params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename)) for (group in params_map.keySet()) { def sub_params = new LinkedHashMap() def group_params = params_map.get(group) // This gets the parameters of that particular group for (param in group_params.keySet()) { - if (params.containsKey(param) && !blacklist.contains(param)) { + if (params.containsKey(param)) { def params_value = params.get(param) def schema_value = group_params.get(param).default def param_type = group_params.get(param).type @@ -330,7 +333,7 @@ class NfcoreSchema { // // Loop over nested exceptions and print the causingException // - private static void printExceptions(ex_json, params_json, log) { + private static void printExceptions(ex_json, params_json, log, enums, limit=5) { def causingExceptions = ex_json['causingExceptions'] if (causingExceptions.length() == 0) { def m = ex_json['message'] =~ /required key \[([^\]]+)\] not found/ @@ -346,11 +349,20 @@ class NfcoreSchema { else { def param = ex_json['pointerToViolation'] - ~/^#\// def param_val = params_json[param].toString() - log.error "* --${param}: ${ex_json['message']} (${param_val})" + if (enums.containsKey(param)) { + def error_msg = "* --${param}: '${param_val}' is not a valid choice (Available choices" + if (enums[param].size() > limit) { + log.error "${error_msg} (${limit} of ${enums[param].size()}): ${enums[param][0..limit-1].join(', ')}, ... )" + } else { + log.error "${error_msg}: ${enums[param].join(', ')})" + } + } else { + log.error "* --${param}: ${ex_json['message']} (${param_val})" + } } } for (ex in causingExceptions) { - printExceptions(ex, params_json, log) + printExceptions(ex, params_json, log, enums) } } diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 44551e0a..2fc0a9b9 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -19,27 +19,16 @@ class NfcoreTemplate { } // - // Check params.hostnames + // Warn if a -profile or Nextflow config has not been provided to run the pipeline // - public static void hostName(workflow, params, log) { - Map colors = logColours(params.monochrome_logs) - if (params.hostnames) { - try { - def hostname = "hostname".execute().text.trim() - params.hostnames.each { prof, hnames -> - hnames.each { hname -> - if (hostname.contains(hname) && !workflow.profile.contains(prof)) { - log.info "=${colors.yellow}====================================================${colors.reset}=\n" + - "${colors.yellow}WARN: You are running with `-profile $workflow.profile`\n" + - " but your machine hostname is ${colors.white}'$hostname'${colors.reset}.\n" + - " ${colors.yellow_bold}Please use `-profile $prof${colors.reset}`\n" + - "=${colors.yellow}====================================================${colors.reset}=" - } - } - } - } catch (Exception e) { - log.warn "[$workflow.manifest.name] Could not determine 'hostname' - skipping check. Reason: ${e.message}." - } + public static void checkConfigProvided(workflow, log) { + if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) { + log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" + + "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" + + " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" + + " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" + + " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" + + "Please refer to the quick start section and usage docs for the pipeline.\n " } } @@ -168,7 +157,6 @@ class NfcoreTemplate { log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed successfully, but with errored process(es) ${colors.reset}-" } } else { - hostName(workflow, params, log) log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-" } } diff --git a/lib/Utils.groovy b/lib/Utils.groovy index 18173e98..1b88aec0 100755 --- a/lib/Utils.groovy +++ b/lib/Utils.groovy @@ -37,11 +37,4 @@ class Utils { "===================================================================================" } } - - // - // Join module args with appropriate spacing - // - public static String joinModuleArgs(args_list) { - return ' ' + args_list.join(' ') - } } diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index b7b81a05..9d3d4487 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -61,6 +61,9 @@ class WorkflowMain { // Print parameter summary log to screen log.info paramsSummaryLog(workflow, params, log) + // Check that a -profile or Nextflow config has been provided to run the pipeline + NfcoreTemplate.checkConfigProvided(workflow, log) + // Check that conda channels are set-up correctly if (params.enable_conda) { Utils.checkCondaChannels(log) @@ -69,9 +72,6 @@ class WorkflowMain { // Check AWS batch settings NfcoreTemplate.awsBatch(workflow, params) - // Check the hostnames against configured profiles - NfcoreTemplate.hostName(workflow, params, log) - // Check input has been provided if (!params.input) { log.error "Please provide an input samplesheet to the pipeline e.g. '--input samplesheet.csv'" diff --git a/modules.json b/modules.json index af8a44de..26f60e0e 100644 --- a/modules.json +++ b/modules.json @@ -3,12 +3,15 @@ "homePage": "https://github.com/nf-core/viralrecon", "repos": { "nf-core/modules": { + "custom/dumpsoftwareversions": { + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + }, "fastqc": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "multiqc": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" } } } -} +} \ No newline at end of file diff --git a/modules/local/functions.nf b/modules/local/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/local/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/local/get_software_versions.nf b/modules/local/get_software_versions.nf deleted file mode 100644 index 15a486ca..00000000 --- a/modules/local/get_software_versions.nf +++ /dev/null @@ -1,33 +0,0 @@ -// Import generic module functions -include { saveFiles } from './functions' - -params.options = [:] - -process GET_SOFTWARE_VERSIONS { - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'pipeline_info', meta:[:], publish_by_meta:[]) } - - conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } - - cache false - - input: - path versions - - output: - path "software_versions.tsv" , emit: tsv - path 'software_versions_mqc.yaml', emit: yaml - - script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ - """ - echo $workflow.manifest.version > pipeline.version.txt - echo $workflow.nextflow.version > nextflow.version.txt - scrape_software_versions.py &> software_versions_mqc.yaml - """ -} diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index ac2d9c54..a532a6b3 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -1,31 +1,27 @@ -// Import generic module functions -include { saveFiles } from './functions' - -params.options = [:] - process SAMPLESHEET_CHECK { tag "$samplesheet" - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'pipeline_info', meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/python:3.8.3' : + 'quay.io/biocontainers/python:3.8.3' }" input: path samplesheet output: - path '*.csv' + path '*.csv' , emit: csv + path "versions.yml", emit: versions script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ """ check_samplesheet.py \\ $samplesheet \\ samplesheet.valid.csv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf new file mode 100644 index 00000000..934bb467 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf @@ -0,0 +1,21 @@ +process CUSTOM_DUMPSOFTWAREVERSIONS { + label 'process_low' + + // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container + conda (params.enable_conda ? "bioconda::multiqc=1.11" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" + + input: + path versions + + output: + path "software_versions.yml" , emit: yml + path "software_versions_mqc.yml", emit: mqc_yml + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + template 'dumpsoftwareversions.py' +} diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml new file mode 100644 index 00000000..5b5b8a60 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml @@ -0,0 +1,34 @@ +name: custom_dumpsoftwareversions +description: Custom module used to dump software versions within the nf-core pipeline template +keywords: + - custom + - version +tools: + - custom: + description: Custom module used to dump software versions within the nf-core pipeline template + homepage: https://github.com/nf-core/tools + documentation: https://github.com/nf-core/tools + licence: ['MIT'] +input: + - versions: + type: file + description: YML file containing software versions + pattern: "*.yml" + +output: + - yml: + type: file + description: Standard YML file containing software versions + pattern: "software_versions.yml" + - mqc_yml: + type: file + description: MultiQC custom content YML file containing software versions + pattern: "software_versions_mqc.yml" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@drpatelh" + - "@grst" diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py new file mode 100644 index 00000000..d1390392 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +import yaml +import platform +from textwrap import dedent + + +def _make_versions_html(versions): + html = [ + dedent( + """\\ + + + + + + + + + + """ + ) + ] + for process, tmp_versions in sorted(versions.items()): + html.append("") + for i, (tool, version) in enumerate(sorted(tmp_versions.items())): + html.append( + dedent( + f"""\\ + + + + + + """ + ) + ) + html.append("") + html.append("
Process Name Software Version
{process if (i == 0) else ''}{tool}{version}
") + return "\\n".join(html) + + +versions_this_module = {} +versions_this_module["${task.process}"] = { + "python": platform.python_version(), + "yaml": yaml.__version__, +} + +with open("$versions") as f: + versions_by_process = yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module + +# aggregate versions by the module name (derived from fully-qualified process name) +versions_by_module = {} +for process, process_versions in versions_by_process.items(): + module = process.split(":")[-1] + try: + assert versions_by_module[module] == process_versions, ( + "We assume that software versions are the same between all modules. " + "If you see this error-message it means you discovered an edge-case " + "and should open an issue in nf-core/tools. " + ) + except KeyError: + versions_by_module[module] = process_versions + +versions_by_module["Workflow"] = { + "Nextflow": "$workflow.nextflow.version", + "$workflow.manifest.name": "$workflow.manifest.version", +} + +versions_mqc = { + "id": "software_versions", + "section_name": "${workflow.manifest.name} Software Versions", + "section_href": "https://github.com/${workflow.manifest.name}", + "plot_type": "html", + "description": "are collected at run time from the software output.", + "data": _make_versions_html(versions_by_module), +} + +with open("software_versions.yml", "w") as f: + yaml.dump(versions_by_module, f, default_flow_style=False) +with open("software_versions_mqc.yml", "w") as f: + yaml.dump(versions_mqc, f, default_flow_style=False) + +with open("versions.yml", "w") as f: + yaml.dump(versions_this_module, f, default_flow_style=False) diff --git a/modules/nf-core/modules/fastqc/functions.nf b/modules/nf-core/modules/fastqc/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/fastqc/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/fastqc/main.nf b/modules/nf-core/modules/fastqc/main.nf index 39c327b2..d250eca0 100644 --- a/modules/nf-core/modules/fastqc/main.nf +++ b/modules/nf-core/modules/fastqc/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process FASTQC { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::fastqc=0.11.9" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0" - } else { - container "quay.io/biocontainers/fastqc:0.11.9--0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0' : + 'quay.io/biocontainers/fastqc:0.11.9--0' }" input: tuple val(meta), path(reads) @@ -24,24 +13,32 @@ process FASTQC { output: tuple val(meta), path("*.html"), emit: html tuple val(meta), path("*.zip") , emit: zip - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: + def args = task.ext.args ?: '' // Add soft-links to original FastQs for consistent naming in pipeline - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def prefix = task.ext.prefix ?: "${meta.id}" if (meta.single_end) { """ [ ! -f ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz - fastqc $options.args --threads $task.cpus ${prefix}.fastq.gz - fastqc --version | sed -e "s/FastQC v//g" > ${software}.version.txt + fastqc $args --threads $task.cpus ${prefix}.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" ) + END_VERSIONS """ } else { """ [ ! -f ${prefix}_1.fastq.gz ] && ln -s ${reads[0]} ${prefix}_1.fastq.gz [ ! -f ${prefix}_2.fastq.gz ] && ln -s ${reads[1]} ${prefix}_2.fastq.gz - fastqc $options.args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz - fastqc --version | sed -e "s/FastQC v//g" > ${software}.version.txt + fastqc $args --threads $task.cpus ${prefix}_1.fastq.gz ${prefix}_2.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" ) + END_VERSIONS """ } } diff --git a/modules/nf-core/modules/fastqc/meta.yml b/modules/nf-core/modules/fastqc/meta.yml index 8eb9953d..b09553a3 100644 --- a/modules/nf-core/modules/fastqc/meta.yml +++ b/modules/nf-core/modules/fastqc/meta.yml @@ -15,6 +15,7 @@ tools: overrepresented sequences. homepage: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/ documentation: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/ + licence: ['GPL-2.0-only'] input: - meta: type: map @@ -40,10 +41,10 @@ output: type: file description: FastQC report archive pattern: "*_{fastqc.zip}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@grst" diff --git a/modules/nf-core/modules/multiqc/functions.nf b/modules/nf-core/modules/multiqc/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/multiqc/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/multiqc/main.nf b/modules/nf-core/modules/multiqc/main.nf index da780800..3dceb162 100644 --- a/modules/nf-core/modules/multiqc/main.nf +++ b/modules/nf-core/modules/multiqc/main.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process MULTIQC { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } - conda (params.enable_conda ? "bioconda::multiqc=1.10.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/multiqc:1.10.1--py_0" - } else { - container "quay.io/biocontainers/multiqc:1.10.1--py_0" - } + conda (params.enable_conda ? 'bioconda::multiqc=1.11' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" input: path multiqc_files @@ -24,12 +13,16 @@ process MULTIQC { path "*multiqc_report.html", emit: report path "*_data" , emit: data path "*_plots" , optional:true, emit: plots - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' """ - multiqc -f $options.args . - multiqc --version | sed -e "s/multiqc, version //g" > ${software}.version.txt + multiqc -f $args . + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) + END_VERSIONS """ } diff --git a/modules/nf-core/modules/multiqc/meta.yml b/modules/nf-core/modules/multiqc/meta.yml index 532a8bb1..63c75a45 100644 --- a/modules/nf-core/modules/multiqc/meta.yml +++ b/modules/nf-core/modules/multiqc/meta.yml @@ -11,6 +11,7 @@ tools: It's a general use tool, perfect for summarising the output from numerous bioinformatics tools. homepage: https://multiqc.info/ documentation: https://multiqc.info/docs/ + licence: ['GPL-3.0-or-later'] input: - multiqc_files: type: file @@ -29,10 +30,10 @@ output: type: file description: Plots created by MultiQC pattern: "*_data" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@abhi18av" - "@bunop" diff --git a/nextflow.config b/nextflow.config index 23a3385a..5f8d00a8 100644 --- a/nextflow.config +++ b/nextflow.config @@ -26,7 +26,6 @@ params { // Boilerplate options outdir = './results' tracedir = "${params.outdir}/pipeline_info" - publish_dir_mode = 'copy' email = null email_on_fail = null plaintext_email = false @@ -34,14 +33,12 @@ params { help = false validate_params = true show_hidden_params = false - schema_ignore_params = 'genomes,modules' + schema_ignore_params = 'genomes' enable_conda = false - singularity_pull_docker_container = false // Config options custom_config_version = 'master' custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" - hostnames = [:] config_profile_description = null config_profile_contact = null config_profile_url = null @@ -58,9 +55,6 @@ params { // Load base.config by default for all pipelines includeConfig 'conf/base.config' -// Load modules.config for DSL2 module specific options -includeConfig 'conf/modules.config' - // Load nf-core custom profiles from different Institutions try { includeConfig "${params.custom_config_base}/nfcore_custom.config" @@ -68,13 +62,6 @@ try { System.err.println("WARNING: Could not load nf-core/config profiles: ${params.custom_config_base}/nfcore_custom.config") } -// Load igenomes.config if required -if (!params.igenomes_ignore) { - includeConfig 'conf/igenomes.config' -} else { - params.genomes = [:] -} - profiles { debug { process.beforeScript = 'echo $HOSTNAME' } conda { @@ -126,11 +113,22 @@ profiles { test_full { includeConfig 'conf/test_full.config' } } +// Load igenomes.config if required +if (!params.igenomes_ignore) { + includeConfig 'conf/igenomes.config' +} else { + params.genomes = [:] +} + // Export these variables to prevent local Python/R libraries from conflicting with those in the container +// The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. +// See https://apeltzer.github.io/post/03-julia-lang-nextflow/ for details on that. Once we have a common agreement on where to keep Julia packages, this is adjustable. + env { PYTHONNOUSERSITE = 1 R_PROFILE_USER = "/.Rprofile" R_ENVIRON_USER = "/.Renviron" + JULIA_DEPOT_PATH = "/usr/local/share/julia" } // Capture exit codes from upstream processes when piping @@ -160,10 +158,13 @@ manifest { homePage = 'https://github.com/nf-core/viralrecon' description = 'Assembly and intrahost/low-frequency variant calling for viral samples' mainScript = 'main.nf' - nextflowVersion = '!>=21.04.0' - version = '2.2' + nextflowVersion = '!>=21.10.3' + version = '2.3dev' } +// Load modules.config for DSL2 module specific options +includeConfig 'conf/modules.config' + // Function to ensure that resource requirements don't go beyond // a maximum limit def check_max(obj, type) { diff --git a/nextflow_schema.json b/nextflow_schema.json index 8798f89f..b16be3c3 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -104,12 +104,6 @@ "help_text": "If you're running offline, Nextflow will not be able to fetch the institutional config files from the internet. If you don't need them, then this is not a problem. If you do need them, you should download the files from the repo and tell Nextflow where to find them with this parameter.", "fa_icon": "fas fa-users-cog" }, - "hostnames": { - "type": "string", - "description": "Institutional configs hostname.", - "hidden": true, - "fa_icon": "fas fa-users-cog" - }, "config_profile_name": { "type": "string", "description": "Institutional config name.", @@ -184,22 +178,6 @@ "fa_icon": "fas fa-question-circle", "hidden": true }, - "publish_dir_mode": { - "type": "string", - "default": "copy", - "description": "Method used to save pipeline results to output directory.", - "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", - "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], - "hidden": true - }, "email_on_fail": { "type": "string", "description": "Email address for completion summary, only when pipeline fails.", @@ -260,13 +238,6 @@ "description": "Run this workflow with Conda. You can also use '-profile conda' instead of providing this parameter.", "hidden": true, "fa_icon": "fas fa-bacon" - }, - "singularity_pull_docker_container": { - "type": "boolean", - "description": "Instead of directly downloading Singularity images for use with Singularity, force the workflow to pull and convert Docker containers instead.", - "hidden": true, - "fa_icon": "fas fa-toolbox", - "help_text": "This may be useful for example if you are unable to directly pull Singularity containers to run the pipeline due to http/https proxy issues." } } } diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index b664bc8c..cddcbb3c 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -2,9 +2,7 @@ // Check input samplesheet and get read channels // -params.options = [:] - -include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' addParams( options: params.options ) +include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' workflow INPUT_CHECK { take: @@ -12,12 +10,14 @@ workflow INPUT_CHECK { main: SAMPLESHEET_CHECK ( samplesheet ) + .csv .splitCsv ( header:true, sep:',' ) .map { create_fastq_channels(it) } .set { reads } emit: - reads // channel: [ val(meta), [ reads ] ] + reads // channel: [ val(meta), [ reads ] ] + versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] } // Function to get list of [ meta, [ fastq_1, fastq_2 ] ] diff --git a/workflows/viralrecon.nf b/workflows/viralrecon.nf index 4a190788..d730a813 100644 --- a/workflows/viralrecon.nf +++ b/workflows/viralrecon.nf @@ -32,18 +32,10 @@ ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multi ======================================================================================== */ -// Don't overwrite global params.modules, create a copy instead and use that within the main script. -def modules = params.modules.clone() - -// -// MODULE: Local to the pipeline -// -include { GET_SOFTWARE_VERSIONS } from '../modules/local/get_software_versions' addParams( options: [publish_files : ['tsv':'']] ) - // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' addParams( options: [:] ) +include { INPUT_CHECK } from '../subworkflows/local/input_check' /* ======================================================================================== @@ -51,14 +43,12 @@ include { INPUT_CHECK } from '../subworkflows/local/input_check' addParams( opti ======================================================================================== */ -def multiqc_options = modules['multiqc'] -multiqc_options.args += params.multiqc_title ? Utils.joinModuleArgs(["--title \"$params.multiqc_title\""]) : '' - // // MODULE: Installed directly from nf-core/modules // -include { FASTQC } from '../modules/nf-core/modules/fastqc/main' addParams( options: modules['fastqc'] ) -include { MULTIQC } from '../modules/nf-core/modules/multiqc/main' addParams( options: multiqc_options ) +include { FASTQC } from '../modules/nf-core/modules/fastqc/main' +include { MULTIQC } from '../modules/nf-core/modules/multiqc/main' +include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' /* ======================================================================================== @@ -71,7 +61,7 @@ def multiqc_report = [] workflow VIRALRECON { - ch_software_versions = Channel.empty() + ch_versions = Channel.empty() // // SUBWORKFLOW: Read in samplesheet, validate and stage input files @@ -79,6 +69,7 @@ workflow VIRALRECON { INPUT_CHECK ( ch_input ) + ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) // // MODULE: Run FastQC @@ -86,21 +77,10 @@ workflow VIRALRECON { FASTQC ( INPUT_CHECK.out.reads ) - ch_software_versions = ch_software_versions.mix(FASTQC.out.version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(FASTQC.out.versions.first()) - // - // MODULE: Pipeline reporting - // - ch_software_versions - .map { it -> if (it) [ it.baseName, it ] } - .groupTuple() - .map { it[1][0] } - .flatten() - .collect() - .set { ch_software_versions } - - GET_SOFTWARE_VERSIONS ( - ch_software_versions.map { it }.collect() + CUSTOM_DUMPSOFTWAREVERSIONS ( + ch_versions.unique().collectFile(name: 'collated_versions.yml') ) // @@ -113,14 +93,14 @@ workflow VIRALRECON { ch_multiqc_files = ch_multiqc_files.mix(Channel.from(ch_multiqc_config)) ch_multiqc_files = ch_multiqc_files.mix(ch_multiqc_custom_config.collect().ifEmpty([])) ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(GET_SOFTWARE_VERSIONS.out.yaml.collect()) + ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) MULTIQC ( ch_multiqc_files.collect() ) - multiqc_report = MULTIQC.out.report.toList() - ch_software_versions = ch_software_versions.mix(MULTIQC.out.version.ifEmpty(null)) + multiqc_report = MULTIQC.out.report.toList() + ch_versions = ch_versions.mix(MULTIQC.out.versions) } /* From 62c34931fb4a263cc0450e4d78dbfb219d43a26c Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 6 Jan 2022 18:34:48 +0000 Subject: [PATCH 003/238] Get tests passing again with new DSL2 syntax --- assets/dummy_file.txt | 0 assets/schema_input.json | 3 +- conf/modules.config | 575 ++++-------------- modules.json | 94 +-- modules/local/asciigenome.nf | 44 +- modules/local/bcftools_isec.nf | 33 +- modules/local/collapse_primers.nf | 32 +- modules/local/cutadapt.nf | 38 +- modules/local/filter_blastn.nf | 25 +- modules/local/get_chrom_sizes.nf | 34 -- modules/local/ivar_variants_to_vcf.nf | 30 +- modules/local/kraken2_build.nf | 40 +- modules/local/make_bed_mask.nf | 29 +- modules/local/multiqc_custom_csv_from_map.nf | 27 - .../local/multiqc_custom_tsv_from_string.nf | 37 -- modules/local/multiqc_illumina.nf | 29 +- modules/local/multiqc_nanopore.nf | 29 +- modules/local/multiqc_tsv_from_list.nf | 25 + modules/local/plot_base_density.nf | 27 +- modules/local/plot_mosdepth_regions.nf | 30 +- modules/local/samplesheet_check.nf | 6 +- modules/local/snpeff_ann.nf | 33 +- modules/local/snpeff_build.nf | 34 +- modules/local/snpsift_extractfields.nf | 33 +- modules/nf-core/modules/abacas/functions.nf | 68 --- modules/nf-core/modules/abacas/main.nf | 30 +- modules/nf-core/modules/abacas/meta.yml | 6 +- .../modules/artic/guppyplex/functions.nf | 68 --- .../nf-core/modules/artic/guppyplex/main.nf | 30 +- .../nf-core/modules/artic/guppyplex/meta.yml | 6 +- .../nf-core/modules/artic/minion/functions.nf | 68 --- modules/nf-core/modules/artic/minion/main.nf | 38 +- modules/nf-core/modules/artic/minion/meta.yml | 6 +- .../modules/bandage/image/functions.nf | 68 --- modules/nf-core/modules/bandage/image/main.nf | 32 +- .../nf-core/modules/bandage/image/meta.yml | 7 +- .../modules/bcftools/consensus/functions.nf | 68 --- .../modules/bcftools/consensus/main.nf | 32 +- .../modules/bcftools/consensus/meta.yml | 7 +- .../modules/bcftools/mpileup/functions.nf | 68 --- .../nf-core/modules/bcftools/mpileup/main.nf | 42 +- .../nf-core/modules/bcftools/mpileup/meta.yml | 7 +- .../modules/bcftools/stats/functions.nf | 68 --- .../nf-core/modules/bcftools/stats/main.nf | 32 +- .../nf-core/modules/bcftools/stats/meta.yml | 7 +- .../modules/bedtools/genomecov/functions.nf | 68 --- .../modules/bedtools/genomecov/main.nf | 45 +- .../modules/bedtools/genomecov/meta.yml | 11 +- .../modules/bedtools/getfasta/functions.nf | 68 --- .../nf-core/modules/bedtools/getfasta/main.nf | 30 +- .../modules/bedtools/getfasta/meta.yml | 7 +- .../modules/bedtools/maskfasta/functions.nf | 68 --- .../modules/bedtools/maskfasta/main.nf | 30 +- .../modules/bedtools/maskfasta/meta.yml | 7 +- .../modules/bedtools/merge/functions.nf | 68 --- .../nf-core/modules/bedtools/merge/main.nf | 30 +- .../nf-core/modules/bedtools/merge/meta.yml | 7 +- .../nf-core/modules/blast/blastn/functions.nf | 68 --- modules/nf-core/modules/blast/blastn/main.nf | 32 +- modules/nf-core/modules/blast/blastn/meta.yml | 7 +- .../modules/blast/makeblastdb/functions.nf | 68 --- .../nf-core/modules/blast/makeblastdb/main.nf | 25 +- .../modules/blast/makeblastdb/meta.yml | 7 +- .../modules/bowtie2/align/functions.nf | 68 --- modules/nf-core/modules/bowtie2/align/main.nf | 57 +- .../nf-core/modules/bowtie2/align/meta.yml | 10 +- .../modules/bowtie2/build/functions.nf | 68 --- modules/nf-core/modules/bowtie2/build/main.nf | 30 +- .../nf-core/modules/bowtie2/build/meta.yml | 7 +- .../nf-core/modules/cat/fastq/functions.nf | 68 --- modules/nf-core/modules/cat/fastq/main.nf | 39 +- modules/nf-core/modules/cat/fastq/meta.yml | 6 + .../custom/dumpsoftwareversions/main.nf | 21 + .../custom/dumpsoftwareversions/meta.yml | 34 ++ .../templates/dumpsoftwareversions.py | 89 +++ .../modules/custom/getchromsizes/main.nf | 29 + .../modules/custom/getchromsizes/meta.yml | 39 ++ modules/nf-core/modules/fastp/functions.nf | 68 --- modules/nf-core/modules/fastp/main.nf | 56 +- modules/nf-core/modules/fastp/meta.yml | 15 +- modules/nf-core/modules/gunzip/functions.nf | 68 --- modules/nf-core/modules/gunzip/main.nf | 38 +- modules/nf-core/modules/gunzip/meta.yml | 13 +- .../modules/ivar/consensus/functions.nf | 68 --- .../nf-core/modules/ivar/consensus/main.nf | 33 +- .../nf-core/modules/ivar/consensus/meta.yml | 7 +- .../nf-core/modules/ivar/trim/functions.nf | 68 --- modules/nf-core/modules/ivar/trim/main.nf | 30 +- modules/nf-core/modules/ivar/trim/meta.yml | 7 +- .../modules/ivar/variants/functions.nf | 68 --- modules/nf-core/modules/ivar/variants/main.nf | 33 +- .../nf-core/modules/ivar/variants/meta.yml | 7 +- .../modules/kraken2/kraken2/functions.nf | 68 --- .../nf-core/modules/kraken2/kraken2/main.nf | 31 +- .../nf-core/modules/kraken2/kraken2/meta.yml | 7 +- modules/nf-core/modules/minia/functions.nf | 68 --- modules/nf-core/modules/minia/main.nf | 33 +- modules/nf-core/modules/minia/meta.yml | 7 +- modules/nf-core/modules/mosdepth/functions.nf | 68 --- modules/nf-core/modules/mosdepth/main.nf | 32 +- modules/nf-core/modules/mosdepth/meta.yml | 7 +- modules/nf-core/modules/nanoplot/functions.nf | 68 --- modules/nf-core/modules/nanoplot/main.nf | 30 +- modules/nf-core/modules/nanoplot/meta.yml | 7 +- .../nf-core/modules/nextclade/functions.nf | 68 --- modules/nf-core/modules/nextclade/main.nf | 30 +- modules/nf-core/modules/nextclade/meta.yml | 6 +- modules/nf-core/modules/pangolin/functions.nf | 68 --- modules/nf-core/modules/pangolin/main.nf | 32 +- modules/nf-core/modules/pangolin/meta.yml | 7 +- .../collectmultiplemetrics/functions.nf | 68 --- .../picard/collectmultiplemetrics/main.nf | 32 +- .../picard/collectmultiplemetrics/meta.yml | 7 +- .../picard/markduplicates/functions.nf | 68 --- .../modules/picard/markduplicates/main.nf | 39 +- .../modules/picard/markduplicates/meta.yml | 84 +-- .../nf-core/modules/plasmidid/functions.nf | 68 --- modules/nf-core/modules/plasmidid/main.nf | 30 +- modules/nf-core/modules/plasmidid/meta.yml | 6 +- modules/nf-core/modules/pycoqc/functions.nf | 68 --- modules/nf-core/modules/pycoqc/main.nf | 28 +- modules/nf-core/modules/pycoqc/meta.yml | 6 +- modules/nf-core/modules/quast/functions.nf | 68 --- modules/nf-core/modules/quast/main.nf | 32 +- modules/nf-core/modules/quast/meta.yml | 9 +- .../modules/samtools/flagstat/functions.nf | 68 --- .../nf-core/modules/samtools/flagstat/main.nf | 30 +- .../modules/samtools/flagstat/meta.yml | 7 +- .../modules/samtools/idxstats/functions.nf | 68 --- .../nf-core/modules/samtools/idxstats/main.nf | 28 +- .../modules/samtools/idxstats/meta.yml | 7 +- .../modules/samtools/index/functions.nf | 68 --- .../nf-core/modules/samtools/index/main.nf | 38 +- .../nf-core/modules/samtools/index/meta.yml | 12 +- .../modules/samtools/mpileup/functions.nf | 68 --- .../nf-core/modules/samtools/mpileup/main.nf | 32 +- .../nf-core/modules/samtools/mpileup/meta.yml | 7 +- .../modules/samtools/sort/functions.nf | 68 --- modules/nf-core/modules/samtools/sort/main.nf | 32 +- .../nf-core/modules/samtools/sort/meta.yml | 7 +- .../modules/samtools/stats/functions.nf | 68 --- .../nf-core/modules/samtools/stats/main.nf | 35 +- .../nf-core/modules/samtools/stats/meta.yml | 28 +- .../modules/samtools/view/functions.nf | 68 --- modules/nf-core/modules/samtools/view/main.nf | 41 +- .../nf-core/modules/samtools/view/meta.yml | 22 +- modules/nf-core/modules/spades/functions.nf | 68 --- modules/nf-core/modules/spades/main.nf | 63 +- modules/nf-core/modules/spades/meta.yml | 28 +- .../nf-core/modules/tabix/bgzip/functions.nf | 68 --- modules/nf-core/modules/tabix/bgzip/main.nf | 32 +- modules/nf-core/modules/tabix/bgzip/meta.yml | 7 +- .../nf-core/modules/tabix/tabix/functions.nf | 68 --- modules/nf-core/modules/tabix/tabix/main.nf | 30 +- modules/nf-core/modules/tabix/tabix/meta.yml | 7 +- .../nf-core/modules/unicycler/functions.nf | 68 --- modules/nf-core/modules/unicycler/main.nf | 46 +- modules/nf-core/modules/unicycler/meta.yml | 25 +- modules/nf-core/modules/untar/functions.nf | 68 --- modules/nf-core/modules/untar/main.nf | 36 +- modules/nf-core/modules/untar/meta.yml | 7 +- subworkflows/local/assembly_minia.nf | 25 +- subworkflows/local/assembly_qc.nf | 67 +- subworkflows/local/assembly_spades.nf | 57 +- subworkflows/local/assembly_unicycler.nf | 46 +- subworkflows/local/input_check.nf | 15 +- subworkflows/local/make_consensus.nf | 75 ++- subworkflows/local/prepare_genome_illumina.nf | 152 +++-- subworkflows/local/prepare_genome_nanopore.nf | 59 +- subworkflows/local/primer_trim_ivar.nf | 40 -- subworkflows/local/snpeff_snpsift.nf | 55 +- subworkflows/local/variants_bcftools.nf | 202 +++--- subworkflows/local/variants_ivar.nf | 238 ++++---- subworkflows/nf-core/align_bowtie2.nf | 49 +- subworkflows/nf-core/bam_sort_samtools.nf | 50 +- subworkflows/nf-core/bam_stats_samtools.nf | 33 +- subworkflows/nf-core/fastqc_fastp.nf | 74 ++- subworkflows/nf-core/filter_bam_samtools.nf | 41 +- .../nf-core/mark_duplicates_picard.nf | 63 +- subworkflows/nf-core/primer_trim_ivar.nf | 45 ++ subworkflows/nf-core/vcf_bgzip_tabix_stats.nf | 31 +- subworkflows/nf-core/vcf_tabix_stats.nf | 29 +- workflows/illumina.nf | 302 ++++----- workflows/nanopore.nf | 166 +++-- 184 files changed, 2555 insertions(+), 5867 deletions(-) create mode 100644 assets/dummy_file.txt delete mode 100644 modules/local/get_chrom_sizes.nf delete mode 100644 modules/local/multiqc_custom_csv_from_map.nf delete mode 100644 modules/local/multiqc_custom_tsv_from_string.nf create mode 100644 modules/local/multiqc_tsv_from_list.nf delete mode 100644 modules/nf-core/modules/abacas/functions.nf delete mode 100644 modules/nf-core/modules/artic/guppyplex/functions.nf delete mode 100644 modules/nf-core/modules/artic/minion/functions.nf delete mode 100644 modules/nf-core/modules/bandage/image/functions.nf delete mode 100644 modules/nf-core/modules/bcftools/consensus/functions.nf delete mode 100644 modules/nf-core/modules/bcftools/mpileup/functions.nf delete mode 100644 modules/nf-core/modules/bcftools/stats/functions.nf delete mode 100644 modules/nf-core/modules/bedtools/genomecov/functions.nf delete mode 100644 modules/nf-core/modules/bedtools/getfasta/functions.nf delete mode 100644 modules/nf-core/modules/bedtools/maskfasta/functions.nf delete mode 100644 modules/nf-core/modules/bedtools/merge/functions.nf delete mode 100644 modules/nf-core/modules/blast/blastn/functions.nf delete mode 100644 modules/nf-core/modules/blast/makeblastdb/functions.nf delete mode 100644 modules/nf-core/modules/bowtie2/align/functions.nf delete mode 100644 modules/nf-core/modules/bowtie2/build/functions.nf delete mode 100644 modules/nf-core/modules/cat/fastq/functions.nf create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/main.nf create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml create mode 100644 modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py create mode 100644 modules/nf-core/modules/custom/getchromsizes/main.nf create mode 100644 modules/nf-core/modules/custom/getchromsizes/meta.yml delete mode 100644 modules/nf-core/modules/fastp/functions.nf delete mode 100644 modules/nf-core/modules/gunzip/functions.nf delete mode 100644 modules/nf-core/modules/ivar/consensus/functions.nf delete mode 100644 modules/nf-core/modules/ivar/trim/functions.nf delete mode 100644 modules/nf-core/modules/ivar/variants/functions.nf delete mode 100644 modules/nf-core/modules/kraken2/kraken2/functions.nf delete mode 100644 modules/nf-core/modules/minia/functions.nf delete mode 100644 modules/nf-core/modules/mosdepth/functions.nf delete mode 100644 modules/nf-core/modules/nanoplot/functions.nf delete mode 100644 modules/nf-core/modules/nextclade/functions.nf delete mode 100644 modules/nf-core/modules/pangolin/functions.nf delete mode 100644 modules/nf-core/modules/picard/collectmultiplemetrics/functions.nf delete mode 100644 modules/nf-core/modules/picard/markduplicates/functions.nf delete mode 100644 modules/nf-core/modules/plasmidid/functions.nf delete mode 100644 modules/nf-core/modules/pycoqc/functions.nf delete mode 100644 modules/nf-core/modules/quast/functions.nf delete mode 100644 modules/nf-core/modules/samtools/flagstat/functions.nf delete mode 100644 modules/nf-core/modules/samtools/idxstats/functions.nf delete mode 100644 modules/nf-core/modules/samtools/index/functions.nf delete mode 100644 modules/nf-core/modules/samtools/mpileup/functions.nf delete mode 100644 modules/nf-core/modules/samtools/sort/functions.nf delete mode 100644 modules/nf-core/modules/samtools/stats/functions.nf delete mode 100644 modules/nf-core/modules/samtools/view/functions.nf delete mode 100644 modules/nf-core/modules/spades/functions.nf delete mode 100644 modules/nf-core/modules/tabix/bgzip/functions.nf delete mode 100644 modules/nf-core/modules/tabix/tabix/functions.nf delete mode 100644 modules/nf-core/modules/unicycler/functions.nf delete mode 100644 modules/nf-core/modules/untar/functions.nf delete mode 100644 subworkflows/local/primer_trim_ivar.nf create mode 100644 subworkflows/nf-core/primer_trim_ivar.nf diff --git a/assets/dummy_file.txt b/assets/dummy_file.txt new file mode 100644 index 00000000..e69de29b diff --git a/assets/schema_input.json b/assets/schema_input.json index d44d187f..9e9d343e 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -37,8 +37,7 @@ } }, "required": [ - "sample", - "fastq_1" + "sample" ] } } diff --git a/conf/modules.config b/conf/modules.config index 1ebbc94f..4e724fc7 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -10,467 +10,130 @@ ---------------------------------------------------------------------------------------- */ -params { - modules { - 'sra_ids_to_runinfo' { - publish_dir = 'public_data' - publish_files = ['tsv':'runinfo'] - } - 'sra_runinfo_to_ftp' { - publish_dir = 'public_data' - publish_files = ['tsv':'runinfo'] - } - 'sra_fastq_ftp' { - args = '-C - --max-time 1200' - publish_dir = 'public_data' - publish_files = ['fastq.gz':'', 'md5':'md5'] - } - 'sra_to_samplesheet' { - publish_dir = 'public_data' - publish_files = false - } - 'sra_merge_samplesheet' { - publish_dir = 'public_data' - } - 'nanopore_collapse_primers' { - publish_dir = 'genome' - } - 'nanopore_snpeff_build' { - publish_dir = 'genome' - } - 'nanopore_pycoqc' { - publish_dir = 'pycoqc' - } - 'nanopore_artic_guppyplex' { - args = '--min-length 400 --max-length 700' - publish_files = false - publish_dir = 'guppyplex' - } - 'nanopore_nanoplot' { - publish_by_meta = true - } - 'nanopore_artic_minion' { - args = '--normalise 500' - publish_files = ['.sorted.bam':'', '.sorted.bam.bai':'', 'fail.vcf':'', 'merged.vcf':'', 'primers.vcf':'', 'gz':'', 'tbi':'', '.consensus.fasta':''] - publish_dir = "${params.artic_minion_caller}" - } - 'nanopore_filter_bam' { - args = '-b -F 4' - suffix = '.mapped.sorted' - publish_dir = "${params.artic_minion_caller}" - } - 'nanopore_filter_bam_stats' { - suffix = '.mapped.sorted' - publish_files = ['bai':'', 'stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] - publish_dir = "${params.artic_minion_caller}" - } - 'nanopore_bcftools_stats' { - publish_files = ['txt':''] - publish_dir = "${params.artic_minion_caller}/bcftools_stats" - } - 'nanopore_mosdepth_genome' { - args = '--fast-mode' - publish_files = ['summary.txt':''] - publish_dir = "${params.artic_minion_caller}/mosdepth/genome" - } - 'nanopore_plot_mosdepth_regions_genome' { - args = '--input_suffix .regions.bed.gz' - publish_files = ['tsv':'', 'pdf': ''] - publish_dir = "${params.artic_minion_caller}/mosdepth/genome" - } - 'nanopore_mosdepth_amplicon' { - args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' - publish_files = ['summary.txt':''] - publish_dir = "${params.artic_minion_caller}/mosdepth/amplicon" - } - 'nanopore_plot_mosdepth_regions_amplicon' { - args = '--input_suffix .regions.bed.gz' - publish_files = ['tsv':'', 'pdf': ''] - publish_dir = "${params.artic_minion_caller}/mosdepth/amplicon" - } - 'nanopore_pangolin' { - publish_dir = "${params.artic_minion_caller}/pangolin" - } - 'nanopore_nextclade' { - publish_files = ['csv':''] - publish_dir = "${params.artic_minion_caller}/nextclade" - } - 'nanopore_asciigenome' { - publish_dir = "${params.artic_minion_caller}/asciigenome" - publish_by_meta = true - } - 'nanopore_quast' { - publish_files = ['quast':''] - publish_dir = "${params.artic_minion_caller}" - } - 'nanopore_snpeff' { - publish_files = ['csv':'', 'txt':'', 'html':''] - publish_dir = "${params.artic_minion_caller}/snpeff" - } - 'nanopore_snpeff_bgzip' { - suffix = '.snpeff' - publish_dir = "${params.artic_minion_caller}/snpeff" - } - 'nanopore_snpeff_tabix' { - args = '-p vcf -f' - suffix = '.snpeff' - publish_dir = "${params.artic_minion_caller}/snpeff" - } - 'nanopore_snpeff_stats' { - suffix = '.snpeff' - publish_files = ['txt':'bcftools_stats'] - publish_dir = "${params.artic_minion_caller}/snpeff" - } - 'nanopore_snpsift' { - publish_dir = "${params.artic_minion_caller}/snpeff" - } - 'nanopore_multiqc' { - args = '' - publish_files = ['_data':"${params.artic_minion_caller}", 'html':"${params.artic_minion_caller}", 'csv':"${params.artic_minion_caller}"] - } - 'illumina_bedtools_getfasta' { - args = '-s -nameOnly' - publish_dir = 'genome' - } - 'illumina_collapse_primers_illumina' { - publish_dir = 'genome' - } - 'illumina_bowtie2_build' { - args = '--seed 1' - publish_dir = 'genome/index' - } - 'illumina_snpeff_build' { - publish_dir = 'genome/db' - } - 'illumina_blast_makeblastdb' { - args = '-parse_seqids -dbtype nucl' - publish_dir = 'genome/db' - } - 'illumina_kraken2_build' { - args = '' - args2 = '' - args3 = '' - publish_dir = 'genome/db' - } - 'illumina_cat_fastq' { - publish_files = false - publish_dir = 'fastq' - } - 'illumina_fastqc_raw' { - args = '--quiet' - publish_dir = 'fastqc/raw' - } - 'illumina_fastqc_trim' { - args = '--quiet' - publish_dir = 'fastqc/trim' - } - 'illumina_fastp' { - args = '--cut_front --cut_tail --trim_poly_x --cut_mean_quality 30 --qualified_quality_phred 30 --unqualified_percent_limit 10 --length_required 50' - publish_files = ['json':'', 'html':'', 'log': 'log'] - } - 'illumina_kraken2_kraken2' { - args = '--report-zero-counts' - publish_files = ['txt':''] - } - 'illumina_bowtie2_align' { - args = '--local --very-sensitive-local --seed 1' - args2 = '-F4' - publish_files = ['log':'log'] - publish_dir = 'variants/bowtie2' - } - 'illumina_bowtie2_sort_bam' { - suffix = '.sorted' - publish_files = ['bam':'', 'bai':'', 'stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] - publish_dir = 'variants/bowtie2' - } - 'illumina_ivar_trim' { - args = '-m 30 -q 20' - suffix = '.ivar_trim' - publish_files = ['log':'log'] - publish_dir = 'variants/bowtie2' - } - 'illumina_ivar_trim_sort_bam' { - suffix = '.ivar_trim.sorted' - publish_files = ['stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] - publish_dir = 'variants/bowtie2' - } - 'illumina_picard_markduplicates' { - args = 'ASSUME_SORTED=true VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' - suffix = '.markduplicates.sorted' - publish_files = ['bam': '', 'metrics.txt':'picard_metrics'] - publish_dir = 'variants/bowtie2' - } - 'illumina_picard_markduplicates_sort_bam' { - suffix = '.markduplicates.sorted' - publish_files = ['bai':'', 'stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] - publish_dir = 'variants/bowtie2' - } - 'illumina_picard_collectmultiplemetrics' { - args = 'VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' - publish_files = ['metrics':'picard_metrics', 'pdf': 'picard_metrics/pdf'] - publish_dir = 'variants/bowtie2' - } - 'illumina_mosdepth_genome' { - args = '--fast-mode' - publish_files = ['summary.txt':''] - publish_dir = 'variants/bowtie2/mosdepth/genome' - } - 'illumina_plot_mosdepth_regions_genome' { - args = '--input_suffix .regions.bed.gz' - publish_files = ['tsv':'', 'pdf': ''] - publish_dir = 'variants/bowtie2/mosdepth/genome' - } - 'illumina_mosdepth_amplicon' { - args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' - publish_files = ['summary.txt':''] - publish_dir = 'variants/bowtie2/mosdepth/amplicon' - } - 'illumina_plot_mosdepth_regions_amplicon' { - args = '--input_suffix .regions.bed.gz' - publish_files = ['tsv':'', 'pdf': ''] - publish_dir = 'variants/bowtie2/mosdepth/amplicon' - } - 'illumina_ivar_variants' { - args = '-t 0.25 -q 20 -m 10' - args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0' - publish_dir = 'variants/ivar' - } - 'illumina_ivar_variants_to_vcf' { - publish_files = ['log':'log'] - publish_dir = 'variants/ivar' - } - 'illumina_ivar_tabix_bgzip' { - publish_dir = 'variants/ivar' - } - 'illumina_ivar_tabix_tabix' { - args = '-p vcf -f' - publish_dir = 'variants/ivar' - } - 'illumina_ivar_bcftools_stats' { - publish_files = ['txt':'bcftools_stats'] - publish_dir = 'variants/ivar' - } - 'illumina_ivar_consensus' { - args = '-t 0.75 -q 20 -m 10 -n N' - args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' - suffix = '.consensus' - publish_dir = 'variants/ivar/consensus' - } - 'illumina_ivar_consensus_plot' { - suffix = '.consensus' - publish_dir = 'variants/ivar/consensus/base_qc' - } - 'illumina_ivar_snpeff' { - publish_files = ['csv':'', 'txt':'', 'html':''] - publish_dir = 'variants/ivar/snpeff' - } - 'illumina_ivar_snpeff_bgzip' { - suffix = '.snpeff' - publish_dir = 'variants/ivar/snpeff' - } - 'illumina_ivar_snpeff_tabix' { - args = '-p vcf -f' - suffix = '.snpeff' - publish_dir = 'variants/ivar/snpeff' - } - 'illumina_ivar_snpeff_stats' { - suffix = '.snpeff' - publish_files = ['txt':'bcftools_stats'] - publish_dir = 'variants/ivar/snpeff' - } - 'illumina_ivar_snpsift' { - publish_dir = 'variants/ivar/snpeff' - } - 'illumina_ivar_quast' { - publish_files = ['quast':''] - publish_dir = 'variants/ivar' - } - 'illumina_ivar_pangolin' { - publish_dir = 'variants/ivar/pangolin' - } - 'illumina_ivar_nextclade' { - publish_files = ['csv':''] - publish_dir = 'variants/ivar/nextclade' - } - 'illumina_ivar_asciigenome' { - publish_dir = 'variants/ivar/asciigenome' - publish_by_meta = true - } - 'illumina_bcftools_mpileup' { - args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' - args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' - args3 = "--include 'INFO/DP>=10'" - publish_files = ['gz':'', 'gz.tbi':'', 'stats.txt':'bcftools_stats'] - publish_dir = 'variants/bcftools' - } - 'illumina_bcftools_consensus_genomecov' { - args = "-bga | awk '\$4 < 10'" - suffix = '.coverage' - publish_files = false - publish_dir = 'variants/bcftools' - } - 'illumina_bcftools_consensus_merge' { - suffix = '.coverage.merged' - publish_files = false - publish_dir = 'variants/bcftools' - } - 'illumina_bcftools_consensus_mask' { - suffix = '.coverage.masked' - publish_files = false - publish_dir = 'variants/bcftools' - } - 'illumina_bcftools_consensus_maskfasta' { - suffix = '.masked' - publish_files = false - publish_dir = 'variants/bcftools' - } - 'illumina_bcftools_consensus_bcftools' { - suffix = '.consensus' - publish_dir = 'variants/bcftools/consensus' - } - 'illumina_bcftools_consensus_plot' { - suffix = '.consensus' - publish_dir = 'variants/bcftools/consensus/base_qc' - } - 'illumina_bcftools_snpeff' { - publish_files = ['csv':'', 'txt':'', 'html':''] - publish_dir = 'variants/bcftools/snpeff' - } - 'illumina_bcftools_snpeff_bgzip' { - suffix = '.snpeff' - publish_dir = 'variants/bcftools/snpeff' - } - 'illumina_bcftools_snpeff_tabix' { - args = '-p vcf -f' - suffix = '.snpeff' - publish_dir = 'variants/bcftools/snpeff' - } - 'illumina_bcftools_snpeff_stats' { - suffix = '.snpeff' - publish_files = ['txt':'bcftools_stats'] - publish_dir = 'variants/bcftools/snpeff' - } - 'illumina_bcftools_snpsift' { - publish_dir = 'variants/bcftools/snpeff' - } - 'illumina_bcftools_quast' { - publish_files = ['quast':''] - publish_dir = 'variants/bcftools' - } - 'illumina_bcftools_pangolin' { - publish_dir = 'variants/bcftools/pangolin' - } - 'illumina_bcftools_nextclade' { - publish_files = ['csv':''] - publish_dir = 'variants/bcftools/nextclade' - } - 'illumina_bcftools_asciigenome' { - publish_dir = 'variants/bcftools/asciigenome' - publish_by_meta = true - } - 'illumina_bcftools_isec' { - args = '--nfiles +2 --output-type z' - publish_dir = 'variants/intersect' - } - 'illumina_cutadapt' { - args = '--overlap 5 --minimum-length 30 --error-rate 0.1' - suffix = '.primer_trim' - publish_files = ['log':'log'] - publish_dir = 'assembly/cutadapt' - } - 'illumina_cutadapt_fastqc' { - args = '--quiet' - suffix = 'primer_trim' - publish_dir = 'assembly/cutadapt/fastqc' - } - 'illumina_spades' { - args = '' - publish_files = ['log':'log', 'fa':'', 'gfa':''] - publish_dir = "assembly/spades/${params.spades_mode}" - } - 'illumina_spades_bandage' { - args = '--height 1000' - publish_dir = "assembly/spades/${params.spades_mode}/bandage" - } - 'illumina_spades_blastn' { - args = "-outfmt '6 stitle std slen qlen qcovs'" - publish_dir = "assembly/spades/${params.spades_mode}/blastn" - } - 'illumina_spades_blastn_filter' { - suffix = '.filter.blastn' - publish_dir = "assembly/spades/${params.spades_mode}/blastn" - } - 'illumina_spades_abacas' { - args = '-m -p nucmer' - publish_dir = "assembly/spades/${params.spades_mode}/abacas" - } - 'illumina_spades_plasmidid' { - args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' - publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] - publish_dir = "assembly/spades/${params.spades_mode}/plasmidid" - } - 'illumina_spades_quast' { - publish_files = ['quast':''] - publish_dir = "assembly/spades/${params.spades_mode}" - } - 'illumina_unicycler' { - publish_files = ['log':'log', 'fa':'', 'gfa':''] - publish_dir = 'assembly/unicycler' - } - 'illumina_unicycler_bandage' { - args = '--height 1000' - publish_dir = 'assembly/unicycler/bandage' - } - 'illumina_unicycler_blastn' { - args = "-outfmt '6 stitle std slen qlen qcovs'" - publish_dir = 'assembly/unicycler/blastn' - } - 'illumina_unicycler_blastn_filter' { - suffix = '.filter.blastn' - publish_dir = 'assembly/unicycler/blastn' - } - 'illumina_unicycler_abacas' { - args = '-m -p nucmer' - publish_dir = 'assembly/unicycler/abacas' - } - 'illumina_unicycler_plasmidid' { - args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' - publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] - publish_dir = 'assembly/unicycler/plasmidid' - } - 'illumina_unicycler_quast' { - publish_files = ['quast':''] - publish_dir = 'assembly/unicycler' - } - 'illumina_minia' { - args = '-kmer-size 31 -abundance-min 20' - publish_dir = 'assembly/minia' - } - 'illumina_minia_blastn' { - args = "-outfmt '6 stitle std slen qlen qcovs'" - publish_dir = 'assembly/minia/blastn' - } - 'illumina_minia_blastn_filter' { - suffix = '.filter.blastn' - publish_dir = 'assembly/minia/blastn' +// +// General configuration options +// + +process { + publishDir = [ + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + + withName: SAMPLESHEET_CHECK { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: CUSTOM_DUMPSOFTWAREVERSIONS { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: 'copy', + pattern: '*_versions.yml' + ] + } +} + +// +// Generic genome preparation options +// + +process { + if (!params.skip_asciigenome) { + withName: CUSTOM_GETCHROMSIZES { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] } - 'illumina_minia_abacas' { - args = '-m -p nucmer' - publish_dir = 'assembly/minia/abacas' + } +} + +// +// Illumina genome preparation options +// + +if (params.platform == 'illumina') { + process { + + withName: 'UNTAR_.*' { + ext.args2 = '--no-same-owner' } - 'illumina_minia_plasmidid' { - args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' - publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] - publish_dir = 'assembly/minia/plasmidid' + + withName: BLAST_MAKEBLASTDB { + ext.args = '-parse_seqids -dbtype nucl' + publishDir = [ + path: { "${params.outdir}/genome/db" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] } - 'illumina_minia_quast' { - publish_files = ['quast':''] - publish_dir = 'assembly/minia' + } +} + +if (params.platform == 'illumina') { + if (!params.skip_variants) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, + mode: 'copy', + pattern: "*.{stats,flagstat,idxstats}" + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { + ext.prefix = { "${meta.id}.sorted" } + publishDir = [ + path: { "${params.outdir}/variants/bowtie2" }, + mode: 'copy', + pattern: "*.bam" + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { + publishDir = [ + path: { "${params.outdir}/variants/bowtie2" }, + mode: 'copy', + pattern: "*.bai" + ] + } } - 'illumina_multiqc' { - args = '' - publish_files = ['_data':'', 'html':''] + + process { + withName: BCFTOOLS_MPILEUP { + ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' + ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' + ext.args3 = "--include 'INFO/DP>=10'" + } + + withName: BEDTOOLS_GENOMECOV { + ext.args = "-bga | awk '\$4 < 10'" + ext.prefix = { "${meta.id}.coverage" } + } + + withName: BEDTOOLS_MERGE { + ext.prefix = { "${meta.id}.coverage.merged" } + } + + withName: BCFTOOLS_CONSENSUS { + ext.prefix = { "${meta.id}.consensus" } + } } } +} +if (params.platform == 'nanopore') { + process { + withName: SAMTOOLS_VIEW { + ext.args = '-b -F 4' + } + } } diff --git a/modules.json b/modules.json index 46636fad..51a5b27f 100644 --- a/modules.json +++ b/modules.json @@ -4,139 +4,145 @@ "repos": { "nf-core/modules": { "abacas": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "artic/guppyplex": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "artic/minion": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" }, "bandage/image": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bcftools/consensus": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bcftools/mpileup": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bcftools/stats": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bedtools/genomecov": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bedtools/getfasta": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" }, "bedtools/maskfasta": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bedtools/merge": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "blast/blastn": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "blast/makeblastdb": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "bowtie2/align": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bowtie2/build": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "e3285528aca2733ff2d544cb5e5fcc34599226f3" }, "cat/fastq": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "826a5603db5cf5b4f1e55cef9cc0b7c37d3c7e70" + }, + "custom/dumpsoftwareversions": { + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + }, + "custom/getchromsizes": { + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "fastp": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "cd94731789aa516631e5ea11e0f49469f2ba82dd" }, "fastqc": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "gunzip": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "ivar/consensus": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "ivar/trim": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "ivar/variants": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "kraken2/kraken2": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "minia": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "mosdepth": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "nanoplot": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "nextclade": { - "git_sha": "29c847424034eb04765d7378fb384ad3094a66a6" + "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" }, "pangolin": { - "git_sha": "e7e30b6da631ce5288151af4e46488ac6d294ff4" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "picard/collectmultiplemetrics": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "0d1e21686a586447b7592e40da9b3a7cdeedf03c" }, "picard/markduplicates": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "0d1e21686a586447b7592e40da9b3a7cdeedf03c" }, "plasmidid": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" }, "pycoqc": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "quast": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" }, "samtools/flagstat": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "samtools/idxstats": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "samtools/index": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "0fafaeebf52cc5ab554b83297ed02a48d852a848" }, "samtools/mpileup": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "samtools/sort": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "samtools/stats": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "samtools/view": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "spades": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "tabix/bgzip": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "tabix/tabix": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "unicycler": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "untar": { - "git_sha": "e937c7950af70930d1f34bb961403d9d2aa81c7d" + "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" } } } diff --git a/modules/local/asciigenome.nf b/modules/local/asciigenome.nf index 6f83c915..30ad698a 100644 --- a/modules/local/asciigenome.nf +++ b/modules/local/asciigenome.nf @@ -1,41 +1,29 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process ASCIIGENOME { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::asciigenome=1.16.0 bioconda::bedtools=2.30.0" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/mulled-v2-093691b47d719890dc19ac0c13c4528e9776897f:27211b8c38006480d69eb1be3ef09a7bf0a49d76-0" - } else { - container "quay.io/biocontainers/mulled-v2-093691b47d719890dc19ac0c13c4528e9776897f:27211b8c38006480d69eb1be3ef09a7bf0a49d76-0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-093691b47d719890dc19ac0c13c4528e9776897f:27211b8c38006480d69eb1be3ef09a7bf0a49d76-0' : + 'quay.io/biocontainers/mulled-v2-093691b47d719890dc19ac0c13c4528e9776897f:27211b8c38006480d69eb1be3ef09a7bf0a49d76-0' }" input: tuple val(meta), path(bam), path(vcf) - path fasta - path sizes - path gff - path bed - val window - val track_height + path fasta + path sizes + path gff + path bed + val window + val track_height output: tuple val(meta), path("*pdf"), emit: pdf - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" - def gff_track = gff ? "$gff" : '' - def bed_track = bed ? "$bed" : '' + def prefix = task.ext.prefix ?: "${meta.id}" + def gff_track = gff ? "$gff" : '' + def bed_track = bed ? "$bed" : '' def paired_end = meta.single_end ? '' : '&& readsAsPairs -on' """ zcat $vcf \\ @@ -61,6 +49,10 @@ process ASCIIGENOME { $gff_track \\ > /dev/null - echo \$(ASCIIGenome -ni --version 2>&1) | sed -e "s/ASCIIGenome //g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + asciigenome: \$(echo \$(ASCIIGenome -ni --version 2>&1) | sed -e "s/ASCIIGenome //g") + bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") + END_VERSIONS """ } diff --git a/modules/local/bcftools_isec.nf b/modules/local/bcftools_isec.nf index f9b6ffb6..da4cc2d8 100644 --- a/modules/local/bcftools_isec.nf +++ b/modules/local/bcftools_isec.nf @@ -1,38 +1,31 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BCFTOOLS_ISEC { tag "$meta.id" - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } + label 'process_low' - conda (params.enable_conda ? "bioconda::bcftools=1.11" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bcftools:1.11--h7c999a4_0" - } else { - container "quay.io/biocontainers/bcftools:1.11--h7c999a4_0" - } + conda (params.enable_conda ? 'bioconda::bcftools=1.13' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bcftools:1.13--h3a49de5_0' : + 'quay.io/biocontainers/bcftools:1.13--h3a49de5_0' }" input: tuple val(meta), path('ivar/*'), path('ivar/*'), path('bcftools/*'), path('bcftools/*') output: tuple val(meta), path("${prefix}"), emit: results - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" """ bcftools isec \\ - $options.args \\ + $args \\ -p $prefix \\ */*.vcf.gz - echo \$(bcftools --version 2>&1) | sed 's/^.*bcftools //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/local/collapse_primers.nf b/modules/local/collapse_primers.nf index 2b81410f..78e371bd 100644 --- a/modules/local/collapse_primers.nf +++ b/modules/local/collapse_primers.nf @@ -1,36 +1,32 @@ -// Import generic module functions -include { saveFiles } from './functions' - -params.options = [:] - process COLLAPSE_PRIMERS { tag "$bed" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'primers', meta:[:], publish_by_meta:[]) } - conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } + conda (params.enable_conda ? "conda-forge::python=3.9.5" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/python:3.9--1' : + 'quay.io/biocontainers/python:3.9--1' }" input: path bed - val left_suffix - val right_suffix + val left_suffix + val right_suffix output: - path '*.bed', emit: bed + path '*.bed' , emit: bed + path "versions.yml", emit: versions - script: + script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ """ collapse_primer_bed.py \\ --left_primer_suffix $left_suffix \\ --right_primer_suffix $right_suffix \\ $bed \\ ${bed.baseName}.collapsed.bed + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS """ } diff --git a/modules/local/cutadapt.nf b/modules/local/cutadapt.nf index 7d75385b..a6bf3af3 100644 --- a/modules/local/cutadapt.nf +++ b/modules/local/cutadapt.nf @@ -1,48 +1,40 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process CUTADAPT { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::cutadapt=3.2' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/cutadapt:3.2--py38h0213d0e_0' - } else { - container 'quay.io/biocontainers/cutadapt:3.2--py38h0213d0e_0' - } + conda (params.enable_conda ? 'bioconda::cutadapt=3.4' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/cutadapt:3.4--py39h38f01e4_1' : + 'quay.io/biocontainers/cutadapt:3.4--py39h38f01e4_1' }" input: tuple val(meta), path(reads) - path adapters + path adapters output: tuple val(meta), path('*.fastq.gz'), emit: reads tuple val(meta), path('*.log') , emit: log - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" - def paired = meta.single_end ? "-a file:adapters.sub.fa" : "-a file:adapters.sub.fa -A file:adapters.sub.fa" - def trimmed = meta.single_end ? "-o ${prefix}.fastq.gz" : "-o ${prefix}_1.fastq.gz -p ${prefix}_2.fastq.gz" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def paired = meta.single_end ? "-a file:adapters.sub.fa" : "-a file:adapters.sub.fa -A file:adapters.sub.fa" + def trimmed = meta.single_end ? "-o ${prefix}.fastq.gz" : "-o ${prefix}_1.fastq.gz -p ${prefix}_2.fastq.gz" """ sed -r '/^[ACTGactg]+\$/ s/\$/X/g' $adapters > adapters.sub.fa cutadapt \\ --cores $task.cpus \\ - $options.args \\ + $args \\ $paired \\ $trimmed \\ $reads \\ > ${prefix}.cutadapt.log - echo \$(cutadapt --version) > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cutadapt: \$(cutadapt --version) + END_VERSIONS """ } diff --git a/modules/local/filter_blastn.nf b/modules/local/filter_blastn.nf index 004314f0..6acf352f 100644 --- a/modules/local/filter_blastn.nf +++ b/modules/local/filter_blastn.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process FILTER_BLASTN { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'blastn', meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "conda-forge::sed=4.7" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img" - } else { - container "biocontainers/biocontainers:v1.2.0_cv1" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + 'biocontainers/biocontainers:v1.2.0_cv1' }" input: tuple val(meta), path(hits) @@ -24,11 +13,17 @@ process FILTER_BLASTN { output: tuple val(meta), path('*.txt'), emit: txt + path "versions.yml" , emit: versions script: - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def prefix = task.ext.prefix ?: "${meta.id}" """ awk 'BEGIN{OFS=\"\\t\";FS=\"\\t\"}{print \$0,\$5/\$15,\$5/\$14}' $hits | awk 'BEGIN{OFS=\"\\t\";FS=\"\\t\"} \$15 > 200 && \$17 > 0.7 && \$1 !~ /phage/ {print \$0}' > tmp.out cat $header tmp.out > ${prefix}.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sed: \$(echo \$(sed --version 2>&1) | sed 's/^.*GNU sed) //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/local/get_chrom_sizes.nf b/modules/local/get_chrom_sizes.nf deleted file mode 100644 index e20fb395..00000000 --- a/modules/local/get_chrom_sizes.nf +++ /dev/null @@ -1,34 +0,0 @@ -// Import generic module functions -include { saveFiles } from './functions' - -params.options = [:] - -process GET_CHROM_SIZES { - tag "$fasta" - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'genome', meta:[:], publish_by_meta:[]) } - - conda (params.enable_conda ? "bioconda::samtools=1.10" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.10--h9402c20_2" - } else { - container "quay.io/biocontainers/samtools:1.10--h9402c20_2" - } - - input: - path fasta - - output: - path '*.sizes' , emit: sizes - path '*.fai' , emit: fai - path "*.version.txt", emit: version - - script: - def software = 'samtools' - """ - samtools faidx $fasta - cut -f 1,2 ${fasta}.fai > ${fasta}.sizes - echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt - """ -} diff --git a/modules/local/ivar_variants_to_vcf.nf b/modules/local/ivar_variants_to_vcf.nf index 3ff46e0f..4b3f696a 100644 --- a/modules/local/ivar_variants_to_vcf.nf +++ b/modules/local/ivar_variants_to_vcf.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process IVAR_VARIANTS_TO_VCF { tag "$meta.id" - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } + conda (params.enable_conda ? "conda-forge::python=3.9.5" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/python:3.9--1' : + 'quay.io/biocontainers/python:3.9--1' }" input: tuple val(meta), path(tsv) @@ -25,16 +14,23 @@ process IVAR_VARIANTS_TO_VCF { tuple val(meta), path("*.vcf"), emit: vcf tuple val(meta), path("*.log"), emit: log tuple val(meta), path("*.tsv"), emit: tsv + path "versions.yml" , emit: versions script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ ivar_variants_to_vcf.py \\ $tsv \\ ${prefix}.vcf \\ - $options.args \\ + $args \\ > ${prefix}.variant_counts.log cat $header ${prefix}.variant_counts.log > ${prefix}.variant_counts_mqc.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS """ } diff --git a/modules/local/kraken2_build.nf b/modules/local/kraken2_build.nf index ae7bc2f6..48cd575e 100644 --- a/modules/local/kraken2_build.nf +++ b/modules/local/kraken2_build.nf @@ -1,36 +1,32 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process KRAKEN2_BUILD { + tag "$library" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } - conda (params.enable_conda ? 'bioconda::kraken2=2.1.1' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/kraken2:2.1.1--pl526hc9558a2_0' - } else { - container 'quay.io/biocontainers/kraken2:2.1.1--pl526hc9558a2_0' - } + conda (params.enable_conda ? 'bioconda::kraken2=2.1.1 conda-forge::pigz=2.6' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' : + 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' }" input: val library output: - path 'kraken2_db' , emit: db - path '*.version.txt', emit: version + path 'kraken2_db' , emit: db + path "versions.yml", emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + def args3 = task.ext.args3 ?: '' """ - kraken2-build --db kraken2_db --threads $task.cpus $options.args --download-taxonomy - kraken2-build --db kraken2_db --threads $task.cpus $options.args2 --download-library $library - kraken2-build --db kraken2_db --threads $task.cpus $options.args3 --build + kraken2-build --db kraken2_db --threads $task.cpus $args --download-taxonomy + kraken2-build --db kraken2_db --threads $task.cpus $args2 --download-library $library + kraken2-build --db kraken2_db --threads $task.cpus $args3 --build - echo \$(kraken2 --version 2>&1) | sed 's/^.*Kraken version //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + kraken2: \$(echo \$(kraken2 --version 2>&1) | sed 's/^.*Kraken version //; s/ .*\$//') + pigz: \$( pigz --version 2>&1 | sed 's/pigz //g' ) + END_VERSIONS """ } diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index 08c3a0a9..58829660 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -1,32 +1,22 @@ -// Import generic module functions -include { initOptions; saveFiles } from './functions' - -params.options = [:] -options = initOptions(params.options) - process MAKE_BED_MASK { tag "$meta.id" - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'bed', meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/python:3.8.3" - } else { - container "quay.io/biocontainers/python:3.8.3" - } + conda (params.enable_conda ? "conda-forge::python=3.9.5" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/python:3.9--1' : + 'quay.io/biocontainers/python:3.9--1' }" input: tuple val(meta), path(vcf), path(bed) - path fasta + path fasta output: tuple val(meta), path("*.bed") , emit: bed tuple val(meta), path("*.fasta"), emit: fasta + path "versions.yml" , emit: versions script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def prefix = task.ext.prefix ?: "${meta.id}" """ make_bed_mask.py \\ $vcf \\ @@ -36,5 +26,10 @@ process MAKE_BED_MASK { ## Rename fasta entry by sample name and not reference genome FASTA_NAME=\$(head -n1 $fasta | sed 's/>//g') sed "s/\${FASTA_NAME}/${meta.id}/g" $fasta > ${prefix}.fasta + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS """ } diff --git a/modules/local/multiqc_custom_csv_from_map.nf b/modules/local/multiqc_custom_csv_from_map.nf deleted file mode 100644 index 49968fd4..00000000 --- a/modules/local/multiqc_custom_csv_from_map.nf +++ /dev/null @@ -1,27 +0,0 @@ -// Import generic module functions -include { saveFiles; getSoftwareName } from './functions' - -params.options = [:] - -process MULTIQC_CUSTOM_CSV_FROM_MAP { - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } - - memory 100.MB - - input: - val csv_data - val out_prefix - - output: - path "*.csv" - - exec: - // Write to file - def file = task.workDir.resolve("${out_prefix}_mqc.csv") - file.write csv_data[0].keySet().join(",") + '\n' - csv_data.each { data -> - file.append(data.values().join(",") + '\n') - } -} diff --git a/modules/local/multiqc_custom_tsv_from_string.nf b/modules/local/multiqc_custom_tsv_from_string.nf deleted file mode 100644 index c2ece8bc..00000000 --- a/modules/local/multiqc_custom_tsv_from_string.nf +++ /dev/null @@ -1,37 +0,0 @@ -// Import generic module functions -include { saveFiles; getSoftwareName } from './functions' - -params.options = [:] - -process MULTIQC_CUSTOM_TSV_FROM_STRING { - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } - - conda (params.enable_conda ? "conda-forge::sed=4.7" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img" - } else { - container "biocontainers/biocontainers:v1.2.0_cv1" - } - - input: - val tsv_data - val col_names - val out_prefix - - output: - path "*.tsv" - - script: - if (tsv_data.size() > 0) { - """ - echo "${col_names}" > ${out_prefix}_mqc.tsv - echo "${tsv_data.join('\n')}" >> ${out_prefix}_mqc.tsv - """ - } else { - """ - touch ${out_prefix}_mqc.tsv - """ - } -} diff --git a/modules/local/multiqc_illumina.nf b/modules/local/multiqc_illumina.nf index f1f15a15..d0637f8c 100644 --- a/modules/local/multiqc_illumina.nf +++ b/modules/local/multiqc_illumina.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process MULTIQC { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "bioconda::multiqc=1.11" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0" - } else { - container "quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" input: path 'multiqc_config.yaml' @@ -55,13 +44,14 @@ process MULTIQC { path "*variants_metrics_mqc.csv", optional:true, emit: csv_variants path "*assembly_metrics_mqc.csv", optional:true, emit: csv_assembly path "*_plots" , optional:true, emit: plots + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' def custom_config = params.multiqc_config ? "--config $multiqc_custom_config" : '' """ ## Run MultiQC once to parse tool logs - multiqc -f $options.args $custom_config . + multiqc -f $args $custom_config . ## Parse YAML files dumped by MultiQC to obtain metrics multiqc_to_custom_csv.py --platform illumina @@ -79,6 +69,11 @@ process MULTIQC { rm -f variants_bcftools/report.tsv ## Run MultiQC a second time - multiqc -f $options.args -e general_stats --ignore *nextclade_clade_mqc.tsv $custom_config . + multiqc -f $args -e general_stats --ignore *nextclade_clade_mqc.tsv $custom_config . + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) + END_VERSIONS """ } diff --git a/modules/local/multiqc_nanopore.nf b/modules/local/multiqc_nanopore.nf index de171181..698bf0e6 100644 --- a/modules/local/multiqc_nanopore.nf +++ b/modules/local/multiqc_nanopore.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process MULTIQC { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "bioconda::multiqc=1.11" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0" - } else { - container "quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" input: path 'multiqc_config.yaml' @@ -42,13 +31,14 @@ process MULTIQC { path "*_data" , emit: data path "*.csv" , optional:true, emit: csv path "*_plots" , optional:true, emit: plots + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' def custom_config = params.multiqc_config ? "--config $multiqc_custom_config" : '' """ ## Run MultiQC once to parse tool logs - multiqc -f $options.args $custom_config . + multiqc -f $args $custom_config . ## Parse YAML files dumped by MultiQC to obtain metrics multiqc_to_custom_csv.py --platform nanopore @@ -57,6 +47,11 @@ process MULTIQC { rm -rf quast ## Run MultiQC a second time - multiqc -f $options.args -e general_stats --ignore *nextclade_clade_mqc.tsv $custom_config . + multiqc -f $args -e general_stats --ignore *nextclade_clade_mqc.tsv $custom_config . + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) + END_VERSIONS """ } diff --git a/modules/local/multiqc_tsv_from_list.nf b/modules/local/multiqc_tsv_from_list.nf new file mode 100644 index 00000000..7f7b7c0a --- /dev/null +++ b/modules/local/multiqc_tsv_from_list.nf @@ -0,0 +1,25 @@ +process MULTIQC_TSV_FROM_LIST { + + executor 'local' + memory 100.MB + + input: + val tsv_data // [ ['foo', 1], ['bar', 1] ] + val header // [ 'name', 'number' ] + val out_prefix + + output: + path "*.tsv" + + exec: + // Generate file contents + def contents = "" + if (tsv_data.size() > 0) { + contents += "${header.join('\t')}\n" + contents += tsv_data.join('\n') + } + + // Write to file + def mqc_file = task.workDir.resolve("${out_prefix}_mqc.tsv") + mqc_file.text = contents +} diff --git a/modules/local/plot_base_density.nf b/modules/local/plot_base_density.nf index d5ddfce1..ec499aba 100644 --- a/modules/local/plot_base_density.nf +++ b/modules/local/plot_base_density.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process PLOT_BASE_DENSITY { tag "$fasta" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'plots', meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "conda-forge::r-base=4.0.3 conda-forge::r-reshape2=1.4.4 conda-forge::r-optparse=1.6.6 conda-forge::r-ggplot2=3.3.3 conda-forge::r-scales=1.1.1 conda-forge::r-viridis=0.5.1 conda-forge::r-tidyverse=1.3.0 bioconda::bioconductor-biostrings=2.58.0 bioconda::bioconductor-complexheatmap=2.6.2" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/mulled-v2-ad9dd5f398966bf899ae05f8e7c54d0fb10cdfa7:05678da05b8e5a7a5130e90a9f9a6c585b965afa-0" - } else { - container "quay.io/biocontainers/mulled-v2-ad9dd5f398966bf899ae05f8e7c54d0fb10cdfa7:05678da05b8e5a7a5130e90a9f9a6c585b965afa-0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-ad9dd5f398966bf899ae05f8e7c54d0fb10cdfa7:05678da05b8e5a7a5130e90a9f9a6c585b965afa-0' : + 'quay.io/biocontainers/mulled-v2-ad9dd5f398966bf899ae05f8e7c54d0fb10cdfa7:05678da05b8e5a7a5130e90a9f9a6c585b965afa-0' }" input: tuple val(meta), path(fasta) @@ -24,13 +13,19 @@ process PLOT_BASE_DENSITY { output: tuple val(meta), path('*.pdf'), emit: pdf tuple val(meta), path('*.tsv'), emit: tsv + path "versions.yml" , emit: versions - script: - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ + def prefix = task.ext.prefix ?: "${meta.id}" """ plot_base_density.r \\ --fasta_files $fasta \\ --prefixes $prefix \\ --output_dir ./ + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: \$(echo \$(R --version 2>&1) | sed 's/^.*R version //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/local/plot_mosdepth_regions.nf b/modules/local/plot_mosdepth_regions.nf index 66dc082d..38f9f21c 100644 --- a/modules/local/plot_mosdepth_regions.nf +++ b/modules/local/plot_mosdepth_regions.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles } from './functions' - -params.options = [:] -options = initOptions(params.options) - process PLOT_MOSDEPTH_REGIONS { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'mosdepth', meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "conda-forge::r-base=4.0.3 conda-forge::r-reshape2=1.4.4 conda-forge::r-optparse=1.6.6 conda-forge::r-ggplot2=3.3.3 conda-forge::r-scales=1.1.1 conda-forge::r-viridis=0.5.1 conda-forge::r-tidyverse=1.3.0 bioconda::bioconductor-biostrings=2.58.0 bioconda::bioconductor-complexheatmap=2.6.2" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/mulled-v2-ad9dd5f398966bf899ae05f8e7c54d0fb10cdfa7:05678da05b8e5a7a5130e90a9f9a6c585b965afa-0" - } else { - container "quay.io/biocontainers/mulled-v2-ad9dd5f398966bf899ae05f8e7c54d0fb10cdfa7:05678da05b8e5a7a5130e90a9f9a6c585b965afa-0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-ad9dd5f398966bf899ae05f8e7c54d0fb10cdfa7:05678da05b8e5a7a5130e90a9f9a6c585b965afa-0' : + 'quay.io/biocontainers/mulled-v2-ad9dd5f398966bf899ae05f8e7c54d0fb10cdfa7:05678da05b8e5a7a5130e90a9f9a6c585b965afa-0' }" input: path beds @@ -25,14 +14,21 @@ process PLOT_MOSDEPTH_REGIONS { path '*coverage.tsv', emit: coverage_tsv path '*heatmap.pdf' , optional:true, emit: heatmap_pdf path '*heatmap.tsv' , optional:true, emit: heatmap_tsv + path "versions.yml" , emit: versions - script: - def prefix = options.suffix ?: "mosdepth" + script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "mosdepth" """ plot_mosdepth_regions.r \\ --input_files ${beds.join(',')} \\ --output_dir ./ \\ --output_suffix $prefix \\ - $options.args + $args + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: \$(echo \$(R --version 2>&1) | sed 's/^.*R version //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index 6c4303c4..60c9c512 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -1,10 +1,10 @@ process SAMPLESHEET_CHECK { tag "$samplesheet" - conda (params.enable_conda ? "conda-forge::python=3.8.3" : null) + conda (params.enable_conda ? "conda-forge::python=3.9.5" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/python:3.8.3' : - 'quay.io/biocontainers/python:3.8.3' }" + 'https://depot.galaxyproject.org/singularity/python:3.9--1' : + 'quay.io/biocontainers/python:3.9--1' }" input: path samplesheet diff --git a/modules/local/snpeff_ann.nf b/modules/local/snpeff_ann.nf index 9bd6c15b..04823334 100644 --- a/modules/local/snpeff_ann.nf +++ b/modules/local/snpeff_ann.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SNPEFF_ANN { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::snpeff=5.0' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/snpeff:5.0--0' - } else { - container 'quay.io/biocontainers/snpeff:5.0--0' - } + conda (params.enable_conda ? "bioconda::snpeff=5.0" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/snpeff:5.0--hdfd78af_1' : + 'quay.io/biocontainers/snpeff:5.0--hdfd78af_1' }" input: tuple val(meta), path(vcf) @@ -29,11 +18,12 @@ process SNPEFF_ANN { tuple val(meta), path("*.csv") , emit: csv tuple val(meta), path("*.genes.txt"), emit: txt tuple val(meta), path("*.html") , emit: html - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def avail_mem = 4 if (!task.memory) { log.info '[snpEff] Available memory not known - defaulting to 4GB. Specify process memory requirements to change this.' @@ -46,12 +36,15 @@ process SNPEFF_ANN { ${fasta.baseName} \\ -config $config \\ -dataDir $db \\ - $options.args \\ + $args \\ $vcf \\ -csvStats ${prefix}.snpeff.csv \\ > ${prefix}.snpeff.vcf mv snpEff_summary.html ${prefix}.snpeff.summary.html - echo \$(snpEff -version 2>&1) | sed 's/^.*SnpEff //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + snpeff: \$(echo \$(snpEff -version 2>&1) | cut -f 2 -d ' ') + END_VERSIONS """ } diff --git a/modules/local/snpeff_build.nf b/modules/local/snpeff_build.nf index 5e61d105..abdd9b3c 100644 --- a/modules/local/snpeff_build.nf +++ b/modules/local/snpeff_build.nf @@ -1,35 +1,24 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SNPEFF_BUILD { tag "$fasta" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } - conda (params.enable_conda ? 'bioconda::snpeff=5.0' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/snpeff:5.0--0' - } else { - container 'quay.io/biocontainers/snpeff:5.0--0' - } + conda (params.enable_conda ? "bioconda::snpeff=5.0" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/snpeff:5.0--hdfd78af_1' : + 'quay.io/biocontainers/snpeff:5.0--hdfd78af_1' }" input: path fasta path gff output: - path 'snpeff_db' , emit: db - path '*.config' , emit: config - path '*.version.txt', emit: version + path 'snpeff_db' , emit: db + path '*.config' , emit: config + path "versions.yml", emit: versions script: - def software = getSoftwareName(task.process) - def basename = fasta.baseName + def basename = fasta.baseName + def avail_mem = 4 if (!task.memory) { log.info '[snpEff] Available memory not known - defaulting to 4GB. Specify process memory requirements to change this.' @@ -58,6 +47,9 @@ process SNPEFF_BUILD { -v \\ ${basename} - echo \$(snpEff -version 2>&1) | sed 's/^.*SnpEff //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + snpeff: \$(echo \$(snpEff -version 2>&1) | cut -f 2 -d ' ') + END_VERSIONS """ } diff --git a/modules/local/snpsift_extractfields.nf b/modules/local/snpsift_extractfields.nf index a02c42a5..88dfb9f7 100644 --- a/modules/local/snpsift_extractfields.nf +++ b/modules/local/snpsift_extractfields.nf @@ -1,33 +1,23 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SNPSIFT_EXTRACTFIELDS { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::snpsift=4.3.1t' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/snpsift:4.3.1t--2' - } else { - container 'quay.io/biocontainers/snpsift:4.3.1t--2' - } + conda (params.enable_conda ? "bioconda::snpsift=4.3.1t" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/snpsift:4.3.1t--hdfd78af_3' : + 'quay.io/biocontainers/snpsift:4.3.1t--hdfd78af_3' }" input: tuple val(meta), path(vcf) output: tuple val(meta), path("*.snpsift.txt"), emit: txt - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def avail_mem = 4 if (!task.memory) { log.info '[SnpSift] Available memory not known - defaulting to 4GB. Specify process memory requirements to change this.' @@ -40,7 +30,7 @@ process SNPSIFT_EXTRACTFIELDS { extractFields \\ -s "," \\ -e "." \\ - $options.args \\ + $args \\ $vcf \\ CHROM POS REF ALT \\ "ANN[*].GENE" "ANN[*].GENEID" \\ @@ -53,6 +43,9 @@ process SNPSIFT_EXTRACTFIELDS { "EFF[*].FUNCLASS" "EFF[*].CODON" "EFF[*].AA" "EFF[*].AA_LEN" \\ > ${prefix}.snpsift.txt - echo \$(SnpSift -h 2>&1) | sed 's/^.*SnpSift version //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + snpsift: \$( echo \$(SnpSift split -h 2>&1) | sed 's/^.*version //' | sed 's/(.*//' | sed 's/t//g' ) + END_VERSIONS """ } diff --git a/modules/nf-core/modules/abacas/functions.nf b/modules/nf-core/modules/abacas/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/abacas/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/abacas/main.nf b/modules/nf-core/modules/abacas/main.nf index 6ec65ea2..49040214 100644 --- a/modules/nf-core/modules/abacas/main.nf +++ b/modules/nf-core/modules/abacas/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process ABACAS { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::abacas=1.3.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/abacas:1.3.1--pl526_0" - } else { - container "quay.io/biocontainers/abacas:1.3.1--pl526_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/abacas:1.3.1--pl526_0' : + 'quay.io/biocontainers/abacas:1.3.1--pl526_0' }" input: tuple val(meta), path(scaffold) @@ -24,22 +13,25 @@ process ABACAS { output: tuple val(meta), path('*.abacas*'), emit: results - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ abacas.pl \\ -r $fasta \\ -q $scaffold \\ - $options.args \\ + $args \\ -o ${prefix}.abacas mv nucmer.delta ${prefix}.abacas.nucmer.delta mv nucmer.filtered.delta ${prefix}.abacas.nucmer.filtered.delta mv nucmer.tiling ${prefix}.abacas.nucmer.tiling mv unused_contigs.out ${prefix}.abacas.unused.contigs.out - echo \$(abacas.pl -v 2>&1) | sed 's/^.*ABACAS.//; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + abacas: \$(echo \$(abacas.pl -v 2>&1) | sed 's/^.*ABACAS.//; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/abacas/meta.yml b/modules/nf-core/modules/abacas/meta.yml index d60afee0..039fb0be 100644 --- a/modules/nf-core/modules/abacas/meta.yml +++ b/modules/nf-core/modules/abacas/meta.yml @@ -48,10 +48,10 @@ output: 'test.abacas.MULTIFASTA.fa' ] pattern: "*.{abacas}*" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" diff --git a/modules/nf-core/modules/artic/guppyplex/functions.nf b/modules/nf-core/modules/artic/guppyplex/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/artic/guppyplex/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/artic/guppyplex/main.nf b/modules/nf-core/modules/artic/guppyplex/main.nf index 41178298..780f5111 100644 --- a/modules/nf-core/modules/artic/guppyplex/main.nf +++ b/modules/nf-core/modules/artic/guppyplex/main.nf @@ -1,41 +1,33 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process ARTIC_GUPPYPLEX { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::artic=1.2.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/artic:1.2.1--py_0" - } else { - container "quay.io/biocontainers/artic:1.2.1--py_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/artic:1.2.1--py_0' : + 'quay.io/biocontainers/artic:1.2.1--py_0' }" input: tuple val(meta), path(fastq_dir) output: tuple val(meta), path("*.fastq.gz"), emit: fastq - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ artic \\ guppyplex \\ - $options.args \\ + $args \\ --directory $fastq_dir \\ --output ${prefix}.fastq pigz -p $task.cpus *.fastq - echo \$(artic --version 2>&1) | sed 's/^.*artic //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + artic: \$(artic --version 2>&1 | sed 's/^.*artic //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/artic/guppyplex/meta.yml b/modules/nf-core/modules/artic/guppyplex/meta.yml index 0caaf5d2..5056f908 100644 --- a/modules/nf-core/modules/artic/guppyplex/meta.yml +++ b/modules/nf-core/modules/artic/guppyplex/meta.yml @@ -34,10 +34,10 @@ output: type: file description: Aggregated FastQ files pattern: "*.{fastq.gz}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" diff --git a/modules/nf-core/modules/artic/minion/functions.nf b/modules/nf-core/modules/artic/minion/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/artic/minion/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/artic/minion/main.nf b/modules/nf-core/modules/artic/minion/main.nf index e408551b..ce04fcc8 100644 --- a/modules/nf-core/modules/artic/minion/main.nf +++ b/modules/nf-core/modules/artic/minion/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process ARTIC_MINION { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::artic=1.2.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/artic:1.2.1--py_0" - } else { - container "quay.io/biocontainers/artic:1.2.1--py_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/artic:1.2.1--py_0' : + 'quay.io/biocontainers/artic:1.2.1--py_0' }" input: tuple val(meta), path(fastq) @@ -40,24 +29,24 @@ process ARTIC_MINION { tuple val(meta), path("${prefix}.pass.vcf.gz") , emit: vcf tuple val(meta), path("${prefix}.pass.vcf.gz.tbi") , emit: tbi tuple val(meta), path("*.json"), optional:true , emit: json - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" def version = scheme_version.toString().toLowerCase().replaceAll('v','') - def fast5 = params.fast5_dir ? "--fast5-directory $fast5_dir" : "" - def summary = params.sequencing_summary ? "--sequencing-summary $sequencing_summary" : "" + def fast5 = fast5_dir ? "--fast5-directory $fast5_dir" : "" + def summary = sequencing_summary ? "--sequencing-summary $sequencing_summary" : "" def model = "" - if (options.args.tokenize().contains('--medaka')) { + if (args.tokenize().contains('--medaka')) { fast5 = "" summary = "" - model = file(params.artic_minion_medaka_model).exists() ? "--medaka-model ./$medaka_model" : "--medaka-model $params.artic_minion_medaka_model" + model = file(medaka_model).exists() ? "--medaka-model ./$medaka_model" : "--medaka-model $medaka_model" } """ artic \\ minion \\ - $options.args \\ + $args \\ --threads $task.cpus \\ --read-file $fastq \\ --scheme-directory ./primer-schemes \\ @@ -68,6 +57,9 @@ process ARTIC_MINION { $scheme \\ $prefix - echo \$(artic --version 2>&1) | sed 's/^.*artic //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + artic: \$(artic --version 2>&1 | sed 's/^.*artic //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/artic/minion/meta.yml b/modules/nf-core/modules/artic/minion/meta.yml index 1b6a73cf..464e1dc7 100644 --- a/modules/nf-core/modules/artic/minion/meta.yml +++ b/modules/nf-core/modules/artic/minion/meta.yml @@ -103,10 +103,10 @@ output: type: file description: JSON file for MultiQC pattern: "*.json" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" diff --git a/modules/nf-core/modules/bandage/image/functions.nf b/modules/nf-core/modules/bandage/image/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bandage/image/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bandage/image/main.nf b/modules/nf-core/modules/bandage/image/main.nf index 6afdb60d..bc2a9495 100644 --- a/modules/nf-core/modules/bandage/image/main.nf +++ b/modules/nf-core/modules/bandage/image/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BANDAGE_IMAGE { tag "${meta.id}" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? 'bioconda::bandage=0.8.1' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bandage:0.8.1--hc9558a2_2" - } else { - container "quay.io/biocontainers/bandage:0.8.1--hc9558a2_2" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bandage:0.8.1--hc9558a2_2' : + 'quay.io/biocontainers/bandage:0.8.1--hc9558a2_2' }" input: tuple val(meta), path(gfa) @@ -24,15 +13,18 @@ process BANDAGE_IMAGE { output: tuple val(meta), path('*.png'), emit: png tuple val(meta), path('*.svg'), emit: svg - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ - Bandage image $gfa ${prefix}.png $options.args - Bandage image $gfa ${prefix}.svg $options.args + Bandage image $gfa ${prefix}.png $args + Bandage image $gfa ${prefix}.svg $args - echo \$(Bandage --version 2>&1) | sed 's/^.*Version: //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bandage: \$(echo \$(Bandage --version 2>&1) | sed 's/^.*Version: //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/bandage/image/meta.yml b/modules/nf-core/modules/bandage/image/meta.yml index 26c23a07..1c2b9840 100644 --- a/modules/nf-core/modules/bandage/image/meta.yml +++ b/modules/nf-core/modules/bandage/image/meta.yml @@ -11,6 +11,7 @@ tools: Bandage - a Bioinformatics Application for Navigating De novo Assembly Graphs Easily homepage: https://github.com/rrwick/Bandage documentation: https://github.com/rrwick/Bandage + licence: ['GPL-3.0-or-later'] input: - meta: type: map @@ -35,9 +36,9 @@ output: type: file description: Bandage image in SVG format pattern: "*.svg" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@heuermh" diff --git a/modules/nf-core/modules/bcftools/consensus/functions.nf b/modules/nf-core/modules/bcftools/consensus/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bcftools/consensus/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bcftools/consensus/main.nf b/modules/nf-core/modules/bcftools/consensus/main.nf index 67321fc2..4633790e 100644 --- a/modules/nf-core/modules/bcftools/consensus/main.nf +++ b/modules/nf-core/modules/bcftools/consensus/main.nf @@ -1,38 +1,30 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BCFTOOLS_CONSENSUS { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::bcftools=1.11' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/bcftools:1.11--h7c999a4_0' - } else { - container 'quay.io/biocontainers/bcftools:1.11--h7c999a4_0' - } + conda (params.enable_conda ? 'bioconda::bcftools=1.13' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bcftools:1.13--h3a49de5_0' : + 'quay.io/biocontainers/bcftools:1.13--h3a49de5_0' }" input: tuple val(meta), path(vcf), path(tbi), path(fasta) output: tuple val(meta), path('*.fa'), emit: fasta - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ - cat $fasta | bcftools consensus $vcf $options.args > ${prefix}.fa + cat $fasta | bcftools consensus $vcf $args > ${prefix}.fa header=\$(head -n 1 ${prefix}.fa | sed 's/>//g') sed -i 's/\${header}/${meta.id}/g' ${prefix}.fa - echo \$(bcftools --version 2>&1) | sed 's/^.*bcftools //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/bcftools/consensus/meta.yml b/modules/nf-core/modules/bcftools/consensus/meta.yml index ef14479d..761115a6 100644 --- a/modules/nf-core/modules/bcftools/consensus/meta.yml +++ b/modules/nf-core/modules/bcftools/consensus/meta.yml @@ -11,6 +11,7 @@ tools: homepage: http://samtools.github.io/bcftools/bcftools.html documentation: http://www.htslib.org/doc/bcftools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -39,10 +40,10 @@ output: type: file description: FASTA reference consensus file pattern: "*.{fasta,fa}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/bcftools/mpileup/functions.nf b/modules/nf-core/modules/bcftools/mpileup/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bcftools/mpileup/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bcftools/mpileup/main.nf b/modules/nf-core/modules/bcftools/mpileup/main.nf index 287a0c9d..8a209a66 100644 --- a/modules/nf-core/modules/bcftools/mpileup/main.nf +++ b/modules/nf-core/modules/bcftools/mpileup/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BCFTOOLS_MPILEUP { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::bcftools=1.11" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bcftools:1.11--h7c999a4_0" - } else { - container "quay.io/biocontainers/bcftools:1.11--h7c999a4_0" - } + conda (params.enable_conda ? 'bioconda::bcftools=1.13' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bcftools:1.13--h3a49de5_0' : + 'quay.io/biocontainers/bcftools:1.13--h3a49de5_0' }" input: tuple val(meta), path(bam) @@ -26,22 +15,31 @@ process BCFTOOLS_MPILEUP { tuple val(meta), path("*.gz") , emit: vcf tuple val(meta), path("*.tbi") , emit: tbi tuple val(meta), path("*stats.txt"), emit: stats - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + def args3 = task.ext.args3 ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ echo "${meta.id}" > sample_name.list + bcftools mpileup \\ --fasta-ref $fasta \\ - $options.args \\ + $args \\ $bam \\ - | bcftools call --output-type v $options.args2 \\ + | bcftools call --output-type v $args2 \\ | bcftools reheader --samples sample_name.list \\ - | bcftools view --output-file ${prefix}.vcf.gz --output-type z $options.args3 + | bcftools view --output-file ${prefix}.vcf.gz --output-type z $args3 + tabix -p vcf -f ${prefix}.vcf.gz + bcftools stats ${prefix}.vcf.gz > ${prefix}.bcftools_stats.txt - echo \$(bcftools --version 2>&1) | sed 's/^.*bcftools //; s/ .*\$//' > ${software}.version.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/bcftools/mpileup/meta.yml b/modules/nf-core/modules/bcftools/mpileup/meta.yml index a15aea14..c31180ee 100644 --- a/modules/nf-core/modules/bcftools/mpileup/meta.yml +++ b/modules/nf-core/modules/bcftools/mpileup/meta.yml @@ -11,6 +11,7 @@ tools: homepage: http://samtools.github.io/bcftools/bcftools.html documentation: http://www.htslib.org/doc/bcftools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -43,10 +44,10 @@ output: type: file description: Text output file containing stats pattern: "*{stats.txt}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/bcftools/stats/functions.nf b/modules/nf-core/modules/bcftools/stats/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bcftools/stats/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bcftools/stats/main.nf b/modules/nf-core/modules/bcftools/stats/main.nf index 84e48c05..67e8dca7 100644 --- a/modules/nf-core/modules/bcftools/stats/main.nf +++ b/modules/nf-core/modules/bcftools/stats/main.nf @@ -1,35 +1,27 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BCFTOOLS_STATS { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::bcftools=1.11" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bcftools:1.11--h7c999a4_0" - } else { - container "quay.io/biocontainers/bcftools:1.11--h7c999a4_0" - } + conda (params.enable_conda ? 'bioconda::bcftools=1.13' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bcftools:1.13--h3a49de5_0' : + 'quay.io/biocontainers/bcftools:1.13--h3a49de5_0' }" input: tuple val(meta), path(vcf) output: tuple val(meta), path("*stats.txt"), emit: stats - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ - bcftools stats $options.args $vcf > ${prefix}.bcftools_stats.txt - echo \$(bcftools --version 2>&1) | sed 's/^.*bcftools //; s/ .*\$//' > ${software}.version.txt + bcftools stats $args $vcf > ${prefix}.bcftools_stats.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/bcftools/stats/meta.yml b/modules/nf-core/modules/bcftools/stats/meta.yml index 6b70f83a..505bf729 100644 --- a/modules/nf-core/modules/bcftools/stats/meta.yml +++ b/modules/nf-core/modules/bcftools/stats/meta.yml @@ -12,6 +12,7 @@ tools: homepage: http://samtools.github.io/bcftools/bcftools.html documentation: http://www.htslib.org/doc/bcftools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -32,10 +33,10 @@ output: type: file description: Text output file containing stats pattern: "*_{stats.txt}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/bedtools/genomecov/functions.nf b/modules/nf-core/modules/bedtools/genomecov/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bedtools/genomecov/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bedtools/genomecov/main.nf b/modules/nf-core/modules/bedtools/genomecov/main.nf index f9b87464..ca491e75 100644 --- a/modules/nf-core/modules/bedtools/genomecov/main.nf +++ b/modules/nf-core/modules/bedtools/genomecov/main.nf @@ -1,44 +1,42 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BEDTOOLS_GENOMECOV { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::bedtools=2.30.0" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0" - } else { - container "quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0' : + 'quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0' }" input: - tuple val(meta), path(intervals) + tuple val(meta), path(intervals), val(scale) path sizes val extension output: tuple val(meta), path("*.${extension}"), emit: genomecov - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def args_list = args.tokenize() + args += (scale > 0 && scale != 1) ? " -scale $scale" : "" + if (!args_list.contains('-bg') && (scale > 0 && scale != 1)) { + args += " -bg" + } + + def prefix = task.ext.prefix ?: "${meta.id}" if (intervals.name =~ /\.bam/) { """ bedtools \\ genomecov \\ -ibam $intervals \\ - $options.args \\ + $args \\ > ${prefix}.${extension} - bedtools --version | sed -e "s/bedtools v//g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") + END_VERSIONS """ } else { """ @@ -46,10 +44,13 @@ process BEDTOOLS_GENOMECOV { genomecov \\ -i $intervals \\ -g $sizes \\ - $options.args \\ + $args \\ > ${prefix}.${extension} - bedtools --version | sed -e "s/bedtools v//g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") + END_VERSIONS """ } } diff --git a/modules/nf-core/modules/bedtools/genomecov/meta.yml b/modules/nf-core/modules/bedtools/genomecov/meta.yml index f629665c..0713e95b 100644 --- a/modules/nf-core/modules/bedtools/genomecov/meta.yml +++ b/modules/nf-core/modules/bedtools/genomecov/meta.yml @@ -9,6 +9,7 @@ tools: description: | A set of tools for genomic analysis tasks, specifically enabling genome arithmetic (merge, count, complement) on various file types. documentation: https://bedtools.readthedocs.io/en/latest/content/tools/genomecov.html + licence: ['MIT'] input: - meta: type: map @@ -19,6 +20,9 @@ input: type: file description: BAM/BED/GFF/VCF pattern: "*.{bam|bed|gff|vcf}" + - scale: + type: value + description: Number containing the scale factor for the output. Set to 1 to disable. Setting to a value other than 1 will also get the -bg bedgraph output format as this is required for this command switch - sizes: type: file description: Tab-delimited table of chromosome names in the first column and chromosome sizes in the second column @@ -35,12 +39,13 @@ output: type: file description: Computed genome coverage file pattern: "*.${extension}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@Emiller88" - "@sruthipsuresh" - "@drpatelh" - "@sidorov-si" + - "@chris-cheshire" diff --git a/modules/nf-core/modules/bedtools/getfasta/functions.nf b/modules/nf-core/modules/bedtools/getfasta/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bedtools/getfasta/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bedtools/getfasta/main.nf b/modules/nf-core/modules/bedtools/getfasta/main.nf index 374a310b..5a283e94 100644 --- a/modules/nf-core/modules/bedtools/getfasta/main.nf +++ b/modules/nf-core/modules/bedtools/getfasta/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BEDTOOLS_GETFASTA { tag "$bed" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "bioconda::bedtools=2.30.0" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0" - } else { - container "quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0' : + 'quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0' }" input: path bed @@ -24,19 +13,22 @@ process BEDTOOLS_GETFASTA { output: path "*.fa" , emit: fasta - path "*.version.txt", emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${bed.baseName}${options.suffix}" : "${bed.baseName}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${bed.baseName}" """ bedtools \\ getfasta \\ - $options.args \\ + $args \\ -fi $fasta \\ -bed $bed \\ -fo ${prefix}.fa - bedtools --version | sed -e "s/bedtools v//g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") + END_VERSIONS """ } diff --git a/modules/nf-core/modules/bedtools/getfasta/meta.yml b/modules/nf-core/modules/bedtools/getfasta/meta.yml index 1ca63bdc..38715c3d 100644 --- a/modules/nf-core/modules/bedtools/getfasta/meta.yml +++ b/modules/nf-core/modules/bedtools/getfasta/meta.yml @@ -9,6 +9,7 @@ tools: description: | A set of tools for genomic analysis tasks, specifically enabling genome arithmetic (merge, count, complement) on various file types. documentation: https://bedtools.readthedocs.io/en/latest/content/tools/intersect.html + licence: ['MIT'] input: - bed: type: file @@ -24,10 +25,10 @@ output: type: file description: Output fasta file with extracted sequences pattern: "*.{fa}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/bedtools/maskfasta/functions.nf b/modules/nf-core/modules/bedtools/maskfasta/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bedtools/maskfasta/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bedtools/maskfasta/main.nf b/modules/nf-core/modules/bedtools/maskfasta/main.nf index 02110149..7eeb4c7d 100644 --- a/modules/nf-core/modules/bedtools/maskfasta/main.nf +++ b/modules/nf-core/modules/bedtools/maskfasta/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BEDTOOLS_MASKFASTA { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::bedtools=2.30.0" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0" - } else { - container "quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0' : + 'quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0' }" input: tuple val(meta), path(bed) @@ -24,18 +13,21 @@ process BEDTOOLS_MASKFASTA { output: tuple val(meta), path("*.fa"), emit: fasta - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ bedtools \\ maskfasta \\ - $options.args \\ + $args \\ -fi $fasta \\ -bed $bed \\ -fo ${prefix}.fa - bedtools --version | sed -e "s/bedtools v//g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") + END_VERSIONS """ } diff --git a/modules/nf-core/modules/bedtools/maskfasta/meta.yml b/modules/nf-core/modules/bedtools/maskfasta/meta.yml index b6e494e6..0b7aa3ed 100644 --- a/modules/nf-core/modules/bedtools/maskfasta/meta.yml +++ b/modules/nf-core/modules/bedtools/maskfasta/meta.yml @@ -9,6 +9,7 @@ tools: description: | A set of tools for genomic analysis tasks, specifically enabling genome arithmetic (merge, count, complement) on various file types. documentation: https://bedtools.readthedocs.io/en/latest/content/tools/intersect.html + licence: ['MIT'] input: - meta: type: map @@ -34,10 +35,10 @@ output: type: file description: Output masked fasta file pattern: "*.{fa}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/bedtools/merge/functions.nf b/modules/nf-core/modules/bedtools/merge/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bedtools/merge/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bedtools/merge/main.nf b/modules/nf-core/modules/bedtools/merge/main.nf index 4ac7d1a5..5f1da95b 100644 --- a/modules/nf-core/modules/bedtools/merge/main.nf +++ b/modules/nf-core/modules/bedtools/merge/main.nf @@ -1,40 +1,32 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BEDTOOLS_MERGE { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::bedtools=2.30.0" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0" - } else { - container "quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0' : + 'quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0' }" input: tuple val(meta), path(bed) output: tuple val(meta), path('*.bed'), emit: bed - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ bedtools \\ merge \\ -i $bed \\ - $options.args \\ + $args \\ > ${prefix}.bed - bedtools --version | sed -e "s/bedtools v//g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") + END_VERSIONS """ } diff --git a/modules/nf-core/modules/bedtools/merge/meta.yml b/modules/nf-core/modules/bedtools/merge/meta.yml index f75bea67..40a42b7b 100644 --- a/modules/nf-core/modules/bedtools/merge/meta.yml +++ b/modules/nf-core/modules/bedtools/merge/meta.yml @@ -8,6 +8,7 @@ tools: description: | A set of tools for genomic analysis tasks, specifically enabling genome arithmetic (merge, count, complement) on various file types. documentation: https://bedtools.readthedocs.io/en/latest/content/tools/merge.html + licence: ['MIT'] input: - meta: type: map @@ -28,10 +29,10 @@ output: type: file description: Overlapped bed file with combined features pattern: "*.{bed}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@Emiller88" - "@sruthipsuresh" diff --git a/modules/nf-core/modules/blast/blastn/functions.nf b/modules/nf-core/modules/blast/blastn/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/blast/blastn/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/blast/blastn/main.nf b/modules/nf-core/modules/blast/blastn/main.nf index 8d519613..3a0bafe0 100644 --- a/modules/nf-core/modules/blast/blastn/main.nf +++ b/modules/nf-core/modules/blast/blastn/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BLAST_BLASTN { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::blast=2.10.1' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/blast:2.10.1--pl526he19e7b1_3' - } else { - container 'quay.io/biocontainers/blast:2.10.1--pl526he19e7b1_3' - } + conda (params.enable_conda ? 'bioconda::blast=2.12.0' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/blast:2.12.0--pl5262h3289130_0' : + 'quay.io/biocontainers/blast:2.12.0--pl5262h3289130_0' }" input: tuple val(meta), path(fasta) @@ -24,19 +13,22 @@ process BLAST_BLASTN { output: tuple val(meta), path('*.blastn.txt'), emit: txt - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ DB=`find -L ./ -name "*.ndb" | sed 's/.ndb//'` blastn \\ -num_threads $task.cpus \\ -db \$DB \\ -query $fasta \\ - $options.args \\ + $args \\ -out ${prefix}.blastn.txt - echo \$(blastn -version 2>&1) | sed 's/^.*blastn: //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + blast: \$(blastn -version 2>&1 | sed 's/^.*blastn: //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/blast/blastn/meta.yml b/modules/nf-core/modules/blast/blastn/meta.yml index d04889a8..39acb663 100644 --- a/modules/nf-core/modules/blast/blastn/meta.yml +++ b/modules/nf-core/modules/blast/blastn/meta.yml @@ -12,6 +12,7 @@ tools: homepage: https://blast.ncbi.nlm.nih.gov/Blast.cgi documentation: https://blast.ncbi.nlm.nih.gov/Blast.cgi?CMD=Web&PAGE_TYPE=Blastdocs doi: 10.1016/S0022-2836(05)80360-2 + licence: ['US-Government-Work'] input: - meta: type: map @@ -31,10 +32,10 @@ output: type: file description: File containing blastn hits pattern: "*.{blastn.txt}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/blast/makeblastdb/functions.nf b/modules/nf-core/modules/blast/makeblastdb/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/blast/makeblastdb/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/blast/makeblastdb/main.nf b/modules/nf-core/modules/blast/makeblastdb/main.nf index bc736f8b..b4c426a4 100644 --- a/modules/nf-core/modules/blast/makeblastdb/main.nf +++ b/modules/nf-core/modules/blast/makeblastdb/main.nf @@ -1,35 +1,30 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BLAST_MAKEBLASTDB { tag "$fasta" label 'process_medium' - conda (params.enable_conda ? 'bioconda::blast=2.10.1' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/blast:2.10.1--pl526he19e7b1_3' - } else { - container 'quay.io/biocontainers/blast:2.10.1--pl526he19e7b1_3' - } + conda (params.enable_conda ? 'bioconda::blast=2.12.0' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/blast:2.12.0--pl5262h3289130_0' : + 'quay.io/biocontainers/blast:2.12.0--pl5262h3289130_0' }" input: path fasta output: path 'blast_db' , emit: db - path '*.version.txt', emit: version + path "versions.yml" , emit: versions script: def args = task.ext.args ?: '' """ makeblastdb \\ -in $fasta \\ - $options.args + $args mkdir blast_db mv ${fasta}* blast_db - echo \$(blastn -version 2>&1) | sed 's/^.*blastn: //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + blast: \$(blastn -version 2>&1 | sed 's/^.*blastn: //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/blast/makeblastdb/meta.yml b/modules/nf-core/modules/blast/makeblastdb/meta.yml index 0ea4903f..c9d18cba 100644 --- a/modules/nf-core/modules/blast/makeblastdb/meta.yml +++ b/modules/nf-core/modules/blast/makeblastdb/meta.yml @@ -11,6 +11,7 @@ tools: homepage: https://blast.ncbi.nlm.nih.gov/Blast.cgi documentation: https://blast.ncbi.nlm.nih.gov/Blast.cgi?CMD=Web&PAGE_TYPE=Blastdocs doi: 10.1016/S0022-2836(05)80360-2 + licence: ['US-Government-Work'] input: - fasta: type: file @@ -21,10 +22,10 @@ output: type: directory description: Output directory containing blast database files pattern: "*" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/bowtie2/align/functions.nf b/modules/nf-core/modules/bowtie2/align/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bowtie2/align/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bowtie2/align/main.nf b/modules/nf-core/modules/bowtie2/align/main.nf index d43d479d..04f817ec 100644 --- a/modules/nf-core/modules/bowtie2/align/main.nf +++ b/modules/nf-core/modules/bowtie2/align/main.nf @@ -1,65 +1,60 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BOWTIE2_ALIGN { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? 'bioconda::bowtie2=2.4.2 bioconda::samtools=1.11 conda-forge::pigz=2.3.4' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0" - } else { - container "quay.io/biocontainers/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0' : + 'quay.io/biocontainers/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0' }" input: tuple val(meta), path(reads) path index + val save_unaligned output: tuple val(meta), path('*.bam'), emit: bam tuple val(meta), path('*.log'), emit: log - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions tuple val(meta), path('*fastq.gz'), optional:true, emit: fastq script: - def split_cpus = Math.floor(task.cpus/2) - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" if (meta.single_end) { - def unaligned = params.save_unaligned ? "--un-gz ${prefix}.unmapped.fastq.gz" : '' + def unaligned = save_unaligned ? "--un-gz ${prefix}.unmapped.fastq.gz" : '' """ INDEX=`find -L ./ -name "*.rev.1.bt2" | sed 's/.rev.1.bt2//'` bowtie2 \\ -x \$INDEX \\ -U $reads \\ - --threads ${split_cpus} \\ + --threads $task.cpus \\ $unaligned \\ - $options.args \\ + $args \\ 2> ${prefix}.bowtie2.log \\ - | samtools view -@ ${split_cpus} $options.args2 -bhS -o ${prefix}.bam - + | samtools view -@ $task.cpus $args2 -bhS -o ${prefix}.bam - - echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bowtie2: \$(echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//') + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + pigz: \$( pigz --version 2>&1 | sed 's/pigz //g' ) + END_VERSIONS """ } else { - def unaligned = params.save_unaligned ? "--un-conc-gz ${prefix}.unmapped.fastq.gz" : '' + def unaligned = save_unaligned ? "--un-conc-gz ${prefix}.unmapped.fastq.gz" : '' """ INDEX=`find -L ./ -name "*.rev.1.bt2" | sed 's/.rev.1.bt2//'` bowtie2 \\ -x \$INDEX \\ -1 ${reads[0]} \\ -2 ${reads[1]} \\ - --threads ${split_cpus} \\ + --threads $task.cpus \\ $unaligned \\ - $options.args \\ + $args \\ 2> ${prefix}.bowtie2.log \\ - | samtools view -@ ${split_cpus} $options.args2 -bhS -o ${prefix}.bam - + | samtools view -@ $task.cpus $args2 -bhS -o ${prefix}.bam - if [ -f ${prefix}.unmapped.fastq.1.gz ]; then mv ${prefix}.unmapped.fastq.1.gz ${prefix}.unmapped_1.fastq.gz @@ -67,7 +62,13 @@ process BOWTIE2_ALIGN { if [ -f ${prefix}.unmapped.fastq.2.gz ]; then mv ${prefix}.unmapped.fastq.2.gz ${prefix}.unmapped_2.fastq.gz fi - echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//' > ${software}.version.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bowtie2: \$(echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//') + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + pigz: \$( pigz --version 2>&1 | sed 's/pigz //g' ) + END_VERSIONS """ } } diff --git a/modules/nf-core/modules/bowtie2/align/meta.yml b/modules/nf-core/modules/bowtie2/align/meta.yml index 9d9cd004..2607dea5 100644 --- a/modules/nf-core/modules/bowtie2/align/meta.yml +++ b/modules/nf-core/modules/bowtie2/align/meta.yml @@ -13,6 +13,7 @@ tools: homepage: http://bowtie-bio.sourceforge.net/bowtie2/index.shtml documentation: http://bowtie-bio.sourceforge.net/bowtie2/manual.shtml doi: 10.1038/nmeth.1923 + licence: ['GPL-3.0-or-later'] input: - meta: type: map @@ -28,15 +29,18 @@ input: type: file description: Bowtie2 genome index files pattern: "*.ebwt" + - save_unaligned: + type: boolean + description: Save unaligned reads to FastQ file(s) output: - bam: type: file description: Output BAM file containing read alignments pattern: "*.{bam}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" - fastq: type: file description: Unaligned FastQ files diff --git a/modules/nf-core/modules/bowtie2/build/functions.nf b/modules/nf-core/modules/bowtie2/build/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/bowtie2/build/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/bowtie2/build/main.nf b/modules/nf-core/modules/bowtie2/build/main.nf index 42ff1d20..da2e9ed5 100644 --- a/modules/nf-core/modules/bowtie2/build/main.nf +++ b/modules/nf-core/modules/bowtie2/build/main.nf @@ -1,35 +1,27 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process BOWTIE2_BUILD { tag "$fasta" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'index', meta:[:], publish_by_meta:[]) } - conda (params.enable_conda ? 'bioconda::bowtie2=2.4.2' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/bowtie2:2.4.2--py38h1c8e9b9_1' - } else { - container 'quay.io/biocontainers/bowtie2:2.4.2--py38h1c8e9b9_1' - } + conda (params.enable_conda ? 'bioconda::bowtie2=2.4.4' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bowtie2:2.4.4--py39hbb4e92a_0' : + 'quay.io/biocontainers/bowtie2:2.4.4--py39hbb4e92a_0' }" input: path fasta output: path 'bowtie2' , emit: index - path '*.version.txt', emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' """ mkdir bowtie2 - bowtie2-build $options.args --threads $task.cpus $fasta bowtie2/${fasta.baseName} - echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//' > ${software}.version.txt + bowtie2-build $args --threads $task.cpus $fasta bowtie2/${fasta.baseName} + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bowtie2: \$(echo \$(bowtie2 --version 2>&1) | sed 's/^.*bowtie2-align-s version //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/bowtie2/build/meta.yml b/modules/nf-core/modules/bowtie2/build/meta.yml index 0a4cd3de..ecc54e9b 100644 --- a/modules/nf-core/modules/bowtie2/build/meta.yml +++ b/modules/nf-core/modules/bowtie2/build/meta.yml @@ -14,6 +14,7 @@ tools: homepage: http://bowtie-bio.sourceforge.net/bowtie2/index.shtml documentation: http://bowtie-bio.sourceforge.net/bowtie2/manual.shtml doi: 10.1038/nmeth.1923 + licence: ['GPL-3.0-or-later'] input: - fasta: type: file @@ -23,10 +24,10 @@ output: type: file description: Bowtie2 genome index files pattern: "*.bt2" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/cat/fastq/functions.nf b/modules/nf-core/modules/cat/fastq/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/cat/fastq/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/cat/fastq/main.nf b/modules/nf-core/modules/cat/fastq/main.nf index 55ccca90..d02598e1 100644 --- a/modules/nf-core/modules/cat/fastq/main.nf +++ b/modules/nf-core/modules/cat/fastq/main.nf @@ -1,36 +1,32 @@ -// Import generic module functions -include { initOptions; saveFiles } from './functions' - -params.options = [:] -options = initOptions(params.options) - process CAT_FASTQ { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:'merged_fastq', meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "conda-forge::sed=4.7" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img" - } else { - container "biocontainers/biocontainers:v1.2.0_cv1" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + 'biocontainers/biocontainers:v1.2.0_cv1' }" input: - tuple val(meta), path(reads) + tuple val(meta), path(reads, stageAs: "input*/*") output: tuple val(meta), path("*.merged.fastq.gz"), emit: reads + path "versions.yml" , emit: versions script: - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" def readList = reads.collect{ it.toString() } if (meta.single_end) { if (readList.size > 1) { """ - cat ${readList.sort().join(' ')} > ${prefix}.merged.fastq.gz + cat ${readList.join(' ')} > ${prefix}.merged.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$(echo \$(cat --version 2>&1) | sed 's/^.*coreutils) //; s/ .*\$//') + END_VERSIONS """ } } else { @@ -39,8 +35,13 @@ process CAT_FASTQ { def read2 = [] readList.eachWithIndex{ v, ix -> ( ix & 1 ? read2 : read1 ) << v } """ - cat ${read1.sort().join(' ')} > ${prefix}_1.merged.fastq.gz - cat ${read2.sort().join(' ')} > ${prefix}_2.merged.fastq.gz + cat ${read1.join(' ')} > ${prefix}_1.merged.fastq.gz + cat ${read2.join(' ')} > ${prefix}_2.merged.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$(echo \$(cat --version 2>&1) | sed 's/^.*coreutils) //; s/ .*\$//') + END_VERSIONS """ } } diff --git a/modules/nf-core/modules/cat/fastq/meta.yml b/modules/nf-core/modules/cat/fastq/meta.yml index e7b8eebe..1992fa34 100644 --- a/modules/nf-core/modules/cat/fastq/meta.yml +++ b/modules/nf-core/modules/cat/fastq/meta.yml @@ -8,6 +8,7 @@ tools: description: | The cat utility reads files sequentially, writing them to the standard output. documentation: https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html + licence: ['GPL-3.0-or-later'] input: - meta: type: map @@ -28,6 +29,11 @@ output: type: file description: Merged fastq file pattern: "*.{merged.fastq.gz}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf b/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf new file mode 100644 index 00000000..934bb467 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/main.nf @@ -0,0 +1,21 @@ +process CUSTOM_DUMPSOFTWAREVERSIONS { + label 'process_low' + + // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container + conda (params.enable_conda ? "bioconda::multiqc=1.11" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.11--pyhdfd78af_0' : + 'quay.io/biocontainers/multiqc:1.11--pyhdfd78af_0' }" + + input: + path versions + + output: + path "software_versions.yml" , emit: yml + path "software_versions_mqc.yml", emit: mqc_yml + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + template 'dumpsoftwareversions.py' +} diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml new file mode 100644 index 00000000..5b5b8a60 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/meta.yml @@ -0,0 +1,34 @@ +name: custom_dumpsoftwareversions +description: Custom module used to dump software versions within the nf-core pipeline template +keywords: + - custom + - version +tools: + - custom: + description: Custom module used to dump software versions within the nf-core pipeline template + homepage: https://github.com/nf-core/tools + documentation: https://github.com/nf-core/tools + licence: ['MIT'] +input: + - versions: + type: file + description: YML file containing software versions + pattern: "*.yml" + +output: + - yml: + type: file + description: Standard YML file containing software versions + pattern: "software_versions.yml" + - mqc_yml: + type: file + description: MultiQC custom content YML file containing software versions + pattern: "software_versions_mqc.yml" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@drpatelh" + - "@grst" diff --git a/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py new file mode 100644 index 00000000..d1390392 --- /dev/null +++ b/modules/nf-core/modules/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +import yaml +import platform +from textwrap import dedent + + +def _make_versions_html(versions): + html = [ + dedent( + """\\ + + + + + + + + + + """ + ) + ] + for process, tmp_versions in sorted(versions.items()): + html.append("") + for i, (tool, version) in enumerate(sorted(tmp_versions.items())): + html.append( + dedent( + f"""\\ + + + + + + """ + ) + ) + html.append("") + html.append("
Process Name Software Version
{process if (i == 0) else ''}{tool}{version}
") + return "\\n".join(html) + + +versions_this_module = {} +versions_this_module["${task.process}"] = { + "python": platform.python_version(), + "yaml": yaml.__version__, +} + +with open("$versions") as f: + versions_by_process = yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module + +# aggregate versions by the module name (derived from fully-qualified process name) +versions_by_module = {} +for process, process_versions in versions_by_process.items(): + module = process.split(":")[-1] + try: + assert versions_by_module[module] == process_versions, ( + "We assume that software versions are the same between all modules. " + "If you see this error-message it means you discovered an edge-case " + "and should open an issue in nf-core/tools. " + ) + except KeyError: + versions_by_module[module] = process_versions + +versions_by_module["Workflow"] = { + "Nextflow": "$workflow.nextflow.version", + "$workflow.manifest.name": "$workflow.manifest.version", +} + +versions_mqc = { + "id": "software_versions", + "section_name": "${workflow.manifest.name} Software Versions", + "section_href": "https://github.com/${workflow.manifest.name}", + "plot_type": "html", + "description": "are collected at run time from the software output.", + "data": _make_versions_html(versions_by_module), +} + +with open("software_versions.yml", "w") as f: + yaml.dump(versions_by_module, f, default_flow_style=False) +with open("software_versions_mqc.yml", "w") as f: + yaml.dump(versions_mqc, f, default_flow_style=False) + +with open("versions.yml", "w") as f: + yaml.dump(versions_this_module, f, default_flow_style=False) diff --git a/modules/nf-core/modules/custom/getchromsizes/main.nf b/modules/nf-core/modules/custom/getchromsizes/main.nf new file mode 100644 index 00000000..270b3f48 --- /dev/null +++ b/modules/nf-core/modules/custom/getchromsizes/main.nf @@ -0,0 +1,29 @@ +process CUSTOM_GETCHROMSIZES { + tag "$fasta" + label 'process_low' + + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" + + input: + path fasta + + output: + path '*.sizes' , emit: sizes + path '*.fai' , emit: fai + path "versions.yml", emit: versions + + script: + def args = task.ext.args ?: '' + """ + samtools faidx $fasta + cut -f 1,2 ${fasta}.fai > ${fasta}.sizes + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + custom: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/custom/getchromsizes/meta.yml b/modules/nf-core/modules/custom/getchromsizes/meta.yml new file mode 100644 index 00000000..eb1db4bb --- /dev/null +++ b/modules/nf-core/modules/custom/getchromsizes/meta.yml @@ -0,0 +1,39 @@ +name: custom_getchromsizes +description: Generates a FASTA file of chromosome sizes and a fasta index file +keywords: + - fasta + - chromosome + - indexing +tools: + - samtools: + description: Tools for dealing with SAM, BAM and CRAM files + homepage: http://www.htslib.org/ + documentation: http://www.htslib.org/doc/samtools.html + tool_dev_url: https://github.com/samtools/samtools + doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] + +input: + - fasta: + type: file + description: FASTA file + pattern: "*.{fasta}" + +output: + - sizes: + type: file + description: File containing chromosome lengths + pattern: "*.{sizes}" + - fai: + type: file + description: FASTA index file + pattern: "*.{fai}" + - versions: + type: file + description: File containing software version + pattern: "versions.yml" + + +authors: + - "@tamara-hodgetts" + - "@chris-cheshire" diff --git a/modules/nf-core/modules/fastp/functions.nf b/modules/nf-core/modules/fastp/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/fastp/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/fastp/main.nf b/modules/nf-core/modules/fastp/main.nf index 6d703615..33603842 100644 --- a/modules/nf-core/modules/fastp/main.nf +++ b/modules/nf-core/modules/fastp/main.nf @@ -1,40 +1,32 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process FASTP { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? 'bioconda::fastp=0.20.1' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/fastp:0.20.1--h8b12597_0' - } else { - container 'quay.io/biocontainers/fastp:0.20.1--h8b12597_0' - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/fastp:0.20.1--h8b12597_0' : + 'quay.io/biocontainers/fastp:0.20.1--h8b12597_0' }" input: tuple val(meta), path(reads) + val save_trimmed_fail + val save_merged output: - tuple val(meta), path('*.trim.fastq.gz'), emit: reads - tuple val(meta), path('*.json') , emit: json - tuple val(meta), path('*.html') , emit: html - tuple val(meta), path('*.log') , emit: log - path '*.version.txt' , emit: version - tuple val(meta), path('*.fail.fastq.gz'), optional:true, emit: reads_fail + tuple val(meta), path('*.trim.fastq.gz') , emit: reads + tuple val(meta), path('*.json') , emit: json + tuple val(meta), path('*.html') , emit: html + tuple val(meta), path('*.log') , emit: log + path "versions.yml" , emit: versions + tuple val(meta), path('*.fail.fastq.gz') , optional:true, emit: reads_fail + tuple val(meta), path('*.merged.fastq.gz'), optional:true, emit: reads_merged script: + def args = task.ext.args ?: '' // Added soft-links to original fastqs for consistent naming in MultiQC - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def prefix = task.ext.prefix ?: "${meta.id}" if (meta.single_end) { - def fail_fastq = params.save_trimmed_fail ? "--failed_out ${prefix}.fail.fastq.gz" : '' + def fail_fastq = save_trimmed_fail ? "--failed_out ${prefix}.fail.fastq.gz" : '' """ [ ! -f ${prefix}.fastq.gz ] && ln -s $reads ${prefix}.fastq.gz fastp \\ @@ -44,12 +36,16 @@ process FASTP { --json ${prefix}.fastp.json \\ --html ${prefix}.fastp.html \\ $fail_fastq \\ - $options.args \\ + $args \\ 2> ${prefix}.fastp.log - echo \$(fastp --version 2>&1) | sed -e "s/fastp //g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastp: \$(fastp --version 2>&1 | sed -e "s/fastp //g") + END_VERSIONS """ } else { - def fail_fastq = params.save_trimmed_fail ? "--unpaired1 ${prefix}_1.fail.fastq.gz --unpaired2 ${prefix}_2.fail.fastq.gz" : '' + def fail_fastq = save_trimmed_fail ? "--unpaired1 ${prefix}_1.fail.fastq.gz --unpaired2 ${prefix}_2.fail.fastq.gz" : '' + def merge_fastq = save_merged ? "-m --merged_out ${prefix}.merged.fastq.gz" : '' """ [ ! -f ${prefix}_1.fastq.gz ] && ln -s ${reads[0]} ${prefix}_1.fastq.gz [ ! -f ${prefix}_2.fastq.gz ] && ln -s ${reads[1]} ${prefix}_2.fastq.gz @@ -61,12 +57,16 @@ process FASTP { --json ${prefix}.fastp.json \\ --html ${prefix}.fastp.html \\ $fail_fastq \\ + $merge_fastq \\ --thread $task.cpus \\ --detect_adapter_for_pe \\ - $options.args \\ + $args \\ 2> ${prefix}.fastp.log - echo \$(fastp --version 2>&1) | sed -e "s/fastp //g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + fastp: \$(fastp --version 2>&1 | sed -e "s/fastp //g") + END_VERSIONS """ } } diff --git a/modules/nf-core/modules/fastp/meta.yml b/modules/nf-core/modules/fastp/meta.yml index 1fc3dfb6..a1875faf 100644 --- a/modules/nf-core/modules/fastp/meta.yml +++ b/modules/nf-core/modules/fastp/meta.yml @@ -10,6 +10,7 @@ tools: A tool designed to provide fast all-in-one preprocessing for FastQ files. This tool is developed in C++ with multithreading supported to afford high performance. documentation: https://github.com/OpenGene/fastp doi: https://doi.org/10.1093/bioinformatics/bty560 + licence: ['MIT'] input: - meta: type: map @@ -30,7 +31,7 @@ output: e.g. [ id:'test', single_end:false ] - reads: type: file - description: The trimmed/modified fastq reads + description: The trimmed/modified/unmerged fastq reads pattern: "*trim.fastq.gz" - json: type: file @@ -39,19 +40,23 @@ output: - html: type: file description: Results in HTML format - pattern: "*.thml" + pattern: "*.html" - log: type: file description: fastq log file pattern: "*.log" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" - reads_fail: type: file description: Reads the failed the preprocessing pattern: "*fail.fastq.gz" + - reads_merged: + type: file + description: Reads that were successfully merged + pattern: "*.{merged.fastq.gz}" authors: - "@drpatelh" - "@kevinmenden" diff --git a/modules/nf-core/modules/gunzip/functions.nf b/modules/nf-core/modules/gunzip/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/gunzip/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/gunzip/main.nf b/modules/nf-core/modules/gunzip/main.nf index 29248796..77a4e546 100644 --- a/modules/nf-core/modules/gunzip/main.nf +++ b/modules/nf-core/modules/gunzip/main.nf @@ -1,35 +1,31 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process GUNZIP { tag "$archive" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "conda-forge::sed=4.7" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img" - } else { - container "biocontainers/biocontainers:v1.2.0_cv1" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + 'biocontainers/biocontainers:v1.2.0_cv1' }" input: - path archive + tuple val(meta), path(archive) output: - path "$gunzip", emit: gunzip - path "*.version.txt", emit: version + tuple val(meta), path("$gunzip"), emit: gunzip + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - gunzip = archive.toString() - '.gz' + def args = task.ext.args ?: '' + gunzip = archive.toString() - '.gz' """ - gunzip -f $options.args $archive - echo \$(gunzip --version 2>&1) | sed 's/^.*(gzip) //; s/ Copyright.*\$//' > ${software}.version.txt + gunzip \\ + -f \\ + $args \\ + $archive + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + gunzip: \$(echo \$(gunzip --version 2>&1) | sed 's/^.*(gzip) //; s/ Copyright.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/gunzip/meta.yml b/modules/nf-core/modules/gunzip/meta.yml index 922e74e6..ea1f1546 100644 --- a/modules/nf-core/modules/gunzip/meta.yml +++ b/modules/nf-core/modules/gunzip/meta.yml @@ -8,7 +8,13 @@ tools: description: | gzip is a file format and a software application used for file compression and decompression. documentation: https://www.gnu.org/software/gzip/manual/gzip.html + licence: ['GPL-3.0-or-later'] input: + - meta: + type: map + description: | + Optional groovy Map containing meta information + e.g. [ id:'test', single_end:false ] - archive: type: file description: File to be compressed/uncompressed @@ -18,10 +24,11 @@ output: type: file description: Compressed/uncompressed file pattern: "*.*" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" + - "@jfy133" diff --git a/modules/nf-core/modules/ivar/consensus/functions.nf b/modules/nf-core/modules/ivar/consensus/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/ivar/consensus/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/ivar/consensus/main.nf b/modules/nf-core/modules/ivar/consensus/main.nf index 1b1019cf..58d97c8c 100644 --- a/modules/nf-core/modules/ivar/consensus/main.nf +++ b/modules/nf-core/modules/ivar/consensus/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process IVAR_CONSENSUS { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::ivar=1.3.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/ivar:1.3.1--h089eab3_0" - } else { - container "quay.io/biocontainers/ivar:1.3.1--h089eab3_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ivar:1.3.1--h089eab3_0' : + 'quay.io/biocontainers/ivar:1.3.1--h089eab3_0' }" input: tuple val(meta), path(bam) @@ -26,22 +15,26 @@ process IVAR_CONSENSUS { tuple val(meta), path("*.fa") , emit: fasta tuple val(meta), path("*.qual.txt"), emit: qual tuple val(meta), path("*.mpileup") , optional:true, emit: mpileup - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" def save_mpileup = params.save_mpileup ? "tee ${prefix}.mpileup |" : "" """ samtools mpileup \\ --reference $fasta \\ - $options.args2 \\ + $args2 \\ $bam | \\ $save_mpileup \\ ivar consensus \\ - $options.args \\ + $args \\ -p $prefix - echo \$(ivar version 2>&1) | sed 's/^.*iVar version //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + ivar: \$(echo \$(ivar version 2>&1) | sed 's/^.*iVar version //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/ivar/consensus/meta.yml b/modules/nf-core/modules/ivar/consensus/meta.yml index 913a7660..2ee5f2c6 100644 --- a/modules/nf-core/modules/ivar/consensus/meta.yml +++ b/modules/nf-core/modules/ivar/consensus/meta.yml @@ -10,6 +10,7 @@ tools: iVar - a computational package that contains functions broadly useful for viral amplicon-based sequencing. homepage: https://github.com/andersen-lab/ivar documentation: https://andersen-lab.github.io/ivar/html/manualpage.html + licence: ['GPL-3.0-or-later'] input: - meta: type: map @@ -42,10 +43,10 @@ output: type: file description: mpileup output from samtools mpileup [OPTIONAL] pattern: "*.mpileup" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@andersgs" - "@drpatelh" diff --git a/modules/nf-core/modules/ivar/trim/functions.nf b/modules/nf-core/modules/ivar/trim/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/ivar/trim/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/ivar/trim/main.nf b/modules/nf-core/modules/ivar/trim/main.nf index afdc99e4..4d0c70a2 100644 --- a/modules/nf-core/modules/ivar/trim/main.nf +++ b/modules/nf-core/modules/ivar/trim/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process IVAR_TRIM { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::ivar=1.3.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/ivar:1.3.1--h089eab3_0" - } else { - container "quay.io/biocontainers/ivar:1.3.1--h089eab3_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ivar:1.3.1--h089eab3_0' : + 'quay.io/biocontainers/ivar:1.3.1--h089eab3_0' }" input: tuple val(meta), path(bam), path(bai) @@ -25,19 +14,22 @@ process IVAR_TRIM { output: tuple val(meta), path("*.bam"), emit: bam tuple val(meta), path('*.log'), emit: log - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ ivar trim \\ - $options.args \\ + $args \\ -i $bam \\ -b $bed \\ -p $prefix \\ > ${prefix}.ivar.log - echo \$(ivar version 2>&1) | sed 's/^.*iVar version //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + ivar: \$(echo \$(ivar version 2>&1) | sed 's/^.*iVar version //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/ivar/trim/meta.yml b/modules/nf-core/modules/ivar/trim/meta.yml index 5791db66..44bc742e 100644 --- a/modules/nf-core/modules/ivar/trim/meta.yml +++ b/modules/nf-core/modules/ivar/trim/meta.yml @@ -10,6 +10,7 @@ tools: iVar - a computational package that contains functions broadly useful for viral amplicon-based sequencing. homepage: https://github.com/andersen-lab/ivar documentation: https://andersen-lab.github.io/ivar/html/manualpage.html + licence: ['GPL-3.0-or-later'] input: - meta: type: map @@ -42,10 +43,10 @@ output: type: file description: Log file generated by iVar for use with MultiQC pattern: "*.log" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@andersgs" - "@drpatelh" diff --git a/modules/nf-core/modules/ivar/variants/functions.nf b/modules/nf-core/modules/ivar/variants/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/ivar/variants/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/ivar/variants/main.nf b/modules/nf-core/modules/ivar/variants/main.nf index 154f309c..ce4abd4d 100644 --- a/modules/nf-core/modules/ivar/variants/main.nf +++ b/modules/nf-core/modules/ivar/variants/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process IVAR_VARIANTS { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::ivar=1.3.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/ivar:1.3.1--h089eab3_0" - } else { - container "quay.io/biocontainers/ivar:1.3.1--h089eab3_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ivar:1.3.1--h089eab3_0' : + 'quay.io/biocontainers/ivar:1.3.1--h089eab3_0' }" input: tuple val(meta), path(bam) @@ -26,25 +15,29 @@ process IVAR_VARIANTS { output: tuple val(meta), path("*.tsv") , emit: tsv tuple val(meta), path("*.mpileup"), optional:true, emit: mpileup - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" def save_mpileup = params.save_mpileup ? "tee ${prefix}.mpileup |" : "" def features = params.gff ? "-g $gff" : "" """ samtools mpileup \\ - $options.args2 \\ + $args2 \\ --reference $fasta \\ $bam | \\ $save_mpileup \\ ivar variants \\ - $options.args \\ + $args \\ $features \\ -r $fasta \\ -p $prefix - echo \$(ivar version 2>&1) | sed 's/^.*iVar version //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + ivar: \$(echo \$(ivar version 2>&1) | sed 's/^.*iVar version //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/ivar/variants/meta.yml b/modules/nf-core/modules/ivar/variants/meta.yml index 7a5fbbc0..fd3fce9e 100644 --- a/modules/nf-core/modules/ivar/variants/meta.yml +++ b/modules/nf-core/modules/ivar/variants/meta.yml @@ -10,6 +10,7 @@ tools: iVar - a computational package that contains functions broadly useful for viral amplicon-based sequencing. homepage: https://github.com/andersen-lab/ivar documentation: https://andersen-lab.github.io/ivar/html/manualpage.html + licence: ['GPL-3.0-or-later'] input: - meta: type: map @@ -42,10 +43,10 @@ output: type: file description: mpileup output from samtools mpileup [OPTIONAL] pattern: "*.mpileup" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@andersgs" - "@drpatelh" diff --git a/modules/nf-core/modules/kraken2/kraken2/functions.nf b/modules/nf-core/modules/kraken2/kraken2/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/kraken2/kraken2/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/kraken2/kraken2/main.nf b/modules/nf-core/modules/kraken2/kraken2/main.nf index ea0b72fd..3c4d1caf 100644 --- a/modules/nf-core/modules/kraken2/kraken2/main.nf +++ b/modules/nf-core/modules/kraken2/kraken2/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process KRAKEN2_KRAKEN2 { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? 'bioconda::kraken2=2.1.1 conda-forge::pigz=2.6' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' - } else { - container 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' : + 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' }" input: tuple val(meta), path(reads) @@ -26,11 +15,11 @@ process KRAKEN2_KRAKEN2 { tuple val(meta), path('*classified*') , emit: classified tuple val(meta), path('*unclassified*'), emit: unclassified tuple val(meta), path('*report.txt') , emit: txt - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" def paired = meta.single_end ? "" : "--paired" def classified = meta.single_end ? "${prefix}.classified.fastq" : "${prefix}.classified#.fastq" def unclassified = meta.single_end ? "${prefix}.unclassified.fastq" : "${prefix}.unclassified#.fastq" @@ -43,11 +32,15 @@ process KRAKEN2_KRAKEN2 { --report ${prefix}.kraken2.report.txt \\ --gzip-compressed \\ $paired \\ - $options.args \\ + $args \\ $reads pigz -p $task.cpus *.fastq - echo \$(kraken2 --version 2>&1) | sed 's/^.*Kraken version //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + kraken2: \$(echo \$(kraken2 --version 2>&1) | sed 's/^.*Kraken version //; s/ .*\$//') + pigz: \$( pigz --version 2>&1 | sed 's/pigz //g' ) + END_VERSIONS """ } diff --git a/modules/nf-core/modules/kraken2/kraken2/meta.yml b/modules/nf-core/modules/kraken2/kraken2/meta.yml index cb1ec0de..4b894705 100644 --- a/modules/nf-core/modules/kraken2/kraken2/meta.yml +++ b/modules/nf-core/modules/kraken2/kraken2/meta.yml @@ -12,6 +12,7 @@ tools: homepage: https://ccb.jhu.edu/software/kraken2/ documentation: https://github.com/DerrickWood/kraken2/wiki/Manual doi: 10.1186/s13059-019-1891-0 + licence: ['MIT'] input: - meta: type: map @@ -50,10 +51,10 @@ output: Kraken2 report containing stats about classified and not classifed reads. pattern: "*.{report.txt}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/minia/functions.nf b/modules/nf-core/modules/minia/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/minia/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/minia/main.nf b/modules/nf-core/modules/minia/main.nf index 9ae79ede..ceff67c5 100644 --- a/modules/nf-core/modules/minia/main.nf +++ b/modules/nf-core/modules/minia/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process MINIA { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::minia=3.2.4" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/minia:3.2.4--he513fc3_0" - } else { - container "quay.io/biocontainers/minia:3.2.4--he513fc3_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/minia:3.2.4--he513fc3_0' : + 'quay.io/biocontainers/minia:3.2.4--he513fc3_0' }" input: tuple val(meta), path(reads) @@ -25,19 +14,23 @@ process MINIA { tuple val(meta), path('*.contigs.fa'), emit: contigs tuple val(meta), path('*.unitigs.fa'), emit: unitigs tuple val(meta), path('*.h5') , emit: h5 - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def read_list = reads.join(",") """ - echo "${reads.join("\n")}" > input_files.txt + echo "${read_list}" | sed 's/,/\\n/g' > input_files.txt minia \\ - $options.args \\ + $args \\ -nb-cores $task.cpus \\ -in input_files.txt \\ -out $prefix - echo \$(minia --version 2>&1) | sed 's/^.*Minia version //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + minia: \$(echo \$(minia --version 2>&1 | grep Minia) | sed 's/^.*Minia version //;') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/minia/meta.yml b/modules/nf-core/modules/minia/meta.yml index d3a76be8..397a1d49 100644 --- a/modules/nf-core/modules/minia/meta.yml +++ b/modules/nf-core/modules/minia/meta.yml @@ -9,6 +9,7 @@ tools: a human genome on a desktop computer in a day. The output of Minia is a set of contigs. homepage: https://github.com/GATB/minia documentation: https://github.com/GATB/minia + licence: ['AGPL-3.0-or-later'] input: - meta: type: map @@ -37,10 +38,10 @@ output: type: file description: Minia output h5 file pattern: "*{.h5}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@kevinmenden" diff --git a/modules/nf-core/modules/mosdepth/functions.nf b/modules/nf-core/modules/mosdepth/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/mosdepth/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/mosdepth/main.nf b/modules/nf-core/modules/mosdepth/main.nf index 618efd79..d2669b7e 100644 --- a/modules/nf-core/modules/mosdepth/main.nf +++ b/modules/nf-core/modules/mosdepth/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process MOSDEPTH { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::mosdepth=0.3.1' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/mosdepth:0.3.1--ha7ba039_0" - } else { - container "quay.io/biocontainers/mosdepth:0.3.1--ha7ba039_0" - } + conda (params.enable_conda ? 'bioconda::mosdepth=0.3.2' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mosdepth:0.3.2--h01d7912_0' : + 'quay.io/biocontainers/mosdepth:0.3.2--h01d7912_0' }" input: tuple val(meta), path(bam), path(bai) @@ -31,18 +20,21 @@ process MOSDEPTH { tuple val(meta), path('*.per-base.bed.gz.csi'), emit: per_base_csi tuple val(meta), path('*.regions.bed.gz') , emit: regions_bed tuple val(meta), path('*.regions.bed.gz.csi') , emit: regions_csi - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" def interval = window_size ? "--by ${window_size}" : "--by ${bed}" """ mosdepth \\ $interval \\ - $options.args \\ + $args \\ $prefix \\ $bam - echo \$(mosdepth --version 2>&1) | sed 's/^.*mosdepth //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + mosdepth: \$(mosdepth --version 2>&1 | sed 's/^.*mosdepth //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/mosdepth/meta.yml b/modules/nf-core/modules/mosdepth/meta.yml index d96e474f..be568aa6 100644 --- a/modules/nf-core/modules/mosdepth/meta.yml +++ b/modules/nf-core/modules/mosdepth/meta.yml @@ -11,6 +11,7 @@ tools: Fast BAM/CRAM depth calculation for WGS, exome, or targeted sequencing. documentation: https://github.com/brentp/mosdepth doi: 10.1093/bioinformatics/btx699 + licence: ['MIT'] input: - meta: type: map @@ -67,10 +68,10 @@ output: type: file description: Index file for BED file with per-region coverage pattern: "*.{regions.bed.gz.csi}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/nanoplot/functions.nf b/modules/nf-core/modules/nanoplot/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/nanoplot/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/nanoplot/main.nf b/modules/nf-core/modules/nanoplot/main.nf index af080dc8..36577d8a 100644 --- a/modules/nf-core/modules/nanoplot/main.nf +++ b/modules/nf-core/modules/nanoplot/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process NANOPLOT { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::nanoplot=1.36.1" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/nanoplot:1.36.1--pyhdfd78af_0" - } else { - container "quay.io/biocontainers/nanoplot:1.36.1--pyhdfd78af_0" - } + conda (params.enable_conda ? 'bioconda::nanoplot=1.38.0' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/nanoplot:1.38.0--pyhdfd78af_0' : + 'quay.io/biocontainers/nanoplot:1.38.0--pyhdfd78af_0' }" input: tuple val(meta), path(ontfile) @@ -26,17 +15,20 @@ process NANOPLOT { tuple val(meta), path("*.png") , emit: png tuple val(meta), path("*.txt") , emit: txt tuple val(meta), path("*.log") , emit: log - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' def input_file = ("$ontfile".endsWith(".fastq.gz")) ? "--fastq ${ontfile}" : ("$ontfile".endsWith(".txt")) ? "--summary ${ontfile}" : '' """ NanoPlot \\ - $options.args \\ + $args \\ -t $task.cpus \\ $input_file - echo \$(NanoPlot --version 2>&1) | sed 's/^.*NanoPlot //; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + nanoplot: \$(echo \$(NanoPlot --version 2>&1) | sed 's/^.*NanoPlot //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/nanoplot/meta.yml b/modules/nf-core/modules/nanoplot/meta.yml index f1d94312..52ebb622 100644 --- a/modules/nf-core/modules/nanoplot/meta.yml +++ b/modules/nf-core/modules/nanoplot/meta.yml @@ -13,6 +13,7 @@ tools: alignment. homepage: http://nanoplot.bioinf.be documentation: https://github.com/wdecoster/NanoPlot + licence: ['GPL-3.0-or-later'] input: - meta: type: map @@ -49,10 +50,10 @@ output: type: file description: log file of NanoPlot run pattern: "*{.log}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@yuukiiwa" diff --git a/modules/nf-core/modules/nextclade/functions.nf b/modules/nf-core/modules/nextclade/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/nextclade/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/nextclade/main.nf b/modules/nf-core/modules/nextclade/main.nf index 8319f6b1..f60af57b 100644 --- a/modules/nf-core/modules/nextclade/main.nf +++ b/modules/nf-core/modules/nextclade/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process NEXTCLADE { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? "bioconda::nextclade_js=0.14.4" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/nextclade_js:0.14.4--h9ee0642_0" - } else { - container "quay.io/biocontainers/nextclade_js:0.14.4--h9ee0642_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/nextclade_js:0.14.4--h9ee0642_0' : + 'quay.io/biocontainers/nextclade_js:0.14.4--h9ee0642_0' }" input: tuple val(meta), path(fasta) @@ -27,14 +16,14 @@ process NEXTCLADE { tuple val(meta), path("${prefix}.tree.json") , emit: json_tree tuple val(meta), path("${prefix}.tsv") , emit: tsv tuple val(meta), path("${prefix}.clades.tsv"), optional:true, emit: tsv_clades - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" """ nextclade \\ - $options.args \\ + $args \\ --jobs $task.cpus \\ --input-fasta $fasta \\ --output-json ${prefix}.json \\ @@ -43,6 +32,9 @@ process NEXTCLADE { --output-tsv-clades-only ${prefix}.clades.tsv \\ --output-tree ${prefix}.tree.json - echo \$(nextclade --version 2>&1) > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + nextclade: \$(nextclade --version 2>&1) + END_VERSIONS """ } diff --git a/modules/nf-core/modules/nextclade/meta.yml b/modules/nf-core/modules/nextclade/meta.yml index d321e08f..1b4a435a 100644 --- a/modules/nf-core/modules/nextclade/meta.yml +++ b/modules/nf-core/modules/nextclade/meta.yml @@ -30,10 +30,10 @@ output: description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" - csv: type: file description: CSV file containing nextclade results diff --git a/modules/nf-core/modules/pangolin/functions.nf b/modules/nf-core/modules/pangolin/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/pangolin/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/pangolin/main.nf b/modules/nf-core/modules/pangolin/main.nf index d1417990..5ee2b2e0 100644 --- a/modules/nf-core/modules/pangolin/main.nf +++ b/modules/nf-core/modules/pangolin/main.nf @@ -1,40 +1,32 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process PANGOLIN { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? 'bioconda::pangolin=3.1.7' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/pangolin:3.1.7--pyhdfd78af_0' - } else { - container 'quay.io/biocontainers/pangolin:3.1.7--pyhdfd78af_0' - } + conda (params.enable_conda ? 'bioconda::pangolin=3.1.11' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pangolin:3.1.11--pyhdfd78af_1' : + 'quay.io/biocontainers/pangolin:3.1.11--pyhdfd78af_1' }" input: tuple val(meta), path(fasta) output: tuple val(meta), path('*.csv'), emit: report - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ pangolin \\ $fasta\\ --outfile ${prefix}.pangolin.csv \\ --threads $task.cpus \\ - $options.args + $args - echo \$(pangolin --version) | sed "s/pangolin //g" > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pangolin: \$(pangolin --version | sed "s/pangolin //g") + END_VERSIONS """ } diff --git a/modules/nf-core/modules/pangolin/meta.yml b/modules/nf-core/modules/pangolin/meta.yml index 2b2eb952..a2c0979a 100644 --- a/modules/nf-core/modules/pangolin/meta.yml +++ b/modules/nf-core/modules/pangolin/meta.yml @@ -10,6 +10,7 @@ tools: Phylogenetic Assignment of Named Global Outbreak LINeages homepage: https://github.com/cov-lineages/pangolin#pangolearn-description manual: https://github.com/cov-lineages/pangolin#pangolearn-description + licence: ['GPL-3.0-or-later'] input: - meta: type: map @@ -24,10 +25,10 @@ output: type: file description: Pangolin lineage report pattern: "*.{csv}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@kevinmenden" - "@drpatelh" diff --git a/modules/nf-core/modules/picard/collectmultiplemetrics/functions.nf b/modules/nf-core/modules/picard/collectmultiplemetrics/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/picard/collectmultiplemetrics/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/picard/collectmultiplemetrics/main.nf b/modules/nf-core/modules/picard/collectmultiplemetrics/main.nf index c0059a40..481761e3 100644 --- a/modules/nf-core/modules/picard/collectmultiplemetrics/main.nf +++ b/modules/nf-core/modules/picard/collectmultiplemetrics/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process PICARD_COLLECTMULTIPLEMETRICS { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::picard=2.23.9" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/picard:2.23.9--0" - } else { - container "quay.io/biocontainers/picard:2.23.9--0" - } + conda (params.enable_conda ? "bioconda::picard=2.26.7" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/picard:2.26.7--hdfd78af_0' : + 'quay.io/biocontainers/picard:2.26.7--hdfd78af_0' }" input: tuple val(meta), path(bam) @@ -25,11 +14,11 @@ process PICARD_COLLECTMULTIPLEMETRICS { output: tuple val(meta), path("*_metrics"), emit: metrics tuple val(meta), path("*.pdf") , emit: pdf - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" def avail_mem = 3 if (!task.memory) { log.info '[Picard CollectMultipleMetrics] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.' @@ -40,11 +29,14 @@ process PICARD_COLLECTMULTIPLEMETRICS { picard \\ -Xmx${avail_mem}g \\ CollectMultipleMetrics \\ - $options.args \\ + $args \\ INPUT=$bam \\ OUTPUT=${prefix}.CollectMultipleMetrics \\ REFERENCE_SEQUENCE=$fasta - echo \$(picard CollectMultipleMetrics --version 2>&1) | grep -o 'Version.*' | cut -f2- -d: > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + picard: \$(picard CollectMultipleMetrics --version 2>&1 | grep -o 'Version.*' | cut -f2- -d:) + END_VERSIONS """ } diff --git a/modules/nf-core/modules/picard/collectmultiplemetrics/meta.yml b/modules/nf-core/modules/picard/collectmultiplemetrics/meta.yml index 34006093..613afc62 100644 --- a/modules/nf-core/modules/picard/collectmultiplemetrics/meta.yml +++ b/modules/nf-core/modules/picard/collectmultiplemetrics/meta.yml @@ -14,6 +14,7 @@ tools: data and formats such as SAM/BAM/CRAM and VCF. homepage: https://broadinstitute.github.io/picard/ documentation: https://broadinstitute.github.io/picard/ + licence: ['MIT'] input: - meta: type: map @@ -41,9 +42,9 @@ output: type: file description: PDF plots of metrics pattern: "*.{pdf}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" diff --git a/modules/nf-core/modules/picard/markduplicates/functions.nf b/modules/nf-core/modules/picard/markduplicates/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/picard/markduplicates/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/picard/markduplicates/main.nf b/modules/nf-core/modules/picard/markduplicates/main.nf index d7647414..3087bff4 100644 --- a/modules/nf-core/modules/picard/markduplicates/main.nf +++ b/modules/nf-core/modules/picard/markduplicates/main.nf @@ -1,34 +1,24 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process PICARD_MARKDUPLICATES { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::picard=2.23.9" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/picard:2.23.9--0" - } else { - container "quay.io/biocontainers/picard:2.23.9--0" - } + conda (params.enable_conda ? "bioconda::picard=2.26.7" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/picard:2.26.7--hdfd78af_0' : + 'quay.io/biocontainers/picard:2.26.7--hdfd78af_0' }" input: tuple val(meta), path(bam) output: tuple val(meta), path("*.bam") , emit: bam + tuple val(meta), path("*.bai") , optional:true, emit: bai tuple val(meta), path("*.metrics.txt"), emit: metrics - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" def avail_mem = 3 if (!task.memory) { log.info '[Picard MarkDuplicates] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.' @@ -39,11 +29,14 @@ process PICARD_MARKDUPLICATES { picard \\ -Xmx${avail_mem}g \\ MarkDuplicates \\ - $options.args \\ - INPUT=$bam \\ - OUTPUT=${prefix}.bam \\ - METRICS_FILE=${prefix}.MarkDuplicates.metrics.txt + $args \\ + I=$bam \\ + O=${prefix}.bam \\ + M=${prefix}.MarkDuplicates.metrics.txt - echo \$(picard MarkDuplicates --version 2>&1) | grep -o 'Version:.*' | cut -f2- -d: > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + picard: \$(echo \$(picard MarkDuplicates --version 2>&1) | grep -o 'Version:.*' | cut -f2- -d:) + END_VERSIONS """ } diff --git a/modules/nf-core/modules/picard/markduplicates/meta.yml b/modules/nf-core/modules/picard/markduplicates/meta.yml index 6420ce9a..c9a08b36 100644 --- a/modules/nf-core/modules/picard/markduplicates/meta.yml +++ b/modules/nf-core/modules/picard/markduplicates/meta.yml @@ -1,46 +1,52 @@ name: picard_markduplicates description: Locate and tag duplicate reads in a BAM file keywords: - - markduplicates - - pcr - - duplicates - - bam - - sam - - cram + - markduplicates + - pcr + - duplicates + - bam + - sam + - cram tools: - - picard: - description: | - A set of command line tools (in Java) for manipulating high-throughput sequencing (HTS) - data and formats such as SAM/BAM/CRAM and VCF. - homepage: https://broadinstitute.github.io/picard/ - documentation: https://broadinstitute.github.io/picard/ + - picard: + description: | + A set of command line tools (in Java) for manipulating high-throughput sequencing (HTS) + data and formats such as SAM/BAM/CRAM and VCF. + homepage: https://broadinstitute.github.io/picard/ + documentation: https://broadinstitute.github.io/picard/ + licence: ['MIT'] input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM file - pattern: "*.{bam}" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: BAM file + pattern: "*.{bam}" output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM file with duplicate reads marked/removed - pattern: "*.{bam}" - - metrics: - type: file - description: Duplicate metrics file generated by picard - pattern: "*.{metrics.txt}" - - version: - type: file - description: File containing software version - pattern: "*.{version.txt}" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: BAM file with duplicate reads marked/removed + pattern: "*.{bam}" + - bai: + type: file + description: An optional BAM index file. If desired, --CREATE_INDEX must be passed as a flag + pattern: "*.{bai}" + - metrics: + type: file + description: Duplicate metrics file generated by picard + pattern: "*.{metrics.txt}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" authors: - - "@drpatelh" + - "@drpatelh" + - "@projectoriented" diff --git a/modules/nf-core/modules/plasmidid/functions.nf b/modules/nf-core/modules/plasmidid/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/plasmidid/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/plasmidid/main.nf b/modules/nf-core/modules/plasmidid/main.nf index 986b6451..7404a678 100644 --- a/modules/nf-core/modules/plasmidid/main.nf +++ b/modules/nf-core/modules/plasmidid/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process PLASMIDID { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? 'bioconda::plasmidid=1.6.5' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/plasmidid:1.6.5--hdfd78af_0' - } else { - container 'quay.io/biocontainers/plasmidid:1.6.5--hdfd78af_0' - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/plasmidid:1.6.5--hdfd78af_0' : + 'quay.io/biocontainers/plasmidid:1.6.5--hdfd78af_0' }" input: tuple val(meta), path(scaffold) @@ -31,20 +20,23 @@ process PLASMIDID { tuple val(meta), path("${prefix}/database/") , emit: database tuple val(meta), path("${prefix}/fasta_files/") , emit: fasta_files tuple val(meta), path("${prefix}/kmer/") , emit: kmer - path '*.version.txt' , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" """ plasmidID \\ -d $fasta \\ -s $prefix \\ -c $scaffold \\ - $options.args \\ + $args \\ -o . mv NO_GROUP/$prefix ./$prefix - echo \$(plasmidID --version 2>&1) > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + plasmidid: \$(echo \$(plasmidID --version 2>&1)) + END_VERSIONS """ } diff --git a/modules/nf-core/modules/plasmidid/meta.yml b/modules/nf-core/modules/plasmidid/meta.yml index b7b188f8..8cde23c5 100644 --- a/modules/nf-core/modules/plasmidid/meta.yml +++ b/modules/nf-core/modules/plasmidid/meta.yml @@ -66,10 +66,10 @@ output: type: directory description: Directory containing the kmer files produced by plasmidid pattern: "database" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" diff --git a/modules/nf-core/modules/pycoqc/functions.nf b/modules/nf-core/modules/pycoqc/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/pycoqc/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/pycoqc/main.nf b/modules/nf-core/modules/pycoqc/main.nf index 3f010247..e966b31c 100644 --- a/modules/nf-core/modules/pycoqc/main.nf +++ b/modules/nf-core/modules/pycoqc/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process PYCOQC { tag "$summary" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "bioconda::pycoqc=2.5.2" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/pycoqc:2.5.2--py_0" - } else { - container "quay.io/biocontainers/pycoqc:2.5.2--py_0" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pycoqc:2.5.2--py_0' : + 'quay.io/biocontainers/pycoqc:2.5.2--py_0' }" input: path summary @@ -24,17 +13,20 @@ process PYCOQC { output: path "*.html" , emit: html path "*.json" , emit: json - path "*.version.txt", emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' """ pycoQC \\ - $options.args \\ + $args \\ -f $summary \\ -o pycoqc.html \\ -j pycoqc.json - echo \$(pycoQC --version 2>&1) | sed 's/^.*pycoQC v//; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pycoqc: \$(pycoQC --version 2>&1 | sed 's/^.*pycoQC v//; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/pycoqc/meta.yml b/modules/nf-core/modules/pycoqc/meta.yml index 059b2f15..33bd6b07 100644 --- a/modules/nf-core/modules/pycoqc/meta.yml +++ b/modules/nf-core/modules/pycoqc/meta.yml @@ -38,10 +38,10 @@ output: type: file description: Results in JSON format pattern: "*.{json}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" diff --git a/modules/nf-core/modules/quast/functions.nf b/modules/nf-core/modules/quast/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/quast/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/quast/main.nf b/modules/nf-core/modules/quast/main.nf index 0b94c410..e88051b5 100644 --- a/modules/nf-core/modules/quast/main.nf +++ b/modules/nf-core/modules/quast/main.nf @@ -1,21 +1,10 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process QUAST { label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? 'bioconda::quast=5.0.2' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container 'https://depot.galaxyproject.org/singularity/quast:5.0.2--py37pl526hb5aa323_2' - } else { - container 'quay.io/biocontainers/quast:5.0.2--py37pl526hb5aa323_2' - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/quast:5.0.2--py37pl526hb5aa323_2' : + 'quay.io/biocontainers/quast:5.0.2--py37pl526hb5aa323_2' }" input: path consensus @@ -27,11 +16,11 @@ process QUAST { output: path "${prefix}" , emit: results path '*.tsv' , emit: tsv - path '*.version.txt', emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - prefix = options.suffix ?: software + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: 'quast' def features = use_gff ? "--features $gff" : '' def reference = use_fasta ? "-r $fasta" : '' """ @@ -40,9 +29,14 @@ process QUAST { $reference \\ $features \\ --threads $task.cpus \\ - $options.args \\ + $args \\ ${consensus.join(' ')} + ln -s ${prefix}/report.tsv - echo \$(quast.py --version 2>&1) | sed 's/^.*QUAST v//; s/ .*\$//' > ${software}.version.txt + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + quast: \$(quast.py --version 2>&1 | sed 's/^.*QUAST v//; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/quast/meta.yml b/modules/nf-core/modules/quast/meta.yml index cc79486e..05faa8b8 100644 --- a/modules/nf-core/modules/quast/meta.yml +++ b/modules/nf-core/modules/quast/meta.yml @@ -9,7 +9,8 @@ tools: description: | QUAST calculates quality metrics for genome assemblies homepage: http://bioinf.spbau.ru/quast - doi: + doi: https://doi.org/10.1093/bioinformatics/btt086 + licence: ['GPL-2.0-only'] input: - consensus: type: file @@ -36,10 +37,10 @@ output: pattern: "{prefix}.lineage_report.csv" - report: - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" diff --git a/modules/nf-core/modules/samtools/flagstat/functions.nf b/modules/nf-core/modules/samtools/flagstat/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/samtools/flagstat/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/samtools/flagstat/main.nf b/modules/nf-core/modules/samtools/flagstat/main.nf index a66ea56d..03721d0b 100644 --- a/modules/nf-core/modules/samtools/flagstat/main.nf +++ b/modules/nf-core/modules/samtools/flagstat/main.nf @@ -1,34 +1,26 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_FLAGSTAT { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::samtools=1.12" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.12--hd5e65b6_0" - } else { - container "quay.io/biocontainers/samtools:1.12--hd5e65b6_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(bam), path(bai) output: tuple val(meta), path("*.flagstat"), emit: flagstat - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' """ - samtools flagstat $bam > ${bam}.flagstat - echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt + samtools flagstat --threads ${task.cpus-1} $bam > ${bam}.flagstat + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/samtools/flagstat/meta.yml b/modules/nf-core/modules/samtools/flagstat/meta.yml index 8414bf54..9bd9ff89 100644 --- a/modules/nf-core/modules/samtools/flagstat/meta.yml +++ b/modules/nf-core/modules/samtools/flagstat/meta.yml @@ -16,6 +16,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -40,9 +41,9 @@ output: type: file description: File containing samtools flagstat output pattern: "*.{flagstat}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" diff --git a/modules/nf-core/modules/samtools/idxstats/functions.nf b/modules/nf-core/modules/samtools/idxstats/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/samtools/idxstats/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/samtools/idxstats/main.nf b/modules/nf-core/modules/samtools/idxstats/main.nf index ff3cd9a6..cd068679 100644 --- a/modules/nf-core/modules/samtools/idxstats/main.nf +++ b/modules/nf-core/modules/samtools/idxstats/main.nf @@ -1,34 +1,26 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_IDXSTATS { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::samtools=1.12" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.12--hd5e65b6_0" - } else { - container "quay.io/biocontainers/samtools:1.12--hd5e65b6_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(bam), path(bai) output: tuple val(meta), path("*.idxstats"), emit: idxstats - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' """ samtools idxstats $bam > ${bam}.idxstats - echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/samtools/idxstats/meta.yml b/modules/nf-core/modules/samtools/idxstats/meta.yml index 530d0772..ec542f34 100644 --- a/modules/nf-core/modules/samtools/idxstats/meta.yml +++ b/modules/nf-core/modules/samtools/idxstats/meta.yml @@ -17,6 +17,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -41,9 +42,9 @@ output: type: file description: File containing samtools idxstats output pattern: "*.{idxstats}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" diff --git a/modules/nf-core/modules/samtools/index/functions.nf b/modules/nf-core/modules/samtools/index/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/samtools/index/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/samtools/index/main.nf b/modules/nf-core/modules/samtools/index/main.nf index 778e9384..db025a8f 100644 --- a/modules/nf-core/modules/samtools/index/main.nf +++ b/modules/nf-core/modules/samtools/index/main.nf @@ -1,35 +1,29 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_INDEX { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::samtools=1.12" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.12--hd5e65b6_0" - } else { - container "quay.io/biocontainers/samtools:1.12--hd5e65b6_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: - tuple val(meta), path(bam) + tuple val(meta), path(input) output: - tuple val(meta), path("*.bai"), optional:true, emit: bai - tuple val(meta), path("*.csi"), optional:true, emit: csi - path "*.version.txt" , emit: version + tuple val(meta), path("*.bai") , optional:true, emit: bai + tuple val(meta), path("*.csi") , optional:true, emit: csi + tuple val(meta), path("*.crai"), optional:true, emit: crai + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' """ - samtools index $options.args $bam - echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt + samtools index -@ ${task.cpus-1} $args $input + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/samtools/index/meta.yml b/modules/nf-core/modules/samtools/index/meta.yml index 5d076e3b..0905b3cd 100644 --- a/modules/nf-core/modules/samtools/index/meta.yml +++ b/modules/nf-core/modules/samtools/index/meta.yml @@ -14,6 +14,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -34,14 +35,19 @@ output: type: file description: BAM/CRAM/SAM index file pattern: "*.{bai,crai,sai}" + - crai: + type: file + description: BAM/CRAM/SAM index file + pattern: "*.{bai,crai,sai}" - csi: type: file description: CSI index file pattern: "*.{csi}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@ewels" + - "@maxulysse" diff --git a/modules/nf-core/modules/samtools/mpileup/functions.nf b/modules/nf-core/modules/samtools/mpileup/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/samtools/mpileup/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/samtools/mpileup/main.nf b/modules/nf-core/modules/samtools/mpileup/main.nf index 8f2cebd1..c40f46d1 100644 --- a/modules/nf-core/modules/samtools/mpileup/main.nf +++ b/modules/nf-core/modules/samtools/mpileup/main.nf @@ -1,22 +1,11 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_MPILEUP { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::samtools=1.12" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.12--hd5e65b6_0" - } else { - container "quay.io/biocontainers/samtools:1.12--hd5e65b6_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(bam) @@ -24,17 +13,20 @@ process SAMTOOLS_MPILEUP { output: tuple val(meta), path("*.mpileup"), emit: mpileup - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ samtools mpileup \\ --fasta-ref $fasta \\ --output ${prefix}.mpileup \\ - $options.args \\ + $args \\ $bam - echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/samtools/mpileup/meta.yml b/modules/nf-core/modules/samtools/mpileup/meta.yml index 7e432a78..fac7a5bc 100644 --- a/modules/nf-core/modules/samtools/mpileup/meta.yml +++ b/modules/nf-core/modules/samtools/mpileup/meta.yml @@ -14,6 +14,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -38,10 +39,10 @@ output: type: file description: mpileup file pattern: "*.{mpileup}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@joseespinosa" diff --git a/modules/nf-core/modules/samtools/sort/functions.nf b/modules/nf-core/modules/samtools/sort/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/samtools/sort/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/samtools/sort/main.nf b/modules/nf-core/modules/samtools/sort/main.nf index 240e8e9f..0c2cf25e 100644 --- a/modules/nf-core/modules/samtools/sort/main.nf +++ b/modules/nf-core/modules/samtools/sort/main.nf @@ -1,35 +1,27 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_SORT { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::samtools=1.12" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.12--hd5e65b6_0" - } else { - container "quay.io/biocontainers/samtools:1.12--hd5e65b6_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: tuple val(meta), path(bam) output: tuple val(meta), path("*.bam"), emit: bam - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ - samtools sort $options.args -@ $task.cpus -o ${prefix}.bam -T $prefix $bam - echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt + samtools sort $args -@ $task.cpus -o ${prefix}.bam -T $prefix $bam + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/samtools/sort/meta.yml b/modules/nf-core/modules/samtools/sort/meta.yml index 704e8c1f..3402a068 100644 --- a/modules/nf-core/modules/samtools/sort/meta.yml +++ b/modules/nf-core/modules/samtools/sort/meta.yml @@ -14,6 +14,7 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -34,10 +35,10 @@ output: type: file description: Sorted BAM/CRAM/SAM file pattern: "*.{bam,cram,sam}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@ewels" diff --git a/modules/nf-core/modules/samtools/stats/functions.nf b/modules/nf-core/modules/samtools/stats/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/samtools/stats/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/samtools/stats/main.nf b/modules/nf-core/modules/samtools/stats/main.nf index 6bb0a4c7..83c87002 100644 --- a/modules/nf-core/modules/samtools/stats/main.nf +++ b/modules/nf-core/modules/samtools/stats/main.nf @@ -1,34 +1,29 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_STATS { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::samtools=1.12" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.12--hd5e65b6_0" - } else { - container "quay.io/biocontainers/samtools:1.12--hd5e65b6_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: - tuple val(meta), path(bam), path(bai) + tuple val(meta), path(input), path(input_index) + path fasta output: tuple val(meta), path("*.stats"), emit: stats - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' + def reference = fasta ? "--reference ${fasta}" : "" """ - samtools stats $bam > ${bam}.stats - echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt + samtools stats --threads ${task.cpus-1} ${reference} ${input} > ${input}.stats + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/samtools/stats/meta.yml b/modules/nf-core/modules/samtools/stats/meta.yml index b549ff5c..869e62e3 100644 --- a/modules/nf-core/modules/samtools/stats/meta.yml +++ b/modules/nf-core/modules/samtools/stats/meta.yml @@ -15,20 +15,25 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - bam: - type: file - description: BAM/CRAM/SAM file - pattern: "*.{bam,cram,sam}" - - bai: - type: file - description: Index for BAM/CRAM/SAM file - pattern: "*.{bai,crai,sai}" + - input: + type: file + description: BAM/CRAM file from alignment + pattern: "*.{bam,cram}" + - input_index: + type: file + description: BAI/CRAI file from alignment + pattern: "*.{bai,crai}" + - fasta: + type: optional file + description: Reference file the CRAM was created with + pattern: "*.{fasta,fa}" output: - meta: type: map @@ -39,9 +44,10 @@ output: type: file description: File containing samtools stats output pattern: "*.{stats}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" + - "@FriederikeHanssen" diff --git a/modules/nf-core/modules/samtools/view/functions.nf b/modules/nf-core/modules/samtools/view/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/samtools/view/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/samtools/view/main.nf b/modules/nf-core/modules/samtools/view/main.nf index ec6c747f..619b84dc 100644 --- a/modules/nf-core/modules/samtools/view/main.nf +++ b/modules/nf-core/modules/samtools/view/main.nf @@ -1,35 +1,32 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SAMTOOLS_VIEW { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::samtools=1.12" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/samtools:1.12--hd5e65b6_0" - } else { - container "quay.io/biocontainers/samtools:1.12--hd5e65b6_0" - } + conda (params.enable_conda ? "bioconda::samtools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.14--hb421002_0' : + 'quay.io/biocontainers/samtools:1.14--hb421002_0' }" input: - tuple val(meta), path(bam) + tuple val(meta), path(input) + path fasta output: - tuple val(meta), path("*.bam"), emit: bam - path "*.version.txt" , emit: version + tuple val(meta), path("*.bam") , emit: bam , optional: true + tuple val(meta), path("*.cram"), emit: cram, optional: true + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def reference = fasta ? "--reference ${fasta} -C" : "" + def file_type = input.getExtension() """ - samtools view $options.args $bam > ${prefix}.bam - echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//' > ${software}.version.txt + samtools view --threads ${task.cpus-1} ${reference} $args $input > ${prefix}.${file_type} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/samtools/view/meta.yml b/modules/nf-core/modules/samtools/view/meta.yml index c35a8b03..8abf34af 100644 --- a/modules/nf-core/modules/samtools/view/meta.yml +++ b/modules/nf-core/modules/samtools/view/meta.yml @@ -14,16 +14,21 @@ tools: homepage: http://www.htslib.org/ documentation: hhttp://www.htslib.org/doc/samtools.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - bam: + - input: type: file description: BAM/CRAM/SAM file pattern: "*.{bam,cram,sam}" + - fasta: + type: optional file + description: Reference file the CRAM was created with + pattern: "*.{fasta,fa}" output: - meta: type: map @@ -32,12 +37,17 @@ output: e.g. [ id:'test', single_end:false ] - bam: type: file - description: filtered/converted BAM/CRAM/SAM file - pattern: "*.{bam,cram,sam}" - - version: + description: filtered/converted BAM/SAM file + pattern: "*.{bam,sam}" + - cram: + type: file + description: filtered/converted CRAM file + pattern: "*.cram" + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@joseespinosa" + - "@FriederikeHanssen" diff --git a/modules/nf-core/modules/spades/functions.nf b/modules/nf-core/modules/spades/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/spades/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/spades/main.nf b/modules/nf-core/modules/spades/main.nf index c6208053..ba690d35 100644 --- a/modules/nf-core/modules/spades/main.nf +++ b/modules/nf-core/modules/spades/main.nf @@ -1,67 +1,70 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process SPADES { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::spades=3.15.2" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/spades:3.15.2--h95f258a_1" - } else { - container "quay.io/biocontainers/spades:3.15.2--h95f258a_1" - } + conda (params.enable_conda ? 'bioconda::spades=3.15.3' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/spades:3.15.3--h95f258a_0' : + 'quay.io/biocontainers/spades:3.15.3--h95f258a_0' }" input: - tuple val(meta), path(reads) + tuple val(meta), path(illumina), path(pacbio), path(nanopore) path hmm output: - tuple val(meta), path('*.scaffolds.fa') , optional:true, emit: scaffolds - tuple val(meta), path('*.contigs.fa') , optional:true, emit: contigs - tuple val(meta), path('*.transcripts.fa') , optional:true, emit: transcripts - tuple val(meta), path('*.gene_clusters.fa'), optional:true, emit: gene_clusters - tuple val(meta), path('*.assembly.gfa') , optional:true, emit: gfa - tuple val(meta), path('*.log') , emit: log - path '*.version.txt' , emit: version + tuple val(meta), path('*.scaffolds.fa.gz') , optional:true, emit: scaffolds + tuple val(meta), path('*.contigs.fa.gz') , optional:true, emit: contigs + tuple val(meta), path('*.transcripts.fa.gz') , optional:true, emit: transcripts + tuple val(meta), path('*.gene_clusters.fa.gz'), optional:true, emit: gene_clusters + tuple val(meta), path('*.assembly.gfa.gz') , optional:true, emit: gfa + tuple val(meta), path('*.log') , emit: log + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" - def input_reads = meta.single_end ? "-s $reads" : "-1 ${reads[0]} -2 ${reads[1]}" - def custom_hmms = params.spades_hmm ? "--custom-hmms $hmm" : "" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def maxmem = task.memory.toGiga() + def illumina_reads = illumina ? ( meta.single_end ? "-s $illumina" : "-1 ${illumina[0]} -2 ${illumina[1]}" ) : "" + def pacbio_reads = pacbio ? "--pacbio $pacbio" : "" + def nanopore_reads = nanopore ? "--nanopore $nanopore" : "" + def custom_hmms = hmm ? "--custom-hmms $hmm" : "" """ spades.py \\ - $options.args \\ + $args \\ --threads $task.cpus \\ + --memory $maxmem \\ $custom_hmms \\ - $input_reads \\ + $illumina_reads \\ + $pacbio_reads \\ + $nanopore_reads \\ -o ./ mv spades.log ${prefix}.spades.log if [ -f scaffolds.fasta ]; then mv scaffolds.fasta ${prefix}.scaffolds.fa + gzip -n ${prefix}.scaffolds.fa fi if [ -f contigs.fasta ]; then mv contigs.fasta ${prefix}.contigs.fa + gzip -n ${prefix}.contigs.fa fi if [ -f transcripts.fasta ]; then mv transcripts.fasta ${prefix}.transcripts.fa + gzip -n ${prefix}.transcripts.fa fi if [ -f assembly_graph_with_scaffolds.gfa ]; then mv assembly_graph_with_scaffolds.gfa ${prefix}.assembly.gfa + gzip -n ${prefix}.assembly.gfa fi if [ -f gene_clusters.fasta ]; then mv gene_clusters.fasta ${prefix}.gene_clusters.fa + gzip -n ${prefix}.gene_clusters.fa fi - echo \$(spades.py --version 2>&1) | sed 's/^.*SPAdes genome assembler v//; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + spades: \$(spades.py --version 2>&1 | sed 's/^.*SPAdes genome assembler v//; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/spades/meta.yml b/modules/nf-core/modules/spades/meta.yml index 5a05e5f3..b6878d3d 100644 --- a/modules/nf-core/modules/spades/meta.yml +++ b/modules/nf-core/modules/spades/meta.yml @@ -20,11 +20,20 @@ input: description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - reads: + - illumina: type: file description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. + List of input FastQ (Illumina or PacBio CCS reads) files + of size 1 and 2 for single-end and paired-end data, + respectively. This input data type is required. + - pacbio: + type: file + description: | + List of input PacBio CLR FastQ files of size 1. + - nanopore: + type: file + description: | + List of input FastQ files of size 1, originating from Oxford Nanopore technology. - hmm: type: file description: @@ -39,31 +48,38 @@ output: type: file description: | Fasta file containing scaffolds + pattern: "*.fa.gz" - contigs: type: file description: | Fasta file containing contigs + pattern: "*.fa.gz" - transcripts: type: file description: | Fasta file containing transcripts + pattern: "*.fa.gz" - gene_clusters: type: file description: | Fasta file containing gene_clusters + pattern: "*.fa.gz" - gfa: type: file description: | gfa file containing assembly + pattern: "*.gfa.gz" - log: type: file description: | Spades log file - - version: + pattern: "*.log" + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@JoseEspinosa" - "@drpatelh" + - "@d4straub" diff --git a/modules/nf-core/modules/tabix/bgzip/functions.nf b/modules/nf-core/modules/tabix/bgzip/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/tabix/bgzip/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/tabix/bgzip/main.nf b/modules/nf-core/modules/tabix/bgzip/main.nf index 56a351db..ed9362b2 100644 --- a/modules/nf-core/modules/tabix/bgzip/main.nf +++ b/modules/nf-core/modules/tabix/bgzip/main.nf @@ -1,36 +1,28 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process TABIX_BGZIP { tag "$meta.id" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::tabix=0.2.6" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/tabix:0.2.6--ha92aebf_0" - } else { - container "quay.io/biocontainers/tabix:0.2.6--ha92aebf_0" - } + conda (params.enable_conda ? 'bioconda::tabix=1.11' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/tabix:1.11--hdfd78af_0' : + 'quay.io/biocontainers/tabix:1.11--hdfd78af_0' }" input: tuple val(meta), path(input) output: tuple val(meta), path("*.gz"), emit: gz - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" """ - bgzip -c $options.args $input > ${prefix}.${input.getExtension()}.gz + bgzip -c $args $input > ${prefix}.${input.getExtension()}.gz - echo \$(tabix -h 2>&1) | sed 's/^.*Version: //; s/(.*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + tabix: \$(echo \$(tabix -h 2>&1) | sed 's/^.*Version: //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/tabix/bgzip/meta.yml b/modules/nf-core/modules/tabix/bgzip/meta.yml index 686d72e6..f8318c7c 100644 --- a/modules/nf-core/modules/tabix/bgzip/meta.yml +++ b/modules/nf-core/modules/tabix/bgzip/meta.yml @@ -11,6 +11,7 @@ tools: homepage: https://www.htslib.org/doc/tabix.html documentation: http://www.htslib.org/doc/bgzip.html doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] input: - meta: type: map @@ -30,10 +31,10 @@ output: type: file description: Output compressed file pattern: "*.{gz}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/tabix/tabix/functions.nf b/modules/nf-core/modules/tabix/tabix/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/tabix/tabix/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/tabix/tabix/main.nf b/modules/nf-core/modules/tabix/tabix/main.nf index da23f535..c721a554 100644 --- a/modules/nf-core/modules/tabix/tabix/main.nf +++ b/modules/nf-core/modules/tabix/tabix/main.nf @@ -1,35 +1,27 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process TABIX_TABIX { tag "$meta.id" label 'process_medium' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } - conda (params.enable_conda ? "bioconda::tabix=0.2.6" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/tabix:0.2.6--ha92aebf_0" - } else { - container "quay.io/biocontainers/tabix:0.2.6--ha92aebf_0" - } + conda (params.enable_conda ? 'bioconda::tabix=1.11' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/tabix:1.11--hdfd78af_0' : + 'quay.io/biocontainers/tabix:1.11--hdfd78af_0' }" input: tuple val(meta), path(tab) output: tuple val(meta), path("*.tbi"), emit: tbi - path "*.version.txt" , emit: version + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' """ - tabix $options.args $tab + tabix $args $tab - echo \$(tabix -h 2>&1) | sed 's/^.*Version: //; s/(.*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + tabix: \$(echo \$(tabix -h 2>&1) | sed 's/^.*Version: //; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/tabix/tabix/meta.yml b/modules/nf-core/modules/tabix/tabix/meta.yml index f66270db..2e37c4ff 100644 --- a/modules/nf-core/modules/tabix/tabix/meta.yml +++ b/modules/nf-core/modules/tabix/tabix/meta.yml @@ -10,6 +10,7 @@ tools: homepage: https://www.htslib.org/doc/tabix.html documentation: https://www.htslib.org/doc/tabix.1.html doi: 10.1093/bioinformatics/btq671 + licence: ['MIT'] input: - meta: type: map @@ -30,10 +31,10 @@ output: type: file description: tabix index file pattern: "*.{tbi}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/modules/unicycler/functions.nf b/modules/nf-core/modules/unicycler/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/unicycler/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/unicycler/main.nf b/modules/nf-core/modules/unicycler/main.nf index 320c0f29..1ccc72a9 100644 --- a/modules/nf-core/modules/unicycler/main.nf +++ b/modules/nf-core/modules/unicycler/main.nf @@ -1,47 +1,43 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process UNICYCLER { tag "$meta.id" label 'process_high' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:meta, publish_by_meta:['id']) } conda (params.enable_conda ? 'bioconda::unicycler=0.4.8' : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/unicycler:0.4.8--py38h8162308_3" - } else { - container "quay.io/biocontainers/unicycler:0.4.8--py38h8162308_3" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/unicycler:0.4.8--py38h8162308_3' : + 'quay.io/biocontainers/unicycler:0.4.8--py38h8162308_3' }" input: - tuple val(meta), path(reads) + tuple val(meta), path(shortreads), path(longreads) output: - tuple val(meta), path('*.scaffolds.fa'), emit: scaffolds - tuple val(meta), path('*.assembly.gfa'), emit: gfa - tuple val(meta), path('*.log') , emit: log - path '*.version.txt' , emit: version + tuple val(meta), path('*.scaffolds.fa.gz'), emit: scaffolds + tuple val(meta), path('*.assembly.gfa.gz'), emit: gfa + tuple val(meta), path('*.log') , emit: log + path "versions.yml" , emit: versions script: - def software = getSoftwareName(task.process) - def prefix = options.suffix ? "${meta.id}${options.suffix}" : "${meta.id}" - def input_reads = meta.single_end ? "-s $reads" : "-1 ${reads[0]} -2 ${reads[1]}" + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def short_reads = shortreads ? ( meta.single_end ? "-s $shortreads" : "-1 ${shortreads[0]} -2 ${shortreads[1]}" ) : "" + def long_reads = longreads ? "-l $longreads" : "" """ unicycler \\ --threads $task.cpus \\ - $options.args \\ - $input_reads \\ + $args \\ + $short_reads \\ + $long_reads \\ --out ./ mv assembly.fasta ${prefix}.scaffolds.fa + gzip -n ${prefix}.scaffolds.fa mv assembly.gfa ${prefix}.assembly.gfa + gzip -n ${prefix}.assembly.gfa mv unicycler.log ${prefix}.unicycler.log - echo \$(unicycler --version 2>&1) | sed 's/^.*Unicycler v//; s/ .*\$//' > ${software}.version.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + unicycler: \$(echo \$(unicycler --version 2>&1) | sed 's/^.*Unicycler v//; s/ .*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/unicycler/meta.yml b/modules/nf-core/modules/unicycler/meta.yml index 286b7f67..b04ac882 100644 --- a/modules/nf-core/modules/unicycler/meta.yml +++ b/modules/nf-core/modules/unicycler/meta.yml @@ -19,37 +19,42 @@ input: description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - reads: + - shortreads: type: file description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, + List of input Illumina FastQ files of size 1 and 2 for single-end and paired-end data, respectively. + - longreads: + type: file + description: | + List of input FastQ files of size 1, PacBio or Nanopore long reads. output: - meta: type: map description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" - scaffolds: type: file description: Fasta file containing scaffolds - pattern: "*.{scaffolds.fa}" + pattern: "*.{scaffolds.fa.gz}" - gfa: type: file description: gfa file containing assembly - pattern: "*.{assembly.gfa}" + pattern: "*.{assembly.gfa.gz}" - log: type: file description: unicycler log file pattern: "*.{log}" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@JoseEspinosa" - "@drpatelh" + - "@d4straub" diff --git a/modules/nf-core/modules/untar/functions.nf b/modules/nf-core/modules/untar/functions.nf deleted file mode 100644 index da9da093..00000000 --- a/modules/nf-core/modules/untar/functions.nf +++ /dev/null @@ -1,68 +0,0 @@ -// -// Utility functions used in nf-core DSL2 module files -// - -// -// Extract name of software tool from process name using $task.process -// -def getSoftwareName(task_process) { - return task_process.tokenize(':')[-1].tokenize('_')[0].toLowerCase() -} - -// -// Function to initialise default values and to generate a Groovy Map of available options for nf-core modules -// -def initOptions(Map args) { - def Map options = [:] - options.args = args.args ?: '' - options.args2 = args.args2 ?: '' - options.args3 = args.args3 ?: '' - options.publish_by_meta = args.publish_by_meta ?: [] - options.publish_dir = args.publish_dir ?: '' - options.publish_files = args.publish_files - options.suffix = args.suffix ?: '' - return options -} - -// -// Tidy up and join elements of a list to return a path string -// -def getPathFromList(path_list) { - def paths = path_list.findAll { item -> !item?.trim().isEmpty() } // Remove empty entries - paths = paths.collect { it.trim().replaceAll("^[/]+|[/]+\$", "") } // Trim whitespace and trailing slashes - return paths.join('/') -} - -// -// Function to save/publish module results -// -def saveFiles(Map args) { - if (!args.filename.endsWith('.version.txt')) { - def ioptions = initOptions(args.options) - def path_list = [ ioptions.publish_dir ?: args.publish_dir ] - if (ioptions.publish_by_meta) { - def key_list = ioptions.publish_by_meta instanceof List ? ioptions.publish_by_meta : args.publish_by_meta - for (key in key_list) { - if (args.meta && key instanceof String) { - def path = key - if (args.meta.containsKey(key)) { - path = args.meta[key] instanceof Boolean ? "${key}_${args.meta[key]}".toString() : args.meta[key] - } - path = path instanceof String ? path : '' - path_list.add(path) - } - } - } - if (ioptions.publish_files instanceof Map) { - for (ext in ioptions.publish_files) { - if (args.filename.endsWith(ext.key)) { - def ext_list = path_list.collect() - ext_list.add(ext.value) - return "${getPathFromList(ext_list)}/$args.filename" - } - } - } else if (ioptions.publish_files == null) { - return "${getPathFromList(path_list)}/$args.filename" - } - } -} diff --git a/modules/nf-core/modules/untar/main.nf b/modules/nf-core/modules/untar/main.nf index fc6d7ec5..6d1996e7 100644 --- a/modules/nf-core/modules/untar/main.nf +++ b/modules/nf-core/modules/untar/main.nf @@ -1,35 +1,33 @@ -// Import generic module functions -include { initOptions; saveFiles; getSoftwareName } from './functions' - -params.options = [:] -options = initOptions(params.options) - process UNTAR { tag "$archive" label 'process_low' - publishDir "${params.outdir}", - mode: params.publish_dir_mode, - saveAs: { filename -> saveFiles(filename:filename, options:params.options, publish_dir:getSoftwareName(task.process), meta:[:], publish_by_meta:[]) } conda (params.enable_conda ? "conda-forge::sed=4.7" : null) - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { - container "https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img" - } else { - container "biocontainers/biocontainers:v1.2.0_cv1" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + 'biocontainers/biocontainers:v1.2.0_cv1' }" input: path archive output: - path "$untar" , emit: untar - path "*.version.txt", emit: version + path "$untar" , emit: untar + path "versions.yml", emit: versions script: - def software = getSoftwareName(task.process) + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' untar = archive.toString() - '.tar.gz' """ - tar -xzvf $options.args $archive - echo \$(tar --version 2>&1) | sed 's/^.*(GNU tar) //; s/ Copyright.*\$//' > ${software}.version.txt + tar \\ + -xzvf \\ + $args \\ + $archive \\ + $args2 \\ + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + untar: \$(echo \$(tar --version 2>&1) | sed 's/^.*(GNU tar) //; s/ Copyright.*\$//') + END_VERSIONS """ } diff --git a/modules/nf-core/modules/untar/meta.yml b/modules/nf-core/modules/untar/meta.yml index af4674f0..51f94995 100644 --- a/modules/nf-core/modules/untar/meta.yml +++ b/modules/nf-core/modules/untar/meta.yml @@ -8,6 +8,7 @@ tools: description: | Extract tar.gz files. documentation: https://www.gnu.org/software/tar/manual/ + licence: ['GPL-3.0-or-later'] input: - archive: type: file @@ -18,10 +19,10 @@ output: type: file description: pattern: "*.*" - - version: + - versions: type: file - description: File containing software version - pattern: "*.{version.txt}" + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/subworkflows/local/assembly_minia.nf b/subworkflows/local/assembly_minia.nf index 16e755e5..43d34c6e 100644 --- a/subworkflows/local/assembly_minia.nf +++ b/subworkflows/local/assembly_minia.nf @@ -2,15 +2,9 @@ // Assembly and downstream processing for minia scaffolds // -params.minia_options = [:] -params.blastn_options = [:] -params.blastn_filter_options = [:] -params.abacas_options = [:] -params.plasmidid_options = [:] -params.quast_options = [:] +include { MINIA } from '../../modules/nf-core/modules/minia/main' -include { MINIA } from '../../modules/nf-core/modules/minia/main' addParams( options: params.minia_options ) -include { ASSEMBLY_QC } from './assembly_qc' addParams( blastn_options: params.blastn_options, blastn_filter_options: params.blastn_filter_options, abacas_options: params.abacas_options, plasmidid_options: params.plasmidid_options, quast_options: params.quast_options ) +include { ASSEMBLY_QC } from './assembly_qc' workflow ASSEMBLY_MINIA { take: @@ -22,10 +16,15 @@ workflow ASSEMBLY_MINIA { main: + ch_versions = Channel.empty() + // // Assemble reads with minia // - MINIA ( reads ) + MINIA ( + reads + ) + ch_versions = ch_versions.mix(MINIA.out.versions.first()) // // Filter for empty contig files @@ -46,23 +45,20 @@ workflow ASSEMBLY_MINIA { blast_db, blast_header ) + ch_versions = ch_versions.mix(ASSEMBLY_QC.out.versions) emit: contigs = MINIA.out.contigs // channel: [ val(meta), [ contigs ] ] unitigs = MINIA.out.unitigs // channel: [ val(meta), [ unitigs ] ] h5 = MINIA.out.h5 // channel: [ val(meta), [ h5 ] ] - minia_version = MINIA.out.version // path: *.version.txt blast_txt = ASSEMBLY_QC.out.blast_txt // channel: [ val(meta), [ txt ] ] blast_filter_txt = ASSEMBLY_QC.out.blast_filter_txt // channel: [ val(meta), [ txt ] ] - blast_version = ASSEMBLY_QC.out.blast_version // path: *.version.txt quast_results = ASSEMBLY_QC.out.quast_results // channel: [ val(meta), [ results ] ] quast_tsv = ASSEMBLY_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] - quast_version = ASSEMBLY_QC.out.quast_version // path: *.version.txt abacas_results = ASSEMBLY_QC.out.abacas_results // channel: [ val(meta), [ results ] ] - abacas_version = ASSEMBLY_QC.out.abacas_version // path: *.version.txt plasmidid_html = ASSEMBLY_QC.out.plasmidid_html // channel: [ val(meta), [ html ] ] plasmidid_tab = ASSEMBLY_QC.out.plasmidid_tab // channel: [ val(meta), [ tab ] ] @@ -72,5 +68,6 @@ workflow ASSEMBLY_MINIA { plasmidid_database = ASSEMBLY_QC.out.plasmidid_database // channel: [ val(meta), [ database/ ] ] plasmidid_fasta = ASSEMBLY_QC.out.plasmidid_fasta // channel: [ val(meta), [ fasta_files/ ] ] plasmidid_kmer = ASSEMBLY_QC.out.plasmidid_kmer // channel: [ val(meta), [ kmer/ ] ] - plasmidid_version = ASSEMBLY_QC.out.plasmidid_version // path: *.version.txt + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/assembly_qc.nf b/subworkflows/local/assembly_qc.nf index 090a85a0..9c42c652 100644 --- a/subworkflows/local/assembly_qc.nf +++ b/subworkflows/local/assembly_qc.nf @@ -2,17 +2,11 @@ // Downstream analysis for assembly scaffolds // -params.blastn_options = [:] -params.blastn_filter_options = [:] -params.abacas_options = [:] -params.plasmidid_options = [:] -params.quast_options = [:] - -include { FILTER_BLASTN } from '../../modules/local/filter_blastn' addParams( options: params.blastn_filter_options ) -include { ABACAS } from '../../modules/nf-core/modules/abacas/main' addParams( options: params.abacas_options ) -include { BLAST_BLASTN } from '../../modules/nf-core/modules/blast/blastn/main' addParams( options: params.blastn_options ) -include { PLASMIDID } from '../../modules/nf-core/modules/plasmidid/main' addParams( options: params.plasmidid_options ) -include { QUAST } from '../../modules/nf-core/modules/quast/main' addParams( options: params.quast_options ) +include { FILTER_BLASTN } from '../../modules/local/filter_blastn' +include { ABACAS } from '../../modules/nf-core/modules/abacas/main' +include { BLAST_BLASTN } from '../../modules/nf-core/modules/blast/blastn/main' +include { PLASMIDID } from '../../modules/nf-core/modules/plasmidid/main' +include { QUAST } from '../../modules/nf-core/modules/quast/main' workflow ASSEMBLY_QC { take: @@ -24,19 +18,27 @@ workflow ASSEMBLY_QC { main: + ch_versions = Channel.empty() + // // Run blastn on assembly scaffolds // ch_blast_txt = Channel.empty() ch_blast_filter_txt = Channel.empty() - ch_blast_version = Channel.empty() if (!params.skip_blast) { - BLAST_BLASTN ( scaffolds, blast_db ) - ch_blast_txt = BLAST_BLASTN.out.txt - ch_blast_version = BLAST_BLASTN.out.version + BLAST_BLASTN ( + scaffolds, + blast_db + ) + ch_blast_txt = BLAST_BLASTN.out.txt + ch_versions = ch_versions.mix(BLAST_BLASTN.out.versions.first()) - FILTER_BLASTN ( BLAST_BLASTN.out.txt, blast_header ) + FILTER_BLASTN ( + BLAST_BLASTN.out.txt, + blast_header + ) ch_blast_filter_txt = FILTER_BLASTN.out.txt + ch_versions = ch_versions.mix(FILTER_BLASTN.out.versions.first()) } // @@ -44,23 +46,30 @@ workflow ASSEMBLY_QC { // ch_quast_results = Channel.empty() ch_quast_tsv = Channel.empty() - ch_quast_version = Channel.empty() if (!params.skip_assembly_quast) { - QUAST ( scaffolds.collect{ it[1] }, fasta, gff, true, params.gff ) + QUAST ( + scaffolds.collect{ it[1] }, + fasta, + gff, + true, + params.gff + ) ch_quast_results = QUAST.out.results ch_quast_tsv = QUAST.out.tsv - ch_quast_version = QUAST.out.version + ch_versions = ch_versions.mix(QUAST.out.versions) } // // Contiguate assembly with ABACAS // ch_abacas_results = Channel.empty() - ch_abacas_version = Channel.empty() if (!params.skip_abacas) { - ABACAS ( scaffolds, fasta ) + ABACAS ( + scaffolds, + fasta + ) ch_abacas_results = ABACAS.out.results - ch_abacas_version = ABACAS.out.version + ch_versions = ch_versions.mix(ABACAS.out.versions.first()) } // @@ -74,9 +83,11 @@ workflow ASSEMBLY_QC { ch_plasmidid_database = Channel.empty() ch_plasmidid_fasta = Channel.empty() ch_plasmidid_kmer = Channel.empty() - ch_plasmidid_version = Channel.empty() if (!params.skip_plasmidid) { - PLASMIDID ( scaffolds, fasta ) + PLASMIDID ( + scaffolds, + fasta + ) ch_plasmidid_html = PLASMIDID.out.html ch_plasmidid_tab = PLASMIDID.out.tab ch_plasmidid_images = PLASMIDID.out.images @@ -85,20 +96,17 @@ workflow ASSEMBLY_QC { ch_plasmidid_database = PLASMIDID.out.database ch_plasmidid_fasta = PLASMIDID.out.fasta_files ch_plasmidid_kmer = PLASMIDID.out.kmer - ch_plasmidid_version = PLASMIDID.out.version + ch_versions = ch_versions.mix(PLASMIDID.out.versions.first()) } emit: blast_txt = ch_blast_txt // channel: [ val(meta), [ txt ] ] blast_filter_txt = ch_blast_filter_txt // channel: [ val(meta), [ txt ] ] - blast_version = ch_blast_version // path: *.version.txt quast_results = ch_quast_results // channel: [ val(meta), [ results ] ] quast_tsv = ch_quast_tsv // channel: [ val(meta), [ tsv ] ] - quast_version = ch_quast_version // path: *.version.txt abacas_results = ch_abacas_results // channel: [ val(meta), [ results ] ] - abacas_version = ch_abacas_version // path: *.version.txt plasmidid_html = ch_plasmidid_html // channel: [ val(meta), [ html ] ] plasmidid_tab = ch_plasmidid_tab // channel: [ val(meta), [ tab ] ] @@ -108,5 +116,6 @@ workflow ASSEMBLY_QC { plasmidid_database = ch_plasmidid_database // channel: [ val(meta), [ database/ ] ] plasmidid_fasta = ch_plasmidid_fasta // channel: [ val(meta), [ fasta_files/ ] ] plasmidid_kmer = ch_plasmidid_kmer // channel: [ val(meta), [ kmer/ ] ] - plasmidid_version = ch_plasmidid_version // path: *.version.txt + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/assembly_spades.nf b/subworkflows/local/assembly_spades.nf index 205a67d4..01fa0b96 100644 --- a/subworkflows/local/assembly_spades.nf +++ b/subworkflows/local/assembly_spades.nf @@ -2,17 +2,10 @@ // Assembly and downstream processing for SPAdes scaffolds // -params.spades_options = [:] -params.bandage_options = [:] -params.blastn_options = [:] -params.blastn_filter_options = [:] -params.abacas_options = [:] -params.plasmidid_options = [:] -params.quast_options = [:] - -include { SPADES } from '../../modules/nf-core/modules/spades/main' addParams( options: params.spades_options ) -include { BANDAGE_IMAGE } from '../../modules/nf-core/modules/bandage/image/main' addParams( options: params.bandage_options ) -include { ASSEMBLY_QC } from './assembly_qc' addParams( blastn_options: params.blastn_options, blastn_filter_options: params.blastn_filter_options, abacas_options: params.abacas_options, plasmidid_options: params.plasmidid_options, quast_options: params.quast_options ) +include { SPADES } from '../../modules/nf-core/modules/spades/main' +include { BANDAGE_IMAGE } from '../../modules/nf-core/modules/bandage/image/main' + +include { ASSEMBLY_QC } from './assembly_qc' workflow ASSEMBLY_SPADES { take: @@ -25,20 +18,26 @@ workflow ASSEMBLY_SPADES { main: + ch_versions = Channel.empty() + // // Filter for paired-end samples if running metaSPAdes / metaviralSPAdes / metaplasmidSPAdes // ch_reads = reads - if (params.spades_options.args.contains('--meta') || params.spades_options.args.contains('--bio')) { - reads - .filter { meta, fastq -> !meta.single_end } - .set { ch_reads } - } + // if (params.spades_options.args.contains('--meta') || params.spades_options.args.contains('--bio')) { + // reads + // .filter { meta, fastq -> !meta.single_end } + // .set { ch_reads } + // } // // Assemble reads with SPAdes // - SPADES ( ch_reads, hmm ) + SPADES ( + ch_reads, + hmm + ) + ch_versions = ch_versions.mix(SPADES.out.versions.first()) // // Filter for empty scaffold files @@ -58,14 +57,15 @@ workflow ASSEMBLY_SPADES { // // Generate assembly visualisation with Bandage // - ch_bandage_png = Channel.empty() - ch_bandage_svg = Channel.empty() - ch_bandage_version = Channel.empty() + ch_bandage_png = Channel.empty() + ch_bandage_svg = Channel.empty() if (!params.skip_bandage) { - BANDAGE_IMAGE ( ch_gfa ) - ch_bandage_version = BANDAGE_IMAGE.out.version - ch_bandage_png = BANDAGE_IMAGE.out.png - ch_bandage_svg = BANDAGE_IMAGE.out.svg + BANDAGE_IMAGE ( + ch_gfa + ) + ch_bandage_png = BANDAGE_IMAGE.out.png + ch_bandage_svg = BANDAGE_IMAGE.out.svg + ch_versions = ch_versions.mix(BANDAGE_IMAGE.out.versions.first()) } // @@ -78,6 +78,7 @@ workflow ASSEMBLY_SPADES { blast_db, blast_header ) + ch_versions = ch_versions.mix(ASSEMBLY_QC.out.versions) emit: scaffolds = SPADES.out.scaffolds // channel: [ val(meta), [ scaffolds ] ] @@ -86,22 +87,17 @@ workflow ASSEMBLY_SPADES { gene_clusters = SPADES.out.gene_clusters // channel: [ val(meta), [ gene_clusters ] ] gfa = SPADES.out.gfa // channel: [ val(meta), [ gfa ] ] log_out = SPADES.out.log // channel: [ val(meta), [ log ] ] - spades_version = SPADES.out.version // path: *.version.txt bandage_png = ch_bandage_png // channel: [ val(meta), [ png ] ] bandage_svg = ch_bandage_svg // channel: [ val(meta), [ svg ] ] - bandage_version = ch_bandage_version // path: *.version.txt blast_txt = ASSEMBLY_QC.out.blast_txt // channel: [ val(meta), [ txt ] ] blast_filter_txt = ASSEMBLY_QC.out.blast_filter_txt // channel: [ val(meta), [ txt ] ] - blast_version = ASSEMBLY_QC.out.blast_version // path: *.version.txt quast_results = ASSEMBLY_QC.out.quast_results // channel: [ val(meta), [ results ] ] quast_tsv = ASSEMBLY_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] - quast_version = ASSEMBLY_QC.out.quast_version // path: *.version.txt abacas_results = ASSEMBLY_QC.out.abacas_results // channel: [ val(meta), [ results ] ] - abacas_version = ASSEMBLY_QC.out.abacas_version // path: *.version.txt plasmidid_html = ASSEMBLY_QC.out.plasmidid_html // channel: [ val(meta), [ html ] ] plasmidid_tab = ASSEMBLY_QC.out.plasmidid_tab // channel: [ val(meta), [ tab ] ] @@ -111,5 +107,6 @@ workflow ASSEMBLY_SPADES { plasmidid_database = ASSEMBLY_QC.out.plasmidid_database // channel: [ val(meta), [ database/ ] ] plasmidid_fasta = ASSEMBLY_QC.out.plasmidid_fasta // channel: [ val(meta), [ fasta_files/ ] ] plasmidid_kmer = ASSEMBLY_QC.out.plasmidid_kmer // channel: [ val(meta), [ kmer/ ] ] - plasmidid_version = ASSEMBLY_QC.out.plasmidid_version // path: *.version.txt + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/assembly_unicycler.nf b/subworkflows/local/assembly_unicycler.nf index 3d2d7787..79265a54 100644 --- a/subworkflows/local/assembly_unicycler.nf +++ b/subworkflows/local/assembly_unicycler.nf @@ -2,17 +2,10 @@ // Assembly and downstream processing for Unicycler scaffolds // -params.unicycler_options = [:] -params.bandage_options = [:] -params.blastn_options = [:] -params.blastn_filter_options = [:] -params.abacas_options = [:] -params.plasmidid_options = [:] -params.quast_options = [:] - -include { UNICYCLER } from '../../modules/nf-core/modules/unicycler/main' addParams( options: params.unicycler_options ) -include { BANDAGE_IMAGE } from '../../modules/nf-core/modules/bandage/image/main' addParams( options: params.bandage_options ) -include { ASSEMBLY_QC } from './assembly_qc' addParams( blastn_options: params.blastn_options, blastn_filter_options: params.blastn_filter_options, abacas_options: params.abacas_options, plasmidid_options: params.plasmidid_options, quast_options: params.quast_options ) +include { UNICYCLER } from '../../modules/nf-core/modules/unicycler/main' +include { BANDAGE_IMAGE } from '../../modules/nf-core/modules/bandage/image/main' + +include { ASSEMBLY_QC } from './assembly_qc' workflow ASSEMBLY_UNICYCLER { take: @@ -24,10 +17,15 @@ workflow ASSEMBLY_UNICYCLER { main: + ch_versions = Channel.empty() + // // Assemble reads with Unicycler // - UNICYCLER ( reads ) + UNICYCLER ( + reads + ) + ch_versions = ch_versions.mix(UNICYCLER.out.versions.first()) // // Filter for empty scaffold files @@ -47,14 +45,15 @@ workflow ASSEMBLY_UNICYCLER { // // Generate assembly visualisation with Bandage // - ch_bandage_png = Channel.empty() - ch_bandage_svg = Channel.empty() - ch_bandage_version = Channel.empty() + ch_bandage_png = Channel.empty() + ch_bandage_svg = Channel.empty() if (!params.skip_bandage) { - BANDAGE_IMAGE ( ch_gfa ) - ch_bandage_version = BANDAGE_IMAGE.out.version - ch_bandage_png = BANDAGE_IMAGE.out.png - ch_bandage_svg = BANDAGE_IMAGE.out.svg + BANDAGE_IMAGE ( + ch_gfa + ) + ch_bandage_png = BANDAGE_IMAGE.out.png + ch_bandage_svg = BANDAGE_IMAGE.out.svg + ch_versions = ch_versions.mix(BANDAGE_IMAGE.out.versions.first()) } // @@ -67,27 +66,23 @@ workflow ASSEMBLY_UNICYCLER { blast_db, blast_header ) + ch_versions = ch_versions.mix(ASSEMBLY_QC.out.versions) emit: scaffolds = UNICYCLER.out.scaffolds // channel: [ val(meta), [ scaffolds ] ] gfa = UNICYCLER.out.gfa // channel: [ val(meta), [ gfa ] ] log_out = UNICYCLER.out.log // channel: [ val(meta), [ log ] ] - unicycler_version = UNICYCLER.out.version // path: *.version.txt bandage_png = ch_bandage_png // channel: [ val(meta), [ png ] ] bandage_svg = ch_bandage_svg // channel: [ val(meta), [ svg ] ] - bandage_version = ch_bandage_version // path: *.version.txt blast_txt = ASSEMBLY_QC.out.blast_txt // channel: [ val(meta), [ txt ] ] blast_filter_txt = ASSEMBLY_QC.out.blast_filter_txt // channel: [ val(meta), [ txt ] ] - blast_version = ASSEMBLY_QC.out.blast_version // path: *.version.txt quast_results = ASSEMBLY_QC.out.quast_results // channel: [ val(meta), [ results ] ] quast_tsv = ASSEMBLY_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] - quast_version = ASSEMBLY_QC.out.quast_version // path: *.version.txt abacas_results = ASSEMBLY_QC.out.abacas_results // channel: [ val(meta), [ results ] ] - abacas_version = ASSEMBLY_QC.out.abacas_version // path: *.version.txt plasmidid_html = ASSEMBLY_QC.out.plasmidid_html // channel: [ val(meta), [ html ] ] plasmidid_tab = ASSEMBLY_QC.out.plasmidid_tab // channel: [ val(meta), [ tab ] ] @@ -97,5 +92,6 @@ workflow ASSEMBLY_UNICYCLER { plasmidid_database = ASSEMBLY_QC.out.plasmidid_database // channel: [ val(meta), [ database/ ] ] plasmidid_fasta = ASSEMBLY_QC.out.plasmidid_fasta // channel: [ val(meta), [ fasta_files/ ] ] plasmidid_kmer = ASSEMBLY_QC.out.plasmidid_kmer // channel: [ val(meta), [ kmer/ ] ] - plasmidid_version = ASSEMBLY_QC.out.plasmidid_version // path: *.version.txt + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index aa11074c..4762335f 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -10,16 +10,22 @@ workflow INPUT_CHECK { platform // string: sequencing platform. Accepted values: 'illumina', 'nanopore' main: - SAMPLESHEET_CHECK ( samplesheet, platform ) + + SAMPLESHEET_CHECK ( + samplesheet, + platform + ) if (platform == 'illumina') { SAMPLESHEET_CHECK + .out .csv .splitCsv ( header:true, sep:',' ) .map { create_fastq_channels(it) } .set { sample_info } } else if (platform == 'nanopore') { SAMPLESHEET_CHECK + .out .csv .splitCsv ( header:true, sep:',' ) .map { row -> [ row.barcode, row.sample ] } @@ -27,14 +33,15 @@ workflow INPUT_CHECK { } emit: - sample_info // channel: [ val(meta), [ reads ] ] + sample_info // channel: [ val(meta), [ reads ] ] + versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] } // Function to get list of [ meta, [ fastq_1, fastq_2 ] ] def create_fastq_channels(LinkedHashMap row) { def meta = [:] - meta.id = row.sample - meta.single_end = row.single_end.toBoolean() + meta.id = row.sample + meta.single_end = row.single_end.toBoolean() def array = [] if (!file(row.fastq_1).exists()) { diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index e34e05d8..d4a195bb 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -2,20 +2,12 @@ // Run various tools to generate a masked genome consensus sequence // -params.genomecov_options = [:] -params.merge_options = [:] -params.mask_options = [:] -params.maskfasta_options = [:] -params.bcftools_options = [:] -params.plot_bases_options = [:] - -include { BEDTOOLS_GENOMECOV } from '../../modules/nf-core/modules/bedtools/genomecov/main' addParams( options: params.genomecov_options ) -include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' addParams( options: params.merge_options ) -include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' addParams( options: params.maskfasta_options ) -include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' addParams( options: params.bcftools_options ) -include { MAKE_BED_MASK } from '../../modules/local/make_bed_mask' addParams( options: params.mask_options ) -include { PLOT_BASE_DENSITY } from '../../modules/local/plot_base_density' addParams( options: params.plot_bases_options ) - +include { BEDTOOLS_GENOMECOV } from '../../modules/nf-core/modules/bedtools/genomecov/main' +include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' +include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' +include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' +include { MAKE_BED_MASK } from '../../modules/local/make_bed_mask' +include { PLOT_BASE_DENSITY } from '../../modules/local/plot_base_density' workflow MAKE_CONSENSUS { take: @@ -23,22 +15,47 @@ workflow MAKE_CONSENSUS { fasta main: - BEDTOOLS_GENOMECOV ( bam_vcf.map { meta, bam, vcf, tbi -> [ meta, bam ] }, [], 'bed' ) - - BEDTOOLS_MERGE ( BEDTOOLS_GENOMECOV.out.genomecov ) - - MAKE_BED_MASK ( bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf ] }.join( BEDTOOLS_MERGE.out.bed, by: [0] ), fasta ) - BEDTOOLS_MASKFASTA ( MAKE_BED_MASK.out.bed, MAKE_BED_MASK.out.fasta.map{it[1]} ) - - BCFTOOLS_CONSENSUS ( bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf, tbi ] }.join( BEDTOOLS_MASKFASTA.out.fasta, by: [0] ) ) - - PLOT_BASE_DENSITY ( BCFTOOLS_CONSENSUS.out.fasta ) + ch_versions = Channel.empty() + + BEDTOOLS_GENOMECOV ( + bam_vcf.map { meta, bam, vcf, tbi -> [ meta, bam, 1 ] }, + [], + 'bed', + ) + ch_versions = ch_versions.mix(BEDTOOLS_GENOMECOV.out.versions.first()) + + BEDTOOLS_MERGE ( + BEDTOOLS_GENOMECOV.out.genomecov + ) + ch_versions = ch_versions.mix(BEDTOOLS_MERGE.out.versions.first()) + + MAKE_BED_MASK ( + bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf ] }.join( BEDTOOLS_MERGE.out.bed, by: [0] ), + fasta + ) + ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) + + BEDTOOLS_MASKFASTA ( + MAKE_BED_MASK.out.bed, + MAKE_BED_MASK.out.fasta.map{it[1]} + ) + ch_versions = ch_versions.mix(BEDTOOLS_MASKFASTA.out.versions.first()) + + BCFTOOLS_CONSENSUS ( + bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf, tbi ] }.join( BEDTOOLS_MASKFASTA.out.fasta, by: [0] ) + ) + ch_versions = ch_versions.mix(BCFTOOLS_CONSENSUS.out.versions.first()) + + PLOT_BASE_DENSITY ( + BCFTOOLS_CONSENSUS.out.fasta + ) + ch_versions = ch_versions.mix(PLOT_BASE_DENSITY.out.versions.first()) emit: - fasta = BCFTOOLS_CONSENSUS.out.fasta // channel: [ val(meta), [ fasta ] ] - tsv = PLOT_BASE_DENSITY.out.tsv // channel: [ val(meta), [ tsv ] ] - pdf = PLOT_BASE_DENSITY.out.pdf // channel: [ val(meta), [ pdf ] ] - bedtools_version = BEDTOOLS_MERGE.out.version // path: *.version.txt - bcftools_version = BCFTOOLS_CONSENSUS.out.version // path: *.version.txt + fasta = BCFTOOLS_CONSENSUS.out.fasta // channel: [ val(meta), [ fasta ] ] + tsv = PLOT_BASE_DENSITY.out.tsv // channel: [ val(meta), [ tsv ] ] + pdf = PLOT_BASE_DENSITY.out.pdf // channel: [ val(meta), [ pdf ] ] + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/prepare_genome_illumina.nf b/subworkflows/local/prepare_genome_illumina.nf index 5912aec9..7febda1d 100644 --- a/subworkflows/local/prepare_genome_illumina.nf +++ b/subworkflows/local/prepare_genome_illumina.nf @@ -2,31 +2,20 @@ // Uncompress and prepare reference genome files // -params.genome_options = [:] -params.index_options = [:] -params.db_options = [:] -params.bowtie2_build_options = [:] -params.collapse_primers_options = [:] -params.bedtools_getfasta_options = [:] -params.snpeff_build_options = [:] -params.makeblastdb_options = [:] -params.kraken2_build_options = [:] - -include { - GUNZIP as GUNZIP_FASTA - GUNZIP as GUNZIP_GFF - GUNZIP as GUNZIP_PRIMER_BED - GUNZIP as GUNZIP_PRIMER_FASTA } from '../../modules/nf-core/modules/gunzip/main' addParams( options: params.genome_options ) -include { UNTAR as UNTAR_BOWTIE2_INDEX } from '../../modules/nf-core/modules/untar/main' addParams( options: params.index_options ) -include { UNTAR as UNTAR_KRAKEN2_DB } from '../../modules/nf-core/modules/untar/main' addParams( options: params.db_options ) -include { UNTAR as UNTAR_BLAST_DB } from '../../modules/nf-core/modules/untar/main' addParams( options: params.db_options ) -include { BOWTIE2_BUILD } from '../../modules/nf-core/modules/bowtie2/build/main' addParams( options: params.bowtie2_build_options ) -include { BLAST_MAKEBLASTDB } from '../../modules/nf-core/modules/blast/makeblastdb/main' addParams( options: params.makeblastdb_options ) -include { BEDTOOLS_GETFASTA } from '../../modules/nf-core/modules/bedtools/getfasta/main' addParams( options: params.bedtools_getfasta_options ) -include { GET_CHROM_SIZES } from '../../modules/local/get_chrom_sizes' addParams( options: params.genome_options ) -include { COLLAPSE_PRIMERS } from '../../modules/local/collapse_primers' addParams( options: params.collapse_primers_options ) -include { KRAKEN2_BUILD } from '../../modules/local/kraken2_build' addParams( options: params.kraken2_build_options ) -include { SNPEFF_BUILD } from '../../modules/local/snpeff_build' addParams( options: params.snpeff_build_options ) +include { GUNZIP as GUNZIP_FASTA } from '../../modules/nf-core/modules/gunzip/main' +include { GUNZIP as GUNZIP_GFF } from '../../modules/nf-core/modules/gunzip/main' +include { GUNZIP as GUNZIP_PRIMER_BED } from '../../modules/nf-core/modules/gunzip/main' +include { GUNZIP as GUNZIP_PRIMER_FASTA } from '../../modules/nf-core/modules/gunzip/main' +include { UNTAR as UNTAR_BOWTIE2_INDEX } from '../../modules/nf-core/modules/untar/main' +include { UNTAR as UNTAR_KRAKEN2_DB } from '../../modules/nf-core/modules/untar/main' +include { UNTAR as UNTAR_BLAST_DB } from '../../modules/nf-core/modules/untar/main' +include { BOWTIE2_BUILD } from '../../modules/nf-core/modules/bowtie2/build/main' +include { BLAST_MAKEBLASTDB } from '../../modules/nf-core/modules/blast/makeblastdb/main' +include { BEDTOOLS_GETFASTA } from '../../modules/nf-core/modules/bedtools/getfasta/main' +include { CUSTOM_GETCHROMSIZES } from '../../modules/nf-core/modules/custom/getchromsizes/main' +include { COLLAPSE_PRIMERS } from '../../modules/local/collapse_primers' +include { KRAKEN2_BUILD } from '../../modules/local/kraken2_build' +include { SNPEFF_BUILD } from '../../modules/local/snpeff_build' workflow PREPARE_GENOME { take: @@ -34,11 +23,17 @@ workflow PREPARE_GENOME { main: + ch_versions = Channel.empty() + // // Uncompress genome fasta file if required // if (params.fasta.endsWith('.gz')) { - ch_fasta = GUNZIP_FASTA ( params.fasta ).gunzip + GUNZIP_FASTA ( + [ [:], params.fasta ] + ) + ch_fasta = GUNZIP_FASTA.out.gunzip.map { it[1] } + ch_versions = ch_versions.mix(GUNZIP_FASTA.out.versions) } else { ch_fasta = file(params.fasta) } @@ -48,7 +43,11 @@ workflow PREPARE_GENOME { // if (params.gff) { if (params.gff.endsWith('.gz')) { - ch_gff = GUNZIP_GFF ( params.gff ).gunzip + GUNZIP_GFF ( + [ [:], params.gff ] + ) + ch_gff = GUNZIP_GFF.out.gunzip.map { it[1] } + ch_versions = ch_versions.mix(GUNZIP_GFF.out.versions) } else { ch_gff = file(params.gff) } @@ -61,7 +60,11 @@ workflow PREPARE_GENOME { // ch_chrom_sizes = Channel.empty() if (!params.skip_asciigenome) { - ch_chrom_sizes = GET_CHROM_SIZES ( ch_fasta ).sizes + CUSTOM_GETCHROMSIZES ( + ch_fasta + ) + ch_chrom_sizes = CUSTOM_GETCHROMSIZES.out.sizes + ch_versions = ch_versions.mix(CUSTOM_GETCHROMSIZES.out.versions) } // @@ -71,12 +74,20 @@ workflow PREPARE_GENOME { if (!params.skip_kraken2) { if (params.kraken2_db) { if (params.kraken2_db.endsWith('.tar.gz')) { - ch_kraken2_db = UNTAR_KRAKEN2_DB ( params.kraken2_db ).untar + UNTAR_KRAKEN2_DB ( + params.kraken2_db + ) + ch_kraken2_db = UNTAR_KRAKEN2_DB.out.untar + ch_versions = ch_versions.mix(UNTAR_KRAKEN2_DB.out.versions) } else { ch_kraken2_db = file(params.kraken2_db) } } else { - ch_kraken2_db = KRAKEN2_BUILD ( params.kraken2_db_name ).db + KRAKEN2_BUILD ( + params.kraken2_db_name + ) + ch_kraken2_db = KRAKEN2_BUILD.out.db + ch_versions = ch_versions.mix(KRAKEN2_BUILD.out.versions) } } @@ -89,25 +100,44 @@ workflow PREPARE_GENOME { if (params.protocol == 'amplicon') { if (params.primer_bed) { if (params.primer_bed.endsWith('.gz')) { - ch_primer_bed = GUNZIP_PRIMER_BED ( params.primer_bed ).gunzip + GUNZIP_PRIMER_BED ( + [ [:], params.primer_bed ] + ) + ch_primer_bed = GUNZIP_PRIMER_BED.out.gunzip.map { it[1] } + ch_versions = ch_versions.mix(GUNZIP_PRIMER_BED.out.versions) } else { ch_primer_bed = file(params.primer_bed) } } if (!params.skip_variants && !params.skip_mosdepth) { - ch_primer_collapsed_bed = COLLAPSE_PRIMERS ( ch_primer_bed, params.primer_left_suffix, params.primer_right_suffix ) + COLLAPSE_PRIMERS ( + ch_primer_bed, + params.primer_left_suffix, + params.primer_right_suffix + ) + ch_primer_collapsed_bed = COLLAPSE_PRIMERS.out.bed + ch_versions = ch_versions.mix(COLLAPSE_PRIMERS.out.versions) } if (!params.skip_assembly && !params.skip_cutadapt) { if (params.primer_fasta) { if (params.primer_fasta.endsWith('.gz')) { - ch_primer_fasta = GUNZIP_PRIMER_FASTA ( params.primer_fasta ).gunzip + GUNZIP_PRIMER_FASTA ( + [ [:], params.primer_fasta ] + ) + ch_primer_fasta = GUNZIP_PRIMER_FASTA.out.gunzip.map { it[1] } + ch_versions = ch_versions.mix(GUNZIP_PRIMER_FASTA.out.versions) } else { ch_primer_fasta = file(params.primer_fasta) } } else { - ch_primer_fasta = BEDTOOLS_GETFASTA ( ch_primer_bed, ch_fasta ).fasta + BEDTOOLS_GETFASTA ( + ch_primer_bed, + ch_fasta + ) + ch_primer_fasta = BEDTOOLS_GETFASTA.out.fasta + ch_versions = ch_versions.mix(BEDTOOLS_GETFASTA.out.versions) } } } @@ -119,12 +149,20 @@ workflow PREPARE_GENOME { if (!params.skip_variants) { if (params.bowtie2_index) { if (params.bowtie2_index.endsWith('.tar.gz')) { - ch_bowtie2_index = UNTAR_BOWTIE2_INDEX ( params.bowtie2_index ).untar + UNTAR_BOWTIE2_INDEX ( + params.bowtie2_index + ) + ch_bowtie2_index = UNTAR_BOWTIE2_INDEX.out.untar + ch_versions = ch_versions.mix(UNTAR_BOWTIE2_INDEX.out.versions) } else { ch_bowtie2_index = file(params.bowtie2_index) } } else { - ch_bowtie2_index = BOWTIE2_BUILD ( ch_fasta ).index + BOWTIE2_BUILD ( + ch_fasta + ) + ch_bowtie2_index = BOWTIE2_BUILD.out.index + ch_versions = ch_versions.mix(BOWTIE2_BUILD.out.versions) } } @@ -136,12 +174,20 @@ workflow PREPARE_GENOME { if (!params.skip_blast) { if (params.blast_db) { if (params.blast_db.endsWith('.tar.gz')) { - ch_blast_db = UNTAR_BLAST_DB ( params.blast_db ).untar + UNTAR_BLAST_DB ( + params.blast_db + ) + ch_blast_db = UNTAR_BLAST_DB.out.untar + ch_versions = ch_versions.mix(UNTAR_BLAST_DB.out.versions) } else { ch_blast_db = file(params.blast_db) } } else { - ch_blast_db = BLAST_MAKEBLASTDB ( ch_fasta ).db + BLAST_MAKEBLASTDB ( + ch_fasta + ) + ch_blast_db = BLAST_MAKEBLASTDB.out.db + ch_versions = ch_versions.mix(BLAST_MAKEBLASTDB.out.versions) } } } @@ -152,21 +198,27 @@ workflow PREPARE_GENOME { ch_snpeff_db = Channel.empty() ch_snpeff_config = Channel.empty() if (!params.skip_variants && params.gff && !params.skip_snpeff) { - SNPEFF_BUILD ( ch_fasta, ch_gff ) + SNPEFF_BUILD ( + ch_fasta, + ch_gff + ) ch_snpeff_db = SNPEFF_BUILD.out.db ch_snpeff_config = SNPEFF_BUILD.out.config + ch_versions = ch_versions.mix(SNPEFF_BUILD.out.versions) } emit: - fasta = ch_fasta // path: genome.fasta - gff = ch_gff // path: genome.gff - chrom_sizes = ch_chrom_sizes // path: genome.sizes - bowtie2_index = ch_bowtie2_index // path: bowtie2/index/ - primer_bed = ch_primer_bed // path: primer.bed - primer_collapsed_bed = ch_primer_collapsed_bed // path: primer.collapsed.bed - primer_fasta = ch_primer_fasta // path: primer.fasta - blast_db = ch_blast_db // path: blast_db/ - kraken2_db = ch_kraken2_db // path: kraken2_db/ - snpeff_db = ch_snpeff_db // path: snpeff_db - snpeff_config = ch_snpeff_config // path: snpeff.config + fasta = ch_fasta // path: genome.fasta + gff = ch_gff // path: genome.gff + chrom_sizes = ch_chrom_sizes // path: genome.sizes + bowtie2_index = ch_bowtie2_index // path: bowtie2/index/ + primer_bed = ch_primer_bed // path: primer.bed + primer_collapsed_bed = ch_primer_collapsed_bed // path: primer.collapsed.bed + primer_fasta = ch_primer_fasta // path: primer.fasta + blast_db = ch_blast_db // path: blast_db/ + kraken2_db = ch_kraken2_db // path: kraken2_db/ + snpeff_db = ch_snpeff_db // path: snpeff_db + snpeff_config = ch_snpeff_config // path: snpeff.config + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/prepare_genome_nanopore.nf b/subworkflows/local/prepare_genome_nanopore.nf index a23ca4b8..f5520c5e 100644 --- a/subworkflows/local/prepare_genome_nanopore.nf +++ b/subworkflows/local/prepare_genome_nanopore.nf @@ -2,17 +2,12 @@ // Uncompress and prepare reference genome files // -params.genome_options = [:] -params.collapse_primers_options = [:] -params.snpeff_build_options = [:] - -include { - GUNZIP as GUNZIP_FASTA - GUNZIP as GUNZIP_GFF - GUNZIP as GUNZIP_PRIMER_BED } from '../../modules/nf-core/modules/gunzip/main' addParams( options: params.genome_options ) -include { GET_CHROM_SIZES } from '../../modules/local/get_chrom_sizes' addParams( options: params.genome_options ) -include { COLLAPSE_PRIMERS } from '../../modules/local/collapse_primers' addParams( options: params.collapse_primers_options ) -include { SNPEFF_BUILD } from '../../modules/local/snpeff_build' addParams( options: params.snpeff_build_options ) +include { GUNZIP as GUNZIP_FASTA } from '../../modules/nf-core/modules/gunzip/main' +include { GUNZIP as GUNZIP_GFF } from '../../modules/nf-core/modules/gunzip/main' +include { GUNZIP as GUNZIP_PRIMER_BED } from '../../modules/nf-core/modules/gunzip/main' +include { CUSTOM_GETCHROMSIZES } from '../../modules/nf-core/modules/custom/getchromsizes/main' +include { COLLAPSE_PRIMERS } from '../../modules/local/collapse_primers' +include { SNPEFF_BUILD } from '../../modules/local/snpeff_build' workflow PREPARE_GENOME { take: @@ -20,11 +15,17 @@ workflow PREPARE_GENOME { main: + ch_versions = Channel.empty() + // // Uncompress genome fasta file if required // if (params.fasta.endsWith('.gz')) { - ch_fasta = GUNZIP_FASTA ( params.fasta ).gunzip + GUNZIP_FASTA ( + [ [:], params.fasta ] + ) + ch_fasta = GUNZIP_FASTA.out.gunzip.map { it[1] } + ch_versions = ch_versions.mix(GUNZIP_FASTA.out.versions) } else { ch_fasta = file(params.fasta) } @@ -34,7 +35,11 @@ workflow PREPARE_GENOME { // if (params.gff) { if (params.gff.endsWith('.gz')) { - ch_gff = GUNZIP_GFF ( params.gff ).gunzip + GUNZIP_GFF ( + [ [:], params.gff ] + ) + ch_gff = GUNZIP_GFF.out.gunzip.map { it[1] } + ch_versions = ch_versions.mix(GUNZIP_GFF.out.versions) } else { ch_gff = file(params.gff) } @@ -47,7 +52,11 @@ workflow PREPARE_GENOME { // ch_chrom_sizes = Channel.empty() if (!params.skip_asciigenome) { - ch_chrom_sizes = GET_CHROM_SIZES ( ch_fasta ).sizes + CUSTOM_GETCHROMSIZES ( + ch_fasta + ) + ch_chrom_sizes = CUSTOM_GETCHROMSIZES.out.sizes + ch_versions = ch_versions.mix(CUSTOM_GETCHROMSIZES.out.versions) } // @@ -56,7 +65,11 @@ workflow PREPARE_GENOME { ch_primer_bed = Channel.empty() if (params.primer_bed) { if (params.primer_bed.endsWith('.gz')) { - ch_primer_bed = GUNZIP_PRIMER_BED ( params.primer_bed ).gunzip + GUNZIP_PRIMER_BED ( + [ [:], params.primer_bed ] + ) + ch_primer_bed = GUNZIP_PRIMER_BED.out.gunzip.map { it[1] } + ch_versions = ch_versions.mix(GUNZIP_PRIMER_BED.out.versions) } else { ch_primer_bed = file(params.primer_bed) } @@ -67,7 +80,13 @@ workflow PREPARE_GENOME { // ch_primer_collapsed_bed = Channel.empty() if (!params.skip_mosdepth) { - ch_primer_collapsed_bed = COLLAPSE_PRIMERS ( ch_primer_bed, params.primer_left_suffix, params.primer_right_suffix ) + COLLAPSE_PRIMERS ( + ch_primer_bed, + params.primer_left_suffix, + params.primer_right_suffix + ) + ch_primer_collapsed_bed = COLLAPSE_PRIMERS.out.bed + ch_versions = ch_versions.mix(COLLAPSE_PRIMERS.out.versions) } // @@ -76,9 +95,13 @@ workflow PREPARE_GENOME { ch_snpeff_db = Channel.empty() ch_snpeff_config = Channel.empty() if (params.gff && !params.skip_snpeff) { - SNPEFF_BUILD ( ch_fasta, ch_gff ) + SNPEFF_BUILD ( + ch_fasta, + ch_gff + ) ch_snpeff_db = SNPEFF_BUILD.out.db ch_snpeff_config = SNPEFF_BUILD.out.config + ch_versions = ch_versions.mix(SNPEFF_BUILD.out.versions) } emit: @@ -89,4 +112,6 @@ workflow PREPARE_GENOME { primer_collapsed_bed = ch_primer_collapsed_bed // path: primer.collapsed.bed snpeff_db = ch_snpeff_db // path: snpeff_db snpeff_config = ch_snpeff_config // path: snpeff.config + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/primer_trim_ivar.nf b/subworkflows/local/primer_trim_ivar.nf deleted file mode 100644 index 4037b5b5..00000000 --- a/subworkflows/local/primer_trim_ivar.nf +++ /dev/null @@ -1,40 +0,0 @@ -// -// iVar trim, sort, index BAM file and run samtools stats, flagstat and idxstats -// - -params.ivar_trim_options = [:] -params.samtools_options = [:] - -include { IVAR_TRIM } from '../../modules/nf-core/modules/ivar/trim/main' addParams( options: params.ivar_trim_options ) -include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' addParams( options: params.samtools_options ) -include { BAM_SORT_SAMTOOLS } from '../nf-core/bam_sort_samtools' addParams( options: params.samtools_options ) - -workflow PRIMER_TRIM_IVAR { - take: - bam // channel: [ val(meta), [ bam ], [bai] ] - bed // path : bed - - main: - - // - // iVar trim primers - // - IVAR_TRIM ( bam, bed ) - - // - // Sort, index BAM file and run samtools stats, flagstat and idxstats - // - BAM_SORT_SAMTOOLS ( IVAR_TRIM.out.bam ) - - emit: - bam_orig = IVAR_TRIM.out.bam // channel: [ val(meta), bam ] - log_out = IVAR_TRIM.out.log // channel: [ val(meta), log ] - ivar_version = IVAR_TRIM.out.version // path: *.version.txt - - bam = BAM_SORT_SAMTOOLS.out.bam // channel: [ val(meta), [ bam ] ] - bai = BAM_SORT_SAMTOOLS.out.bai // channel: [ val(meta), [ bai ] ] - stats = BAM_SORT_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] - flagstat = BAM_SORT_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] - idxstats = BAM_SORT_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] - samtools_version = BAM_SORT_SAMTOOLS.out.version // path: *.version.txt -} diff --git a/subworkflows/local/snpeff_snpsift.nf b/subworkflows/local/snpeff_snpsift.nf index cee772e3..ad84feb3 100644 --- a/subworkflows/local/snpeff_snpsift.nf +++ b/subworkflows/local/snpeff_snpsift.nf @@ -2,15 +2,10 @@ // Run snpEff, bgzip, tabix, stats and SnpSift commands // -params.snpeff_options = [:] -params.bgzip_options = [:] -params.tabix_options = [:] -params.stats_options = [:] -params.snpsift_options = [:] +include { SNPEFF_ANN } from '../../modules/local/snpeff_ann' +include { SNPSIFT_EXTRACTFIELDS } from '../../modules/local/snpsift_extractfields' -include { SNPEFF_ANN } from '../../modules/local/snpeff_ann' addParams( options: params.snpeff_options ) -include { SNPSIFT_EXTRACTFIELDS } from '../../modules/local/snpsift_extractfields' addParams( options: params.snpsift_options ) -include { VCF_BGZIP_TABIX_STATS } from '../nf-core/vcf_bgzip_tabix_stats' addParams( bgzip_options: params.bgzip_options, tabix_options: params.tabix_options, stats_options: params.stats_options ) +include { VCF_BGZIP_TABIX_STATS } from '../nf-core/vcf_bgzip_tabix_stats' workflow SNPEFF_SNPSIFT { take: @@ -21,24 +16,36 @@ workflow SNPEFF_SNPSIFT { main: - SNPEFF_ANN ( vcf, db, config, fasta ) + ch_versions = Channel.empty() - VCF_BGZIP_TABIX_STATS ( SNPEFF_ANN.out.vcf ) + SNPEFF_ANN ( + vcf, + db, + config, + fasta + ) + ch_versions = ch_versions.mix(SNPEFF_ANN.out.versions.first()) - SNPSIFT_EXTRACTFIELDS ( VCF_BGZIP_TABIX_STATS.out.vcf ) + VCF_BGZIP_TABIX_STATS ( + SNPEFF_ANN.out.vcf + ) + ch_versions = ch_versions.mix(VCF_BGZIP_TABIX_STATS.out.versions) + + SNPSIFT_EXTRACTFIELDS ( + VCF_BGZIP_TABIX_STATS.out.vcf + ) + ch_versions = ch_versions.mix(SNPSIFT_EXTRACTFIELDS.out.versions.first()) emit: - csv = SNPEFF_ANN.out.csv // channel: [ val(meta), [ csv ] ] - txt = SNPEFF_ANN.out.txt // channel: [ val(meta), [ txt ] ] - html = SNPEFF_ANN.out.html // channel: [ val(meta), [ html ] ] - snpeff_version = SNPEFF_ANN.out.version // path: *.version.txt - - vcf = VCF_BGZIP_TABIX_STATS.out.vcf // channel: [ val(meta), [ vcf.gz ] ] - tbi = VCF_BGZIP_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] - stats = VCF_BGZIP_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] - tabix_version = VCF_BGZIP_TABIX_STATS.out.tabix_version // path: *.version.txt - bcftools_version = VCF_BGZIP_TABIX_STATS.out.bcftools_version // path: *.version.txt - - snpsift_txt = SNPSIFT_EXTRACTFIELDS.out.txt // channel: [ val(meta), [ txt ] ] - snpsift_version = SNPSIFT_EXTRACTFIELDS.out.version // path: *.version.txt + csv = SNPEFF_ANN.out.csv // channel: [ val(meta), [ csv ] ] + txt = SNPEFF_ANN.out.txt // channel: [ val(meta), [ txt ] ] + html = SNPEFF_ANN.out.html // channel: [ val(meta), [ html ] ] + + vcf = VCF_BGZIP_TABIX_STATS.out.vcf // channel: [ val(meta), [ vcf.gz ] ] + tbi = VCF_BGZIP_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = VCF_BGZIP_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] + + snpsift_txt = SNPSIFT_EXTRACTFIELDS.out.txt // channel: [ val(meta), [ txt ] ] + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index 626659a9..08e29e2f 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -2,30 +2,14 @@ // Variant calling and downstream processing for BCFTools // -params.bcftools_mpileup_options = [:] -params.quast_options = [:] -params.consensus_genomecov_options = [:] -params.consensus_merge_options = [:] -params.consensus_mask_options = [:] -params.consensus_maskfasta_options = [:] -params.consensus_bcftools_options = [:] -params.consensus_plot_options = [:] -params.snpeff_options = [:] -params.snpsift_options = [:] -params.snpeff_bgzip_options = [:] -params.snpeff_tabix_options = [:] -params.snpeff_stats_options = [:] -params.pangolin_options = [:] -params.nextclade_options = [:] -params.asciigenome_options = [:] - -include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' addParams( options: params.bcftools_mpileup_options ) -include { QUAST } from '../../modules/nf-core/modules/quast/main' addParams( options: params.quast_options ) -include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' addParams( options: params.pangolin_options ) -include { NEXTCLADE } from '../../modules/nf-core/modules/nextclade/main' addParams( options: params.nextclade_options ) -include { ASCIIGENOME } from '../../modules/local/asciigenome' addParams( options: params.asciigenome_options ) -include { MAKE_CONSENSUS } from './make_consensus' addParams( genomecov_options: params.consensus_genomecov_options, merge_options: params.consensus_merge_options, mask_options: params.consensus_mask_options, maskfasta_options: params.consensus_maskfasta_options, bcftools_options: params.consensus_bcftools_options, plot_bases_options: params.consensus_plot_options ) -include { SNPEFF_SNPSIFT } from './snpeff_snpsift' addParams( snpeff_options: params.snpeff_options, snpsift_options: params.snpsift_options, bgzip_options: params.snpeff_bgzip_options, tabix_options: params.snpeff_tabix_options, stats_options: params.snpeff_stats_options ) +include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' +include { QUAST } from '../../modules/nf-core/modules/quast/main' +include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' +include { NEXTCLADE } from '../../modules/nf-core/modules/nextclade/main' +include { ASCIIGENOME } from '../../modules/local/asciigenome' + +include { MAKE_CONSENSUS } from './make_consensus' +include { SNPEFF_SNPSIFT } from './snpeff_snpsift' workflow VARIANTS_BCFTOOLS { take: @@ -39,82 +23,98 @@ workflow VARIANTS_BCFTOOLS { main: + ch_versions = Channel.empty() + // // Call variants // - BCFTOOLS_MPILEUP ( bam, fasta ) + BCFTOOLS_MPILEUP ( + bam, + fasta + ) + ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) // // Create genome consensus using variants in VCF, run QUAST and pangolin // - ch_consensus = Channel.empty() - ch_bases_tsv = Channel.empty() - ch_bases_pdf = Channel.empty() - ch_bedtools_version = Channel.empty() - ch_quast_results = Channel.empty() - ch_quast_tsv = Channel.empty() - ch_quast_version = Channel.empty() - ch_pangolin_report = Channel.empty() - ch_pangolin_version = Channel.empty() - ch_nextclade_report = Channel.empty() - ch_nextclade_version = Channel.empty() + ch_consensus = Channel.empty() + ch_bases_tsv = Channel.empty() + ch_bases_pdf = Channel.empty() + ch_quast_results = Channel.empty() + ch_quast_tsv = Channel.empty() + ch_pangolin_report = Channel.empty() + ch_nextclade_report = Channel.empty() if (!params.skip_consensus) { - MAKE_CONSENSUS ( bam.join(BCFTOOLS_MPILEUP.out.vcf, by: [0]).join(BCFTOOLS_MPILEUP.out.tbi, by: [0]), fasta ) - ch_consensus = MAKE_CONSENSUS.out.fasta - ch_bases_tsv = MAKE_CONSENSUS.out.tsv - ch_bases_pdf = MAKE_CONSENSUS.out.pdf - ch_bedtools_version = MAKE_CONSENSUS.out.bedtools_version + MAKE_CONSENSUS ( + bam.join(BCFTOOLS_MPILEUP.out.vcf, by: [0]).join(BCFTOOLS_MPILEUP.out.tbi, by: [0]), + fasta + ) + ch_consensus = MAKE_CONSENSUS.out.fasta + ch_bases_tsv = MAKE_CONSENSUS.out.tsv + ch_bases_pdf = MAKE_CONSENSUS.out.pdf + ch_versions = ch_versions.mix(MAKE_CONSENSUS.out.versions) if (!params.skip_variants_quast) { - QUAST ( ch_consensus.collect{ it[1] }, fasta, gff, true, params.gff ) + QUAST ( + ch_consensus.collect{ it[1] }, + fasta, + gff, + true, + params.gff + ) ch_quast_results = QUAST.out.results ch_quast_tsv = QUAST.out.tsv - ch_quast_version = QUAST.out.version + ch_versions = ch_versions.mix(QUAST.out.versions) } if (!params.skip_pangolin) { - PANGOLIN ( ch_consensus ) - ch_pangolin_report = PANGOLIN.out.report - ch_pangolin_version = PANGOLIN.out.version + PANGOLIN ( + ch_consensus + ) + ch_pangolin_report = PANGOLIN.out.report + ch_versions = ch_versions.mix(PANGOLIN.out.versions.first()) } if (!params.skip_nextclade) { - NEXTCLADE ( ch_consensus ) - ch_nextclade_report = NEXTCLADE.out.csv - ch_nextclade_version = NEXTCLADE.out.version + NEXTCLADE ( + ch_consensus + ) + ch_nextclade_report = NEXTCLADE.out.csv + ch_versions = ch_versions.mix(NEXTCLADE.out.versions.first()) } } // // Annotate variants // - ch_snpeff_vcf = Channel.empty() - ch_snpeff_tbi = Channel.empty() - ch_snpeff_stats = Channel.empty() - ch_snpeff_csv = Channel.empty() - ch_snpeff_txt = Channel.empty() - ch_snpeff_html = Channel.empty() - ch_snpsift_txt = Channel.empty() - ch_snpeff_version = Channel.empty() - ch_snpsift_version = Channel.empty() + ch_snpeff_vcf = Channel.empty() + ch_snpeff_tbi = Channel.empty() + ch_snpeff_stats = Channel.empty() + ch_snpeff_csv = Channel.empty() + ch_snpeff_txt = Channel.empty() + ch_snpeff_html = Channel.empty() + ch_snpsift_txt = Channel.empty() if (params.gff && !params.skip_snpeff) { - SNPEFF_SNPSIFT ( BCFTOOLS_MPILEUP.out.vcf, snpeff_db, snpeff_config, fasta ) - ch_snpeff_vcf = SNPEFF_SNPSIFT.out.vcf - ch_snpeff_tbi = SNPEFF_SNPSIFT.out.tbi - ch_snpeff_stats = SNPEFF_SNPSIFT.out.stats - ch_snpeff_csv = SNPEFF_SNPSIFT.out.csv - ch_snpeff_txt = SNPEFF_SNPSIFT.out.txt - ch_snpeff_html = SNPEFF_SNPSIFT.out.html - ch_snpsift_txt = SNPEFF_SNPSIFT.out.snpsift_txt - ch_snpeff_version = SNPEFF_SNPSIFT.out.snpeff_version - ch_snpsift_version = SNPEFF_SNPSIFT.out.snpsift_version + SNPEFF_SNPSIFT ( + BCFTOOLS_MPILEUP.out.vcf, + snpeff_db, + snpeff_config, + fasta + ) + ch_snpeff_vcf = SNPEFF_SNPSIFT.out.vcf + ch_snpeff_tbi = SNPEFF_SNPSIFT.out.tbi + ch_snpeff_stats = SNPEFF_SNPSIFT.out.stats + ch_snpeff_csv = SNPEFF_SNPSIFT.out.csv + ch_snpeff_txt = SNPEFF_SNPSIFT.out.txt + ch_snpeff_html = SNPEFF_SNPSIFT.out.html + ch_snpsift_txt = SNPEFF_SNPSIFT.out.snpsift_txt + ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) } // // Variant screenshots with ASCIIGenome // - ch_asciigenome_pdf = Channel.empty() - ch_asciigenome_version = Channel.empty() + ch_asciigenome_pdf = Channel.empty() if (!params.skip_asciigenome) { bam .join(BCFTOOLS_MPILEUP.out.vcf, by: [0]) @@ -135,41 +135,35 @@ workflow VARIANTS_BCFTOOLS { params.asciigenome_window_size, params.asciigenome_read_depth ) - ch_asciigenome_pdf = ASCIIGENOME.out.pdf - ch_asciigenome_version = ASCIIGENOME.out.version + ch_asciigenome_pdf = ASCIIGENOME.out.pdf + ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first()) } emit: - vcf = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] - stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] - bcftools_version = BCFTOOLS_MPILEUP.out.version // path: *.version.txt - - consensus = ch_consensus // channel: [ val(meta), [ fasta ] ] - bases_tsv = ch_bases_tsv // channel: [ val(meta), [ tsv ] ] - bases_pdf = ch_bases_pdf // channel: [ val(meta), [ pdf ] ] - bedtools_version = ch_bedtools_version // path: *.version.txt - - quast_results = ch_quast_results // channel: [ val(meta), [ results ] ] - quast_tsv = ch_quast_tsv // channel: [ val(meta), [ tsv ] ] - quast_version = ch_quast_version // path: *.version.txt - - snpeff_vcf = ch_snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] - snpeff_tbi = ch_snpeff_tbi // channel: [ val(meta), [ tbi ] ] - snpeff_stats = ch_snpeff_stats // channel: [ val(meta), [ txt ] ] - snpeff_csv = ch_snpeff_csv // channel: [ val(meta), [ csv ] ] - snpeff_txt = ch_snpeff_txt // channel: [ val(meta), [ txt ] ] - snpeff_html = ch_snpeff_html // channel: [ val(meta), [ html ] ] - snpsift_txt = ch_snpsift_txt // channel: [ val(meta), [ txt ] ] - snpeff_version = ch_snpeff_version // path: *.version.txt - snpsift_version = ch_snpsift_version // path: *.version.txt - - pangolin_report = ch_pangolin_report // channel: [ val(meta), [ csv ] ] - pangolin_version = ch_pangolin_version // path: *.version.txt - - nextclade_report = ch_nextclade_report // channel: [ val(meta), [ csv ] ] - nextclade_version = ch_nextclade_version // path: *.version.txt - - asciigenome_pdf = ch_asciigenome_pdf // channel: [ val(meta), [ pdf ] ] - asciigenome_version = ch_asciigenome_version // path: *.version.txt + vcf = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] + + consensus = ch_consensus // channel: [ val(meta), [ fasta ] ] + bases_tsv = ch_bases_tsv // channel: [ val(meta), [ tsv ] ] + bases_pdf = ch_bases_pdf // channel: [ val(meta), [ pdf ] ] + + quast_results = ch_quast_results // channel: [ val(meta), [ results ] ] + quast_tsv = ch_quast_tsv // channel: [ val(meta), [ tsv ] ] + + snpeff_vcf = ch_snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] + snpeff_tbi = ch_snpeff_tbi // channel: [ val(meta), [ tbi ] ] + snpeff_stats = ch_snpeff_stats // channel: [ val(meta), [ txt ] ] + snpeff_csv = ch_snpeff_csv // channel: [ val(meta), [ csv ] ] + snpeff_txt = ch_snpeff_txt // channel: [ val(meta), [ txt ] ] + snpeff_html = ch_snpeff_html // channel: [ val(meta), [ html ] ] + snpsift_txt = ch_snpsift_txt // channel: [ val(meta), [ txt ] ] + + pangolin_report = ch_pangolin_report // channel: [ val(meta), [ csv ] ] + + nextclade_report = ch_nextclade_report // channel: [ val(meta), [ csv ] ] + + asciigenome_pdf = ch_asciigenome_pdf // channel: [ val(meta), [ pdf ] ] + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index 2d2748ef..18949e99 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -2,33 +2,17 @@ // Variant calling and downstream processing for IVar // -params.ivar_variants_options = [:] -params.ivar_variants_to_vcf_options = [:] -params.tabix_bgzip_options = [:] -params.tabix_tabix_options = [:] -params.bcftools_stats_options = [:] -params.ivar_consensus_options = [:] -params.consensus_plot_options = [:] -params.quast_options = [:] -params.snpeff_options = [:] -params.snpsift_options = [:] -params.snpeff_bgzip_options = [:] -params.snpeff_tabix_options = [:] -params.snpeff_stats_options = [:] -params.pangolin_options = [:] -params.nextclade_options = [:] -params.asciigenome_options = [:] - -include { IVAR_VARIANTS_TO_VCF } from '../../modules/local/ivar_variants_to_vcf' addParams( options: params.ivar_variants_to_vcf_options ) -include { PLOT_BASE_DENSITY } from '../../modules/local/plot_base_density' addParams( options: params.consensus_plot_options ) -include { IVAR_VARIANTS } from '../../modules/nf-core/modules/ivar/variants/main' addParams( options: params.ivar_variants_options ) -include { IVAR_CONSENSUS } from '../../modules/nf-core/modules/ivar/consensus/main' addParams( options: params.ivar_consensus_options ) -include { QUAST } from '../../modules/nf-core/modules/quast/main' addParams( options: params.quast_options ) -include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' addParams( options: params.pangolin_options ) -include { NEXTCLADE } from '../../modules/nf-core/modules/nextclade/main' addParams( options: params.nextclade_options ) -include { ASCIIGENOME } from '../../modules/local/asciigenome' addParams( options: params.asciigenome_options ) -include { VCF_BGZIP_TABIX_STATS } from '../nf-core/vcf_bgzip_tabix_stats' addParams( bgzip_options: params.tabix_bgzip_options, tabix_options: params.tabix_tabix_options, stats_options: params.bcftools_stats_options ) -include { SNPEFF_SNPSIFT } from './snpeff_snpsift' addParams( snpeff_options: params.snpeff_options, snpsift_options: params.snpsift_options, bgzip_options: params.snpeff_bgzip_options, tabix_options: params.snpeff_tabix_options, stats_options: params.snpeff_stats_options ) +include { IVAR_VARIANTS_TO_VCF } from '../../modules/local/ivar_variants_to_vcf' +include { PLOT_BASE_DENSITY } from '../../modules/local/plot_base_density' +include { IVAR_VARIANTS } from '../../modules/nf-core/modules/ivar/variants/main' +include { IVAR_CONSENSUS } from '../../modules/nf-core/modules/ivar/consensus/main' +include { QUAST } from '../../modules/nf-core/modules/quast/main' +include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' +include { NEXTCLADE } from '../../modules/nf-core/modules/nextclade/main' +include { ASCIIGENOME } from '../../modules/local/asciigenome' + +include { VCF_BGZIP_TABIX_STATS } from '../nf-core/vcf_bgzip_tabix_stats' +include { SNPEFF_SNPSIFT } from './snpeff_snpsift' workflow VARIANTS_IVAR { take: @@ -43,91 +27,120 @@ workflow VARIANTS_IVAR { main: + ch_versions = Channel.empty() + // // Call variants // - IVAR_VARIANTS ( bam, fasta, gff ) + IVAR_VARIANTS ( + bam, + fasta, + gff + ) + ch_versions = ch_versions.mix(IVAR_VARIANTS.out.versions.first()) // // Convert original iVar output to VCF, zip and index // - IVAR_VARIANTS_TO_VCF ( IVAR_VARIANTS.out.tsv, ivar_multiqc_header ) + IVAR_VARIANTS_TO_VCF ( + IVAR_VARIANTS.out.tsv, + ivar_multiqc_header + ) + ch_versions = ch_versions.mix(IVAR_VARIANTS_TO_VCF.out.versions.first()) - VCF_BGZIP_TABIX_STATS ( IVAR_VARIANTS_TO_VCF.out.vcf ) + VCF_BGZIP_TABIX_STATS ( + IVAR_VARIANTS_TO_VCF.out.vcf + ) + ch_versions = ch_versions.mix(VCF_BGZIP_TABIX_STATS.out.versions) // // Create genome consensus // - ch_consensus = Channel.empty() - ch_consensus_qual = Channel.empty() - ch_bases_tsv = Channel.empty() - ch_bases_pdf = Channel.empty() - ch_quast_results = Channel.empty() - ch_quast_tsv = Channel.empty() - ch_quast_version = Channel.empty() - ch_pangolin_report = Channel.empty() - ch_pangolin_version = Channel.empty() - ch_nextclade_report = Channel.empty() - ch_nextclade_version = Channel.empty() + ch_consensus = Channel.empty() + ch_consensus_qual = Channel.empty() + ch_bases_tsv = Channel.empty() + ch_bases_pdf = Channel.empty() + ch_quast_results = Channel.empty() + ch_quast_tsv = Channel.empty() + ch_pangolin_report = Channel.empty() + ch_nextclade_report = Channel.empty() if (!params.skip_consensus) { - IVAR_CONSENSUS ( bam, fasta ) - ch_consensus = IVAR_CONSENSUS.out.fasta - ch_consensus_qual = IVAR_CONSENSUS.out.qual + IVAR_CONSENSUS ( + bam, + fasta + ) + ch_consensus = IVAR_CONSENSUS.out.fasta + ch_consensus_qual = IVAR_CONSENSUS.out.qual + ch_versions = ch_versions.mix(IVAR_CONSENSUS.out.versions.first()) - PLOT_BASE_DENSITY ( ch_consensus ) + PLOT_BASE_DENSITY ( + ch_consensus + ) ch_bases_tsv = PLOT_BASE_DENSITY.out.tsv ch_bases_pdf = PLOT_BASE_DENSITY.out.pdf + ch_versions = ch_versions.mix(PLOT_BASE_DENSITY.out.versions.first()) if (!params.skip_variants_quast) { - QUAST ( ch_consensus.collect{ it[1] }, fasta, gff, true, params.gff ) + QUAST ( + ch_consensus.collect{ it[1] }, + fasta, + gff, + true, + params.gff + ) ch_quast_results = QUAST.out.results ch_quast_tsv = QUAST.out.tsv - ch_quast_version = QUAST.out.version + ch_versions = ch_versions.mix(QUAST.out.versions) } if (!params.skip_pangolin) { - PANGOLIN ( ch_consensus ) - ch_pangolin_report = PANGOLIN.out.report - ch_pangolin_version = PANGOLIN.out.version + PANGOLIN ( + ch_consensus + ) + ch_pangolin_report = PANGOLIN.out.report + ch_versions = ch_versions.mix(PANGOLIN.out.versions.first()) } if (!params.skip_nextclade) { - NEXTCLADE ( ch_consensus ) - ch_nextclade_report = NEXTCLADE.out.csv - ch_nextclade_version = NEXTCLADE.out.version + NEXTCLADE ( + ch_consensus + ) + ch_nextclade_report = NEXTCLADE.out.csv + ch_versions = ch_versions.mix(NEXTCLADE.out.versions.first()) } } // // Annotate variants // - ch_snpeff_vcf = Channel.empty() - ch_snpeff_tbi = Channel.empty() - ch_snpeff_stats = Channel.empty() - ch_snpeff_csv = Channel.empty() - ch_snpeff_txt = Channel.empty() - ch_snpeff_html = Channel.empty() - ch_snpsift_txt = Channel.empty() - ch_snpeff_version = Channel.empty() - ch_snpsift_version = Channel.empty() + ch_snpeff_vcf = Channel.empty() + ch_snpeff_tbi = Channel.empty() + ch_snpeff_stats = Channel.empty() + ch_snpeff_csv = Channel.empty() + ch_snpeff_txt = Channel.empty() + ch_snpeff_html = Channel.empty() + ch_snpsift_txt = Channel.empty() if (params.gff && !params.skip_snpeff) { - SNPEFF_SNPSIFT ( VCF_BGZIP_TABIX_STATS.out.vcf, snpeff_db, snpeff_config, fasta ) - ch_snpeff_vcf = SNPEFF_SNPSIFT.out.vcf - ch_snpeff_tbi = SNPEFF_SNPSIFT.out.tbi - ch_snpeff_stats = SNPEFF_SNPSIFT.out.stats - ch_snpeff_csv = SNPEFF_SNPSIFT.out.csv - ch_snpeff_txt = SNPEFF_SNPSIFT.out.txt - ch_snpeff_html = SNPEFF_SNPSIFT.out.html - ch_snpsift_txt = SNPEFF_SNPSIFT.out.snpsift_txt - ch_snpeff_version = SNPEFF_SNPSIFT.out.snpeff_version - ch_snpsift_version = SNPEFF_SNPSIFT.out.snpsift_version + SNPEFF_SNPSIFT ( + VCF_BGZIP_TABIX_STATS.out.vcf, + snpeff_db, + snpeff_config, + fasta + ) + ch_snpeff_vcf = SNPEFF_SNPSIFT.out.vcf + ch_snpeff_tbi = SNPEFF_SNPSIFT.out.tbi + ch_snpeff_stats = SNPEFF_SNPSIFT.out.stats + ch_snpeff_csv = SNPEFF_SNPSIFT.out.csv + ch_snpeff_txt = SNPEFF_SNPSIFT.out.txt + ch_snpeff_html = SNPEFF_SNPSIFT.out.html + ch_snpsift_txt = SNPEFF_SNPSIFT.out.snpsift_txt + ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) } // // Variant screenshots with ASCIIGenome // - ch_asciigenome_pdf = Channel.empty() - ch_asciigenome_version = Channel.empty() + ch_asciigenome_pdf = Channel.empty() if (!params.skip_asciigenome) { bam .join(VCF_BGZIP_TABIX_STATS.out.vcf, by: [0]) @@ -148,49 +161,42 @@ workflow VARIANTS_IVAR { params.asciigenome_window_size, params.asciigenome_read_depth ) - ch_asciigenome_pdf = ASCIIGENOME.out.pdf - ch_asciigenome_version = ASCIIGENOME.out.version + ch_asciigenome_pdf = ASCIIGENOME.out.pdf + ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first()) } emit: - tsv = IVAR_VARIANTS.out.tsv // channel: [ val(meta), [ tsv ] ] - ivar_version = IVAR_VARIANTS.out.version // path: *.version.txt - - vcf_orig = IVAR_VARIANTS_TO_VCF.out.vcf // channel: [ val(meta), [ vcf ] ] - log_out = IVAR_VARIANTS_TO_VCF.out.log // channel: [ val(meta), [ log ] ] - multiqc_tsv = IVAR_VARIANTS_TO_VCF.out.tsv // channel: [ val(meta), [ tsv ] ] - - vcf = VCF_BGZIP_TABIX_STATS.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = VCF_BGZIP_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] - stats = VCF_BGZIP_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] - tabix_version = VCF_BGZIP_TABIX_STATS.out.tabix_version // path: *.version.txt - bcftools_version = VCF_BGZIP_TABIX_STATS.out.bcftools_version // path: *.version.txt - - consensus = ch_consensus // channel: [ val(meta), [ fasta ] ] - consensus_qual = ch_consensus_qual // channel: [ val(meta), [ fasta ] ] - bases_tsv = ch_bases_tsv // channel: [ val(meta), [ tsv ] ] - bases_pdf = ch_bases_pdf // channel: [ val(meta), [ pdf ] ] - - quast_results = ch_quast_results // channel: [ val(meta), [ results ] ] - quast_tsv = ch_quast_tsv // channel: [ val(meta), [ tsv ] ] - quast_version = ch_quast_version // path: *.version.txt - - snpeff_vcf = ch_snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] - snpeff_tbi = ch_snpeff_tbi // channel: [ val(meta), [ tbi ] ] - snpeff_stats = ch_snpeff_stats // channel: [ val(meta), [ txt ] ] - snpeff_csv = ch_snpeff_csv // channel: [ val(meta), [ csv ] ] - snpeff_txt = ch_snpeff_txt // channel: [ val(meta), [ txt ] ] - snpeff_html = ch_snpeff_html // channel: [ val(meta), [ html ] ] - snpsift_txt = ch_snpsift_txt // channel: [ val(meta), [ txt ] ] - snpeff_version = ch_snpeff_version // path: *.version.txt - snpsift_version = ch_snpsift_version // path: *.version.txt - - pangolin_report = ch_pangolin_report // channel: [ val(meta), [ csv ] ] - pangolin_version = ch_pangolin_version // path: *.version.txt - - nextclade_report = ch_nextclade_report // channel: [ val(meta), [ csv ] ] - nextclade_version = ch_nextclade_version // path: *.version.txt - - asciigenome_pdf = ch_asciigenome_pdf // channel: [ val(meta), [ pdf ] ] - asciigenome_version = ch_asciigenome_version // path: *.version.txt + tsv = IVAR_VARIANTS.out.tsv // channel: [ val(meta), [ tsv ] ] + + vcf_orig = IVAR_VARIANTS_TO_VCF.out.vcf // channel: [ val(meta), [ vcf ] ] + log_out = IVAR_VARIANTS_TO_VCF.out.log // channel: [ val(meta), [ log ] ] + multiqc_tsv = IVAR_VARIANTS_TO_VCF.out.tsv // channel: [ val(meta), [ tsv ] ] + + vcf = VCF_BGZIP_TABIX_STATS.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = VCF_BGZIP_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = VCF_BGZIP_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] + + consensus = ch_consensus // channel: [ val(meta), [ fasta ] ] + consensus_qual = ch_consensus_qual // channel: [ val(meta), [ fasta ] ] + bases_tsv = ch_bases_tsv // channel: [ val(meta), [ tsv ] ] + bases_pdf = ch_bases_pdf // channel: [ val(meta), [ pdf ] ] + + quast_results = ch_quast_results // channel: [ val(meta), [ results ] ] + quast_tsv = ch_quast_tsv // channel: [ val(meta), [ tsv ] ] + + snpeff_vcf = ch_snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] + snpeff_tbi = ch_snpeff_tbi // channel: [ val(meta), [ tbi ] ] + snpeff_stats = ch_snpeff_stats // channel: [ val(meta), [ txt ] ] + snpeff_csv = ch_snpeff_csv // channel: [ val(meta), [ csv ] ] + snpeff_txt = ch_snpeff_txt // channel: [ val(meta), [ txt ] ] + snpeff_html = ch_snpeff_html // channel: [ val(meta), [ html ] ] + snpsift_txt = ch_snpsift_txt // channel: [ val(meta), [ txt ] ] + + pangolin_report = ch_pangolin_report // channel: [ val(meta), [ csv ] ] + + nextclade_report = ch_nextclade_report // channel: [ val(meta), [ csv ] ] + + asciigenome_pdf = ch_asciigenome_pdf // channel: [ val(meta), [ pdf ] ] + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-core/align_bowtie2.nf b/subworkflows/nf-core/align_bowtie2.nf index 9cf615fd..1ec4764b 100644 --- a/subworkflows/nf-core/align_bowtie2.nf +++ b/subworkflows/nf-core/align_bowtie2.nf @@ -2,39 +2,48 @@ // Alignment with Bowtie2 // -params.align_options = [:] -params.samtools_options = [:] - -include { BOWTIE2_ALIGN } from '../../modules/nf-core/modules/bowtie2/align/main' addParams( options: params.align_options ) -include { BAM_SORT_SAMTOOLS } from '../nf-core/bam_sort_samtools' addParams( options: params.samtools_options ) +include { BOWTIE2_ALIGN } from '../../modules/nf-core/modules/bowtie2/align/main' +include { BAM_SORT_SAMTOOLS } from './bam_sort_samtools' workflow ALIGN_BOWTIE2 { take: - reads // channel: [ val(meta), [ reads ] ] - index // channel: /path/to/bowtie2/index/ + reads // channel: [ val(meta), [ reads ] ] + index // channel: /path/to/bowtie2/index/ + save_unaligned // value: boolean main: + ch_versions = Channel.empty() + // // Map reads with Bowtie2 // - BOWTIE2_ALIGN ( reads, index ) + BOWTIE2_ALIGN ( + reads, + index, + save_unaligned + ) + ch_versions = ch_versions.mix(BOWTIE2_ALIGN.out.versions.first()) // // Sort, index BAM file and run samtools stats, flagstat and idxstats // - BAM_SORT_SAMTOOLS ( BOWTIE2_ALIGN.out.bam ) + BAM_SORT_SAMTOOLS ( + BOWTIE2_ALIGN.out.bam + ) + ch_versions = ch_versions.mix(BAM_SORT_SAMTOOLS.out.versions) emit: - bam_orig = BOWTIE2_ALIGN.out.bam // channel: [ val(meta), bam ] - log_out = BOWTIE2_ALIGN.out.log // channel: [ val(meta), log ] - fastq = BOWTIE2_ALIGN.out.fastq // channel: [ val(meta), fastq ] - bowtie2_version = BOWTIE2_ALIGN.out.version // path: *.version.txt - - bam = BAM_SORT_SAMTOOLS.out.bam // channel: [ val(meta), [ bam ] ] - bai = BAM_SORT_SAMTOOLS.out.bai // channel: [ val(meta), [ bai ] ] - stats = BAM_SORT_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] - flagstat = BAM_SORT_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] - idxstats = BAM_SORT_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] - samtools_version = BAM_SORT_SAMTOOLS.out.version // path: *.version.txt + bam_orig = BOWTIE2_ALIGN.out.bam // channel: [ val(meta), bam ] + log_out = BOWTIE2_ALIGN.out.log // channel: [ val(meta), log ] + fastq = BOWTIE2_ALIGN.out.fastq // channel: [ val(meta), fastq ] + + bam = BAM_SORT_SAMTOOLS.out.bam // channel: [ val(meta), [ bam ] ] + bai = BAM_SORT_SAMTOOLS.out.bai // channel: [ val(meta), [ bai ] ] + csi = BAM_SORT_SAMTOOLS.out.csi // channel: [ val(meta), [ csi ] ] + stats = BAM_SORT_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] + flagstat = BAM_SORT_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] + idxstats = BAM_SORT_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-core/bam_sort_samtools.nf b/subworkflows/nf-core/bam_sort_samtools.nf index 89ce5661..d1e6c74c 100644 --- a/subworkflows/nf-core/bam_sort_samtools.nf +++ b/subworkflows/nf-core/bam_sort_samtools.nf @@ -2,26 +2,56 @@ // Sort, index BAM file and run samtools stats, flagstat and idxstats // -params.options = [:] - -include { SAMTOOLS_SORT } from '../../modules/nf-core/modules/samtools/sort/main' addParams( options: params.options ) -include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' addParams( options: params.options ) -include { BAM_STATS_SAMTOOLS } from './bam_stats_samtools' addParams( options: params.options ) +include { SAMTOOLS_SORT } from '../../modules/nf-core/modules/samtools/sort/main' +include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' +include { BAM_STATS_SAMTOOLS } from './bam_stats_samtools' workflow BAM_SORT_SAMTOOLS { take: - bam // channel: [ val(meta), [ bam ] ] + ch_bam // channel: [ val(meta), [ bam ] ] main: - SAMTOOLS_SORT ( bam ) - SAMTOOLS_INDEX ( SAMTOOLS_SORT.out.bam ) - BAM_STATS_SAMTOOLS ( SAMTOOLS_SORT.out.bam.join(SAMTOOLS_INDEX.out.bai, by: [0]) ) + + ch_versions = Channel.empty() + + SAMTOOLS_SORT ( + ch_bam + ) + ch_versions = ch_versions.mix(SAMTOOLS_SORT.out.versions.first()) + + SAMTOOLS_INDEX ( + SAMTOOLS_SORT.out.bam + ) + ch_versions = ch_versions.mix(SAMTOOLS_INDEX.out.versions.first()) + + SAMTOOLS_SORT + .out + .bam + .join(SAMTOOLS_INDEX.out.bai, by: [0], remainder: true) + .join(SAMTOOLS_INDEX.out.csi, by: [0], remainder: true) + .map { + meta, bam, bai, csi -> + if (bai) { + [ meta, bam, bai ] + } else { + [ meta, bam, csi ] + } + } + .set { ch_bam_bai } + + BAM_STATS_SAMTOOLS ( + ch_bam_bai + ) + ch_versions = ch_versions.mix(BAM_STATS_SAMTOOLS.out.versions) emit: bam = SAMTOOLS_SORT.out.bam // channel: [ val(meta), [ bam ] ] bai = SAMTOOLS_INDEX.out.bai // channel: [ val(meta), [ bai ] ] + csi = SAMTOOLS_INDEX.out.csi // channel: [ val(meta), [ csi ] ] + stats = BAM_STATS_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] flagstat = BAM_STATS_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] idxstats = BAM_STATS_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] - version = SAMTOOLS_SORT.out.version // path: *.version.txt + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-core/bam_stats_samtools.nf b/subworkflows/nf-core/bam_stats_samtools.nf index 00476afe..68d632c3 100644 --- a/subworkflows/nf-core/bam_stats_samtools.nf +++ b/subworkflows/nf-core/bam_stats_samtools.nf @@ -2,24 +2,37 @@ // Run SAMtools stats, flagstat and idxstats // -params.options = [:] - -include { SAMTOOLS_STATS } from '../../modules/nf-core/modules/samtools/stats/main' addParams( options: params.options ) -include { SAMTOOLS_IDXSTATS } from '../../modules/nf-core/modules/samtools/idxstats/main' addParams( options: params.options ) -include { SAMTOOLS_FLAGSTAT } from '../../modules/nf-core/modules/samtools/flagstat/main' addParams( options: params.options ) +include { SAMTOOLS_STATS } from '../../modules/nf-core/modules/samtools/stats/main' +include { SAMTOOLS_IDXSTATS } from '../../modules/nf-core/modules/samtools/idxstats/main' +include { SAMTOOLS_FLAGSTAT } from '../../modules/nf-core/modules/samtools/flagstat/main' workflow BAM_STATS_SAMTOOLS { take: - bam_bai // channel: [ val(meta), [ bam ], [bai] ] + ch_bam_bai // channel: [ val(meta), [ bam ], [bai/csi] ] main: - SAMTOOLS_STATS ( bam_bai ) - SAMTOOLS_FLAGSTAT ( bam_bai ) - SAMTOOLS_IDXSTATS ( bam_bai ) + ch_versions = Channel.empty() + + SAMTOOLS_STATS ( + ch_bam_bai, + [] + ) + ch_versions = ch_versions.mix(SAMTOOLS_STATS.out.versions.first()) + + SAMTOOLS_FLAGSTAT ( + ch_bam_bai + ) + ch_versions = ch_versions.mix(SAMTOOLS_FLAGSTAT.out.versions.first()) + + SAMTOOLS_IDXSTATS ( + ch_bam_bai + ) + ch_versions = ch_versions.mix(SAMTOOLS_IDXSTATS.out.versions.first()) emit: stats = SAMTOOLS_STATS.out.stats // channel: [ val(meta), [ stats ] ] flagstat = SAMTOOLS_FLAGSTAT.out.flagstat // channel: [ val(meta), [ flagstat ] ] idxstats = SAMTOOLS_IDXSTATS.out.idxstats // channel: [ val(meta), [ idxstats ] ] - version = SAMTOOLS_STATS.out.version // path: *.version.txt + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-core/fastqc_fastp.nf b/subworkflows/nf-core/fastqc_fastp.nf index 41193c1a..45be0c14 100644 --- a/subworkflows/nf-core/fastqc_fastp.nf +++ b/subworkflows/nf-core/fastqc_fastp.nf @@ -2,47 +2,60 @@ // Read QC and trimming // -params.fastqc_raw_options = [:] -params.fastqc_trim_options = [:] -params.fastp_options = [:] - -include { FASTQC as FASTQC_RAW } from '../../modules/nf-core/modules/fastqc/main' addParams( options: params.fastqc_raw_options ) -include { FASTQC as FASTQC_TRIM } from '../../modules/nf-core/modules/fastqc/main' addParams( options: params.fastqc_trim_options ) -include { FASTP } from '../../modules/nf-core/modules/fastp/main' addParams( options: params.fastp_options ) +include { FASTQC as FASTQC_RAW } from '../../modules/nf-core/modules/fastqc/main' +include { FASTQC as FASTQC_TRIM } from '../../modules/nf-core/modules/fastqc/main' +include { FASTP } from '../../modules/nf-core/modules/fastp/main' workflow FASTQC_FASTP { take: - reads // channel: [ val(meta), [ reads ] ] + reads // channel: [ val(meta), [ reads ] ] + save_trimmed_fail // value: boolean + save_merged // value: boolean main: + + ch_versions = Channel.empty() + fastqc_raw_html = Channel.empty() fastqc_raw_zip = Channel.empty() - fastqc_version = Channel.empty() if (!params.skip_fastqc) { - FASTQC_RAW ( reads ).html.set { fastqc_raw_html } - fastqc_raw_zip = FASTQC_RAW.out.zip - fastqc_version = FASTQC_RAW.out.version + FASTQC_RAW ( + reads + ) + fastqc_raw_html = FASTQC_RAW.out.html + fastqc_raw_zip = FASTQC_RAW.out.zip + ch_versions = ch_versions.mix(FASTQC_RAW.out.versions.first()) } - trim_reads = reads - trim_json = Channel.empty() - trim_html = Channel.empty() - trim_log = Channel.empty() - trim_reads_fail = Channel.empty() - fastp_version = Channel.empty() - fastqc_trim_html = Channel.empty() - fastqc_trim_zip = Channel.empty() + trim_reads = reads + trim_json = Channel.empty() + trim_html = Channel.empty() + trim_log = Channel.empty() + trim_reads_fail = Channel.empty() + trim_reads_merged = Channel.empty() + fastqc_trim_html = Channel.empty() + fastqc_trim_zip = Channel.empty() if (!params.skip_fastp) { - FASTP ( reads ).reads.set { trim_reads } - trim_json = FASTP.out.json - trim_html = FASTP.out.html - trim_log = FASTP.out.log - trim_reads_fail = FASTP.out.reads_fail - fastp_version = FASTP.out.version + FASTP ( + reads, + save_trimmed_fail, + save_merged + ) + trim_reads = FASTP.out.reads + trim_json = FASTP.out.json + trim_html = FASTP.out.html + trim_log = FASTP.out.log + trim_reads_fail = FASTP.out.reads_fail + trim_reads_merged = FASTP.out.reads_merged + ch_versions = ch_versions.mix(FASTP.out.versions.first()) if (!params.skip_fastqc) { - FASTQC_TRIM ( trim_reads ).html.set { fastqc_trim_html } - fastqc_trim_zip = FASTQC_TRIM.out.zip + FASTQC_TRIM ( + trim_reads + ) + fastqc_trim_html = FASTQC_TRIM.out.html + fastqc_trim_zip = FASTQC_TRIM.out.zip + ch_versions = ch_versions.mix(FASTQC_TRIM.out.versions.first()) } } @@ -52,11 +65,12 @@ workflow FASTQC_FASTP { trim_html // channel: [ val(meta), [ html ] ] trim_log // channel: [ val(meta), [ log ] ] trim_reads_fail // channel: [ val(meta), [ fastq.gz ] ] - fastp_version // path: *.version.txt + trim_reads_merged // channel: [ val(meta), [ fastq.gz ] ] fastqc_raw_html // channel: [ val(meta), [ html ] ] fastqc_raw_zip // channel: [ val(meta), [ zip ] ] fastqc_trim_html // channel: [ val(meta), [ html ] ] fastqc_trim_zip // channel: [ val(meta), [ zip ] ] - fastqc_version // path: *.version.txt + + versions = ch_versions.ifEmpty(null) // channel: [ versions.yml ] } diff --git a/subworkflows/nf-core/filter_bam_samtools.nf b/subworkflows/nf-core/filter_bam_samtools.nf index 583aa29f..cfa8b568 100644 --- a/subworkflows/nf-core/filter_bam_samtools.nf +++ b/subworkflows/nf-core/filter_bam_samtools.nf @@ -2,12 +2,9 @@ // Filter co-ordinate sorted BAM, index and run samtools stats, flagstat and idxstats // -params.samtools_view_options = [:] -params.samtools_index_options = [:] - -include { SAMTOOLS_VIEW } from '../../modules/nf-core/modules/samtools/view/main' addParams( options: params.samtools_view_options ) -include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' addParams( options: params.samtools_index_options ) -include { BAM_STATS_SAMTOOLS } from '../nf-core/bam_stats_samtools' addParams( options: params.samtools_index_options ) +include { SAMTOOLS_VIEW } from '../../modules/nf-core/modules/samtools/view/main' +include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' +include { BAM_STATS_SAMTOOLS } from './bam_stats_samtools' workflow FILTER_BAM_SAMTOOLS { take: @@ -15,22 +12,36 @@ workflow FILTER_BAM_SAMTOOLS { main: + ch_versions = Channel.empty() + // // Filter BAM using Samtools view // - SAMTOOLS_VIEW ( bam ) + SAMTOOLS_VIEW ( + bam, + [] + ) + ch_versions = ch_versions.mix(SAMTOOLS_VIEW.out.versions.first()) // // Index BAM file and run samtools stats, flagstat and idxstats // - SAMTOOLS_INDEX ( SAMTOOLS_VIEW.out.bam ) - BAM_STATS_SAMTOOLS ( SAMTOOLS_VIEW.out.bam.join(SAMTOOLS_INDEX.out.bai, by: [0]) ) + SAMTOOLS_INDEX ( + SAMTOOLS_VIEW.out.bam + ) + ch_versions = ch_versions.mix(SAMTOOLS_INDEX.out.versions.first()) + + BAM_STATS_SAMTOOLS ( + SAMTOOLS_VIEW.out.bam.join(SAMTOOLS_INDEX.out.bai, by: [0]) + ) + ch_versions = ch_versions.mix(BAM_STATS_SAMTOOLS.out.versions) emit: - bam = SAMTOOLS_VIEW.out.bam // channel: [ val(meta), [ bam ] ] - bai = SAMTOOLS_INDEX.out.bai // channel: [ val(meta), [ bai ] ] - stats = BAM_STATS_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] - flagstat = BAM_STATS_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] - idxstats = BAM_STATS_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] - samtools_version = SAMTOOLS_INDEX.out.version // path: *.version.txt + bam = SAMTOOLS_VIEW.out.bam // channel: [ val(meta), [ bam ] ] + bai = SAMTOOLS_INDEX.out.bai // channel: [ val(meta), [ bai ] ] + stats = BAM_STATS_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] + flagstat = BAM_STATS_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] + idxstats = BAM_STATS_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-core/mark_duplicates_picard.nf b/subworkflows/nf-core/mark_duplicates_picard.nf index e5bc9501..08bb41ba 100644 --- a/subworkflows/nf-core/mark_duplicates_picard.nf +++ b/subworkflows/nf-core/mark_duplicates_picard.nf @@ -1,13 +1,10 @@ // -// Picard MarkDuplicates, sort, index BAM file and run samtools stats, flagstat and idxstats +// Picard MarkDuplicates, index BAM file and run samtools stats, flagstat and idxstats // -params.markduplicates_options = [:] -params.samtools_options = [:] - -include { PICARD_MARKDUPLICATES } from '../../modules/nf-core/modules/picard/markduplicates/main' addParams( options: params.markduplicates_options ) -include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' addParams( options: params.samtools_options ) -include { BAM_STATS_SAMTOOLS } from './bam_stats_samtools' addParams( options: params.samtools_options ) +include { PICARD_MARKDUPLICATES } from '../../modules/nf-core/modules/picard/markduplicates/main' +include { SAMTOOLS_INDEX } from '../../modules/nf-core/modules/samtools/index/main' +include { BAM_STATS_SAMTOOLS } from './bam_stats_samtools' workflow MARK_DUPLICATES_PICARD { take: @@ -15,25 +12,53 @@ workflow MARK_DUPLICATES_PICARD { main: + ch_versions = Channel.empty() + // // Picard MarkDuplicates // - PICARD_MARKDUPLICATES ( bam ) + PICARD_MARKDUPLICATES ( + bam + ) + ch_versions = ch_versions.mix(PICARD_MARKDUPLICATES.out.versions.first()) // // Index BAM file and run samtools stats, flagstat and idxstats // - SAMTOOLS_INDEX ( PICARD_MARKDUPLICATES.out.bam ) - BAM_STATS_SAMTOOLS ( PICARD_MARKDUPLICATES.out.bam.join(SAMTOOLS_INDEX.out.bai, by: [0]) ) + SAMTOOLS_INDEX ( + PICARD_MARKDUPLICATES.out.bam + ) + ch_versions = ch_versions.mix(SAMTOOLS_INDEX.out.versions.first()) + + PICARD_MARKDUPLICATES + .out + .bam + .join(SAMTOOLS_INDEX.out.bai, by: [0], remainder: true) + .join(SAMTOOLS_INDEX.out.csi, by: [0], remainder: true) + .map { + meta, bam, bai, csi -> + if (bai) { + [ meta, bam, bai ] + } else { + [ meta, bam, csi ] + } + } + .set { ch_bam_bai } + + BAM_STATS_SAMTOOLS ( + ch_bam_bai + ) + ch_versions = ch_versions.mix(BAM_STATS_SAMTOOLS.out.versions) emit: - bam = PICARD_MARKDUPLICATES.out.bam // channel: [ val(meta), [ bam ] ] - metrics = PICARD_MARKDUPLICATES.out.metrics // channel: [ val(meta), [ metrics ] ] - picard_version = PICARD_MARKDUPLICATES.out.version // path: *.version.txt - - bai = SAMTOOLS_INDEX.out.bai // channel: [ val(meta), [ bai ] ] - stats = BAM_STATS_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] - flagstat = BAM_STATS_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] - idxstats = BAM_STATS_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] - samtools_version = SAMTOOLS_INDEX.out.version // path: *.version.txt + bam = PICARD_MARKDUPLICATES.out.bam // channel: [ val(meta), [ bam ] ] + metrics = PICARD_MARKDUPLICATES.out.metrics // channel: [ val(meta), [ metrics ] ] + + bai = SAMTOOLS_INDEX.out.bai // channel: [ val(meta), [ bai ] ] + csi = SAMTOOLS_INDEX.out.csi // channel: [ val(meta), [ csi ] ] + stats = BAM_STATS_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] + flagstat = BAM_STATS_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] + idxstats = BAM_STATS_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] + + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-core/primer_trim_ivar.nf b/subworkflows/nf-core/primer_trim_ivar.nf new file mode 100644 index 00000000..e3046bb0 --- /dev/null +++ b/subworkflows/nf-core/primer_trim_ivar.nf @@ -0,0 +1,45 @@ +// +// iVar trim, sort, index BAM file and run samtools stats, flagstat and idxstats +// + +include { IVAR_TRIM } from '../../modules/nf-core/modules/ivar/trim/main' +include { BAM_SORT_SAMTOOLS } from './bam_sort_samtools' + +workflow PRIMER_TRIM_IVAR { + take: + bam // channel: [ val(meta), [ bam ], [bai] ] + bed // path : bed + + main: + + ch_versions = Channel.empty() + + // + // iVar trim primers + // + IVAR_TRIM ( + bam, + bed + ) + ch_versions = ch_versions.mix(IVAR_TRIM.out.versions.first()) + + // + // Sort, index BAM file and run samtools stats, flagstat and idxstats + // + BAM_SORT_SAMTOOLS ( + IVAR_TRIM.out.bam + ) + ch_versions = ch_versions.mix(BAM_SORT_SAMTOOLS.out.versions) + + emit: + bam_orig = IVAR_TRIM.out.bam // channel: [ val(meta), bam ] + log_out = IVAR_TRIM.out.log // channel: [ val(meta), log ] + + bam = BAM_SORT_SAMTOOLS.out.bam // channel: [ val(meta), [ bam ] ] + bai = BAM_SORT_SAMTOOLS.out.bai // channel: [ val(meta), [ bai ] ] + stats = BAM_SORT_SAMTOOLS.out.stats // channel: [ val(meta), [ stats ] ] + flagstat = BAM_SORT_SAMTOOLS.out.flagstat // channel: [ val(meta), [ flagstat ] ] + idxstats = BAM_SORT_SAMTOOLS.out.idxstats // channel: [ val(meta), [ idxstats ] ] + + versions = ch_versions // channel: [ versions.yml ] +} diff --git a/subworkflows/nf-core/vcf_bgzip_tabix_stats.nf b/subworkflows/nf-core/vcf_bgzip_tabix_stats.nf index 73437873..6df4bad7 100644 --- a/subworkflows/nf-core/vcf_bgzip_tabix_stats.nf +++ b/subworkflows/nf-core/vcf_bgzip_tabix_stats.nf @@ -2,26 +2,31 @@ // Run BCFTools bgzip, tabix and stats commands // -params.bgzip_options = [:] -params.tabix_options = [:] -params.stats_options = [:] - -include { TABIX_BGZIP } from '../../modules/nf-core/modules/tabix/bgzip/main' addParams( options: params.bgzip_options ) -include { VCF_TABIX_STATS } from './vcf_tabix_stats' addParams( tabix_options: params.tabix_options, stats_options: params.stats_options ) +include { TABIX_BGZIP } from '../../modules/nf-core/modules/tabix/bgzip/main' +include { VCF_TABIX_STATS } from './vcf_tabix_stats' workflow VCF_BGZIP_TABIX_STATS { take: vcf // channel: [ val(meta), [ vcf ] ] main: - TABIX_BGZIP ( vcf ) - VCF_TABIX_STATS ( TABIX_BGZIP.out.gz ) + + ch_versions = Channel.empty() + + TABIX_BGZIP ( + vcf + ) + ch_versions = ch_versions.mix(TABIX_BGZIP.out.versions.first()) + + VCF_TABIX_STATS ( + TABIX_BGZIP.out.gz + ) + ch_versions = ch_versions.mix(VCF_TABIX_STATS.out.versions) emit: - vcf = TABIX_BGZIP.out.gz // channel: [ val(meta), [ vcf.gz ] ] - tabix_version = TABIX_BGZIP.out.version // path: *.version.txt + vcf = TABIX_BGZIP.out.gz // channel: [ val(meta), [ vcf.gz ] ] + tbi = VCF_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = VCF_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] - tbi = VCF_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] - stats = VCF_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] - bcftools_version = VCF_TABIX_STATS.out.bcftools_version // path: *.version.txt + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-core/vcf_tabix_stats.nf b/subworkflows/nf-core/vcf_tabix_stats.nf index a49d824f..623ff347 100644 --- a/subworkflows/nf-core/vcf_tabix_stats.nf +++ b/subworkflows/nf-core/vcf_tabix_stats.nf @@ -2,24 +2,31 @@ // Run BCFTools tabix and stats commands // -params.tabix_options = [:] -params.stats_options = [:] - -include { TABIX_TABIX } from '../../modules/nf-core/modules/tabix/tabix/main' addParams( options: params.tabix_options ) -include { BCFTOOLS_STATS } from '../../modules/nf-core/modules/bcftools/stats/main' addParams( options: params.stats_options ) +include { TABIX_TABIX } from '../../modules/nf-core/modules/tabix/tabix/main' +include { BCFTOOLS_STATS } from '../../modules/nf-core/modules/bcftools/stats/main' workflow VCF_TABIX_STATS { take: vcf // channel: [ val(meta), [ vcf ] ] main: - TABIX_TABIX ( vcf ) - BCFTOOLS_STATS ( vcf ) + + ch_versions = Channel.empty() + + TABIX_TABIX ( + vcf + ) + ch_versions = ch_versions.mix(TABIX_TABIX.out.versions.first()) + + BCFTOOLS_STATS ( + vcf + ) + ch_versions = ch_versions.mix(BCFTOOLS_STATS.out.versions.first()) emit: - tbi = TABIX_TABIX.out.tbi // channel: [ val(meta), [ tbi ] ] - tabix_version = TABIX_TABIX.out.version // path: *.version.txt + tbi = TABIX_TABIX.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = BCFTOOLS_STATS.out.stats // channel: [ val(meta), [ txt ] ] + + versions = ch_versions // channel: [ versions.yml ] - stats = BCFTOOLS_STATS.out.stats // channel: [ val(meta), [ txt ] ] - bcftools_version = BCFTOOLS_STATS.out.version // path: *.version.txt } diff --git a/workflows/illumina.nf b/workflows/illumina.nf index f8cba65a..b0ae3df5 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -53,72 +53,29 @@ ch_ivar_variants_header_mqc = file("$projectDir/assets/headers/ivar_variants_hea ======================================================================================== */ -// Don't overwrite global params.modules, create a copy instead and use that within the main script. -def modules = params.modules.clone() - -def multiqc_options = modules['illumina_multiqc'] -multiqc_options.args += params.multiqc_title ? Utils.joinModuleArgs(["--title \"$params.multiqc_title\""]) : '' - -if (!params.skip_assembly) { - multiqc_options.publish_files.put('assembly_metrics_mqc.csv','') -} -if (!params.skip_variants) { - multiqc_options.publish_files.put('variants_metrics_mqc.csv','') -} - -include { BCFTOOLS_ISEC } from '../modules/local/bcftools_isec' addParams( options: modules['illumina_bcftools_isec'] ) -include { CUTADAPT } from '../modules/local/cutadapt' addParams( options: modules['illumina_cutadapt'] ) -include { GET_SOFTWARE_VERSIONS } from '../modules/local/get_software_versions' addParams( options: [publish_files: ['tsv':'']] ) -include { MULTIQC } from '../modules/local/multiqc_illumina' addParams( options: multiqc_options ) -include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_GENOME } from '../modules/local/plot_mosdepth_regions' addParams( options: modules['illumina_plot_mosdepth_regions_genome'] ) -include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_AMPLICON } from '../modules/local/plot_mosdepth_regions' addParams( options: modules['illumina_plot_mosdepth_regions_amplicon'] ) -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_FAIL_READS } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_FAIL_MAPPED } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_IVAR_NEXTCLADE } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_BCFTOOLS_NEXTCLADE } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) +// +// MODULE: Loaded from modules/local/ +// +include { BCFTOOLS_ISEC } from '../modules/local/bcftools_isec' +include { CUTADAPT } from '../modules/local/cutadapt' +include { MULTIQC } from '../modules/local/multiqc_illumina' +include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_GENOME } from '../modules/local/plot_mosdepth_regions' +include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_AMPLICON } from '../modules/local/plot_mosdepth_regions' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_FAIL_READS } from '../modules/local/multiqc_tsv_from_list' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_FAIL_MAPPED } from '../modules/local/multiqc_tsv_from_list' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_IVAR_NEXTCLADE } from '../modules/local/multiqc_tsv_from_list' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_BCFTOOLS_NEXTCLADE } from '../modules/local/multiqc_tsv_from_list' // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -def publish_genome_options = params.save_reference ? [publish_dir: 'genome'] : [publish_files: false] -def publish_index_options = params.save_reference ? [publish_dir: 'genome/index'] : [publish_files: false] -def publish_db_options = params.save_reference ? [publish_dir: 'genome/db'] : [publish_files: false] -def bedtools_getfasta_options = modules['illumina_bedtools_getfasta'] -def bowtie2_build_options = modules['illumina_bowtie2_build'] -def snpeff_build_options = modules['illumina_snpeff_build'] -def makeblastdb_options = modules['illumina_blast_makeblastdb'] -def kraken2_build_options = modules['illumina_kraken2_build'] -def collapse_primers_options = modules['illumina_collapse_primers_illumina'] -if (!params.save_reference) { - bedtools_getfasta_options['publish_files'] = false - bowtie2_build_options['publish_files'] = false - snpeff_build_options['publish_files'] = false - makeblastdb_options['publish_files'] = false - kraken2_build_options['publish_files'] = false - collapse_primers_options['publish_files'] = false -} - -def ivar_trim_options = modules['illumina_ivar_trim'] -ivar_trim_options.args += params.ivar_trim_noprimer ? '' : Utils.joinModuleArgs(['-e']) -ivar_trim_options.args += params.ivar_trim_offset ? Utils.joinModuleArgs(["-x ${params.ivar_trim_offset}"]) : '' - -def ivar_trim_sort_bam_options = modules['illumina_ivar_trim_sort_bam'] -if (params.skip_markduplicates) { - ivar_trim_sort_bam_options.publish_files.put('bam','') - ivar_trim_sort_bam_options.publish_files.put('bai','') -} - -def spades_options = modules['illumina_spades'] -spades_options.args += params.spades_mode ? Utils.joinModuleArgs(["--${params.spades_mode}"]) : '' - -include { INPUT_CHECK } from '../subworkflows/local/input_check' addParams( options: [:] ) -include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_illumina' addParams( genome_options: publish_genome_options, index_options: publish_index_options, db_options: publish_db_options, bowtie2_build_options: bowtie2_build_options, bedtools_getfasta_options: bedtools_getfasta_options, collapse_primers_options: collapse_primers_options, snpeff_build_options: snpeff_build_options, makeblastdb_options: makeblastdb_options, kraken2_build_options: kraken2_build_options ) -include { PRIMER_TRIM_IVAR } from '../subworkflows/local/primer_trim_ivar' addParams( ivar_trim_options: ivar_trim_options, samtools_options: ivar_trim_sort_bam_options ) -include { VARIANTS_IVAR } from '../subworkflows/local/variants_ivar' addParams( ivar_variants_options: modules['illumina_ivar_variants'], ivar_variants_to_vcf_options: modules['illumina_ivar_variants_to_vcf'], tabix_bgzip_options: modules['illumina_ivar_tabix_bgzip'], tabix_tabix_options: modules['illumina_ivar_tabix_tabix'], bcftools_stats_options: modules['illumina_ivar_bcftools_stats'], ivar_consensus_options: modules['illumina_ivar_consensus'], consensus_plot_options: modules['illumina_ivar_consensus_plot'], quast_options: modules['illumina_ivar_quast'], snpeff_options: modules['illumina_ivar_snpeff'], snpsift_options: modules['illumina_ivar_snpsift'], snpeff_bgzip_options: modules['illumina_ivar_snpeff_bgzip'], snpeff_tabix_options: modules['illumina_ivar_snpeff_tabix'], snpeff_stats_options: modules['illumina_ivar_snpeff_stats'], pangolin_options: modules['illumina_ivar_pangolin'], nextclade_options: modules['illumina_ivar_nextclade'], asciigenome_options: modules['illumina_ivar_asciigenome'] ) -include { VARIANTS_BCFTOOLS } from '../subworkflows/local/variants_bcftools' addParams( bcftools_mpileup_options: modules['illumina_bcftools_mpileup'], quast_options: modules['illumina_bcftools_quast'], consensus_genomecov_options: modules['illumina_bcftools_consensus_genomecov'], consensus_merge_options: modules['illumina_bcftools_consensus_merge'], consensus_mask_options: modules['illumina_bcftools_consensus_mask'], consensus_maskfasta_options: modules['illumina_bcftools_consensus_maskfasta'], consensus_bcftools_options: modules['illumina_bcftools_consensus_bcftools'], consensus_plot_options: modules['illumina_bcftools_consensus_plot'], snpeff_options: modules['illumina_bcftools_snpeff'], snpsift_options: modules['illumina_bcftools_snpsift'], snpeff_bgzip_options: modules['illumina_bcftools_snpeff_bgzip'], snpeff_tabix_options: modules['illumina_bcftools_snpeff_tabix'], snpeff_stats_options: modules['illumina_bcftools_snpeff_stats'], pangolin_options: modules['illumina_bcftools_pangolin'], nextclade_options: modules['illumina_bcftools_nextclade'], asciigenome_options: modules['illumina_bcftools_asciigenome'] ) -include { ASSEMBLY_SPADES } from '../subworkflows/local/assembly_spades' addParams( spades_options: spades_options, bandage_options: modules['illumina_spades_bandage'], blastn_options: modules['illumina_spades_blastn'], blastn_filter_options: modules['illumina_spades_blastn_filter'], abacas_options: modules['illumina_spades_abacas'], plasmidid_options: modules['illumina_spades_plasmidid'], quast_options: modules['illumina_spades_quast'] ) -include { ASSEMBLY_UNICYCLER } from '../subworkflows/local/assembly_unicycler' addParams( unicycler_options: modules['illumina_unicycler'], bandage_options: modules['illumina_unicycler_bandage'], blastn_options: modules['illumina_unicycler_blastn'], blastn_filter_options: modules['illumina_unicycler_blastn_filter'], abacas_options: modules['illumina_unicycler_abacas'], plasmidid_options: modules['illumina_unicycler_plasmidid'], quast_options: modules['illumina_unicycler_quast'] ) -include { ASSEMBLY_MINIA } from '../subworkflows/local/assembly_minia' addParams( minia_options: modules['illumina_minia'], blastn_options: modules['illumina_minia_blastn'], blastn_filter_options: modules['illumina_minia_blastn_filter'], abacas_options: modules['illumina_minia_abacas'], plasmidid_options: modules['illumina_minia_plasmidid'], quast_options: modules['illumina_minia_quast'] ) +include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_illumina' +include { VARIANTS_IVAR } from '../subworkflows/local/variants_ivar' +include { VARIANTS_BCFTOOLS } from '../subworkflows/local/variants_bcftools' +include { ASSEMBLY_SPADES } from '../subworkflows/local/assembly_spades' +include { ASSEMBLY_UNICYCLER } from '../subworkflows/local/assembly_unicycler' +include { ASSEMBLY_MINIA } from '../subworkflows/local/assembly_minia' /* ======================================================================================== @@ -129,28 +86,21 @@ include { ASSEMBLY_MINIA } from '../subworkflows/local/assembly_minia' // // MODULE: Installed directly from nf-core/modules // -include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fastq/main' addParams( options: modules['illumina_cat_fastq'] ) -include { FASTQC } from '../modules/nf-core/modules/fastqc/main' addParams( options: modules['illumina_cutadapt_fastqc'] ) -include { KRAKEN2_KRAKEN2 } from '../modules/nf-core/modules/kraken2/kraken2/main' addParams( options: modules['illumina_kraken2_kraken2'] ) -include { PICARD_COLLECTMULTIPLEMETRICS } from '../modules/nf-core/modules/picard/collectmultiplemetrics/main' addParams( options: modules['illumina_picard_collectmultiplemetrics'] ) -include { MOSDEPTH as MOSDEPTH_GENOME } from '../modules/nf-core/modules/mosdepth/main' addParams( options: modules['illumina_mosdepth_genome'] ) -include { MOSDEPTH as MOSDEPTH_AMPLICON } from '../modules/nf-core/modules/mosdepth/main' addParams( options: modules['illumina_mosdepth_amplicon'] ) +include { CAT_FASTQ } from '../modules/nf-core/modules/cat/fastq/main' +include { FASTQC } from '../modules/nf-core/modules/fastqc/main' +include { KRAKEN2_KRAKEN2 } from '../modules/nf-core/modules/kraken2/kraken2/main' +include { PICARD_COLLECTMULTIPLEMETRICS } from '../modules/nf-core/modules/picard/collectmultiplemetrics/main' +include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' +include { MOSDEPTH as MOSDEPTH_GENOME } from '../modules/nf-core/modules/mosdepth/main' +include { MOSDEPTH as MOSDEPTH_AMPLICON } from '../modules/nf-core/modules/mosdepth/main' // // SUBWORKFLOW: Consisting entirely of nf-core/modules // -def fastp_options = modules['illumina_fastp'] -if (params.save_trimmed_fail) { fastp_options.publish_files.put('fail.fastq.gz','') } - -def bowtie2_align_options = modules['illumina_bowtie2_align'] -if (params.save_unaligned) { bowtie2_align_options.publish_files.put('fastq.gz','unmapped') } - -def markduplicates_options = modules['illumina_picard_markduplicates'] -markduplicates_options.args += params.filter_duplicates ? Utils.joinModuleArgs(['REMOVE_DUPLICATES=true']) : '' - -include { FASTQC_FASTP } from '../subworkflows/nf-core/fastqc_fastp' addParams( fastqc_raw_options: modules['illumina_fastqc_raw'], fastqc_trim_options: modules['illumina_fastqc_trim'], fastp_options: fastp_options ) -include { ALIGN_BOWTIE2 } from '../subworkflows/nf-core/align_bowtie2' addParams( align_options: bowtie2_align_options, samtools_options: modules['illumina_bowtie2_sort_bam'] ) -include { MARK_DUPLICATES_PICARD } from '../subworkflows/nf-core/mark_duplicates_picard' addParams( markduplicates_options: markduplicates_options, samtools_options: modules['illumina_picard_markduplicates_sort_bam'] ) +include { FASTQC_FASTP } from '../subworkflows/nf-core/fastqc_fastp' +include { ALIGN_BOWTIE2 } from '../subworkflows/nf-core/align_bowtie2' +include { PRIMER_TRIM_IVAR } from '../subworkflows/nf-core/primer_trim_ivar' +include { MARK_DUPLICATES_PICARD } from '../subworkflows/nf-core/mark_duplicates_picard' /* ======================================================================================== @@ -165,7 +115,7 @@ def fail_mapped_reads = [:] workflow ILLUMINA { - ch_software_versions = Channel.empty() + ch_versions = Channel.empty() // // SUBWORKFLOW: Uncompress and prepare reference genome files @@ -173,6 +123,7 @@ workflow ILLUMINA { PREPARE_GENOME ( ch_dummy_file ) + ch_versions = ch_versions.mix(PREPARE_GENOME.out.versions) // Check genome fasta only contains a single contig PREPARE_GENOME @@ -203,6 +154,7 @@ workflow ILLUMINA { ch_input, params.platform ) + .sample_info .map { meta, fastq -> meta.id = meta.id.split('_')[0..-2].join('_') @@ -217,6 +169,7 @@ workflow ILLUMINA { return [ meta, fastq.flatten() ] } .set { ch_fastq } + ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) // // MODULE: Concatenate FastQ files from same sample if required @@ -224,18 +177,21 @@ workflow ILLUMINA { CAT_FASTQ ( ch_fastq.multiple ) + .reads .mix(ch_fastq.single) .set { ch_cat_fastq } + ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first().ifEmpty(null)) // // SUBWORKFLOW: Read QC and trim adapters // FASTQC_FASTP ( - ch_cat_fastq + ch_cat_fastq, + params.save_trimmed_fail, + false ) - ch_variants_fastq = FASTQC_FASTP.out.reads - ch_software_versions = ch_software_versions.mix(FASTQC_FASTP.out.fastqc_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(FASTQC_FASTP.out.fastp_version.first().ifEmpty(null)) + ch_variants_fastq = FASTQC_FASTP.out.reads + ch_versions = ch_versions.mix(FASTQC_FASTP.out.versions) // // Filter empty FastQ files after adapter trimming @@ -266,7 +222,7 @@ workflow ILLUMINA { } .set { ch_pass_fail_reads } - MULTIQC_CUSTOM_TSV_FAIL_READS ( + MULTIQC_TSV_FAIL_READS ( ch_pass_fail_reads.collect(), 'Sample\tReads before trimming', 'fail_mapped_reads' @@ -284,8 +240,8 @@ workflow ILLUMINA { ch_variants_fastq, PREPARE_GENOME.out.kraken2_db ) - ch_kraken2_multiqc = KRAKEN2_KRAKEN2.out.txt - ch_software_versions = ch_software_versions.mix(KRAKEN2_KRAKEN2.out.version.first().ifEmpty(null)) + ch_kraken2_multiqc = KRAKEN2_KRAKEN2.out.txt + ch_versions = ch_versions.mix(KRAKEN2_KRAKEN2.out.versions.first().ifEmpty(null)) if (params.kraken2_variants_host_filter) { ch_variants_fastq = KRAKEN2_KRAKEN2.out.unclassified @@ -306,14 +262,14 @@ workflow ILLUMINA { if (!params.skip_variants) { ALIGN_BOWTIE2 ( ch_variants_fastq, - PREPARE_GENOME.out.bowtie2_index + PREPARE_GENOME.out.bowtie2_index, + params.save_unaligned ) ch_bam = ALIGN_BOWTIE2.out.bam ch_bai = ALIGN_BOWTIE2.out.bai ch_bowtie2_multiqc = ALIGN_BOWTIE2.out.log_out ch_bowtie2_flagstat_multiqc = ALIGN_BOWTIE2.out.flagstat - ch_software_versions = ch_software_versions.mix(ALIGN_BOWTIE2.out.bowtie2_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ALIGN_BOWTIE2.out.samtools_version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(ALIGN_BOWTIE2.out.versions) } // @@ -346,7 +302,7 @@ workflow ILLUMINA { } .set { ch_pass_fail_mapped } - MULTIQC_CUSTOM_TSV_FAIL_MAPPED ( + MULTIQC_TSV_FAIL_MAPPED ( ch_pass_fail_mapped.fail.collect(), 'Sample\tMapped reads', 'fail_mapped_samples' @@ -366,7 +322,7 @@ workflow ILLUMINA { ch_bam = PRIMER_TRIM_IVAR.out.bam ch_bai = PRIMER_TRIM_IVAR.out.bai ch_ivar_trim_flagstat_multiqc = PRIMER_TRIM_IVAR.out.flagstat - ch_software_versions = ch_software_versions.mix(PRIMER_TRIM_IVAR.out.ivar_version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(PRIMER_TRIM_IVAR.out.versions) } // @@ -380,7 +336,7 @@ workflow ILLUMINA { ch_bam = MARK_DUPLICATES_PICARD.out.bam ch_bai = MARK_DUPLICATES_PICARD.out.bai ch_markduplicates_flagstat_multiqc = MARK_DUPLICATES_PICARD.out.flagstat - ch_software_versions = ch_software_versions.mix(MARK_DUPLICATES_PICARD.out.picard_version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(MARK_DUPLICATES_PICARD.out.versions) } // @@ -391,7 +347,7 @@ workflow ILLUMINA { ch_bam, PREPARE_GENOME.out.fasta ) - ch_software_versions = ch_software_versions.mix(PICARD_COLLECTMULTIPLEMETRICS.out.version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(PICARD_COLLECTMULTIPLEMETRICS.out.versions.first().ifEmpty(null)) } // @@ -406,12 +362,13 @@ workflow ILLUMINA { ch_dummy_file, 200 ) - ch_mosdepth_multiqc = MOSDEPTH_GENOME.out.global_txt - ch_software_versions = ch_software_versions.mix(MOSDEPTH_GENOME.out.version.first().ifEmpty(null)) + ch_mosdepth_multiqc = MOSDEPTH_GENOME.out.global_txt + ch_versions = ch_versions.mix(MOSDEPTH_GENOME.out.versions.first().ifEmpty(null)) PLOT_MOSDEPTH_REGIONS_GENOME ( MOSDEPTH_GENOME.out.regions_bed.collect { it[1] } ) + ch_versions = ch_versions.mix(PLOT_MOSDEPTH_REGIONS_GENOME.out.versions) if (params.protocol == 'amplicon') { MOSDEPTH_AMPLICON ( @@ -419,11 +376,13 @@ workflow ILLUMINA { PREPARE_GENOME.out.primer_collapsed_bed, 0 ) + ch_versions = ch_versions.mix(MOSDEPTH_AMPLICON.out.versions.first().ifEmpty(null)) PLOT_MOSDEPTH_REGIONS_AMPLICON ( MOSDEPTH_AMPLICON.out.regions_bed.collect { it[1] } ) ch_amplicon_heatmap_multiqc = PLOT_MOSDEPTH_REGIONS_AMPLICON.out.heatmap_tsv + ch_versions = ch_versions.mix(PLOT_MOSDEPTH_REGIONS_AMPLICON.out.versions) } } @@ -457,15 +416,7 @@ workflow ILLUMINA { ch_ivar_quast_multiqc = VARIANTS_IVAR.out.quast_tsv ch_ivar_pangolin_multiqc = VARIANTS_IVAR.out.pangolin_report ch_ivar_nextclade_report = VARIANTS_IVAR.out.nextclade_report - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.ivar_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.tabix_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.bcftools_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.quast_version.ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.snpeff_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.snpsift_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.pangolin_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.nextclade_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_IVAR.out.asciigenome_version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(VARIANTS_IVAR.out.versions) // // MODULE: Get Nextclade clade information for MultiQC report @@ -477,7 +428,7 @@ workflow ILLUMINA { } .set { ch_ivar_nextclade_multiqc } - MULTIQC_CUSTOM_TSV_IVAR_NEXTCLADE ( + MULTIQC_TSV_IVAR_NEXTCLADE ( ch_ivar_nextclade_multiqc.collect(), 'Sample\tclade', 'ivar_nextclade_clade' @@ -512,14 +463,7 @@ workflow ILLUMINA { ch_bcftools_quast_multiqc = VARIANTS_BCFTOOLS.out.quast_tsv ch_bcftools_pangolin_multiqc = VARIANTS_BCFTOOLS.out.pangolin_report ch_bcftools_nextclade_report = VARIANTS_BCFTOOLS.out.nextclade_report - ch_software_versions = ch_software_versions.mix(VARIANTS_BCFTOOLS.out.bcftools_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_BCFTOOLS.out.bedtools_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_BCFTOOLS.out.quast_version.ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_BCFTOOLS.out.snpeff_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_BCFTOOLS.out.snpsift_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_BCFTOOLS.out.pangolin_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_BCFTOOLS.out.nextclade_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(VARIANTS_BCFTOOLS.out.asciigenome_version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(VARIANTS_BCFTOOLS.out.versions) // // MODULE: Get Nextclade clade information for MultiQC report @@ -531,7 +475,7 @@ workflow ILLUMINA { } .set { ch_bcftools_nextclade_multiqc } - MULTIQC_CUSTOM_TSV_BCFTOOLS_NEXTCLADE ( + MULTIQC_TSV_BCFTOOLS_NEXTCLADE ( ch_bcftools_nextclade_multiqc.collect(), 'Sample\tclade', 'bcftools_nextclade_clade' @@ -549,6 +493,7 @@ workflow ILLUMINA { .join(ch_bcftools_vcf, by: [0]) .join(ch_bcftools_tbi, by: [0]) ) + ch_versions = ch_versions.mix(BCFTOOLS_ISEC.out.versions) } // @@ -560,93 +505,72 @@ workflow ILLUMINA { ch_assembly_fastq, PREPARE_GENOME.out.primer_fasta ) - ch_assembly_fastq = CUTADAPT.out.reads - ch_cutadapt_multiqc = CUTADAPT.out.log - ch_software_versions = ch_software_versions.mix(CUTADAPT.out.version.first().ifEmpty(null)) + ch_assembly_fastq = CUTADAPT.out.reads + ch_cutadapt_multiqc = CUTADAPT.out.log + ch_versions = ch_versions.mix(CUTADAPT.out.versions.first().ifEmpty(null)) if (!params.skip_fastqc) { FASTQC ( CUTADAPT.out.reads ) + ch_versions = ch_versions.mix(FASTQC.out.versions.first().ifEmpty(null)) } } - // - // SUBWORKFLOW: Run SPAdes assembly and downstream analysis - // + // // + // // SUBWORKFLOW: Run SPAdes assembly and downstream analysis + // // ch_spades_quast_multiqc = Channel.empty() - if (!params.skip_assembly && 'spades' in assemblers) { - ASSEMBLY_SPADES ( - ch_assembly_fastq, - ch_spades_hmm, - PREPARE_GENOME.out.fasta, - PREPARE_GENOME.out.gff, - PREPARE_GENOME.out.blast_db, - ch_blast_outfmt6_header - ) - ch_spades_quast_multiqc = ASSEMBLY_SPADES.out.quast_tsv - ch_software_versions = ch_software_versions.mix(ASSEMBLY_SPADES.out.spades_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_SPADES.out.bandage_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_SPADES.out.blast_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_SPADES.out.quast_version.ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_SPADES.out.abacas_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_SPADES.out.plasmidid_version.first().ifEmpty(null)) - } - - // - // SUBWORKFLOW: Run Unicycler assembly and downstream analysis - // + // if (!params.skip_assembly && 'spades' in assemblers) { + // ASSEMBLY_SPADES ( + // ch_assembly_fastq, + // ch_spades_hmm, + // PREPARE_GENOME.out.fasta, + // PREPARE_GENOME.out.gff, + // PREPARE_GENOME.out.blast_db, + // ch_blast_outfmt6_header + // ) + // ch_spades_quast_multiqc = ASSEMBLY_SPADES.out.quast_tsv + // ch_versions = ch_versions.mix(ASSEMBLY_SPADES.out.versions) + // } + + // // + // // SUBWORKFLOW: Run Unicycler assembly and downstream analysis + // // ch_unicycler_quast_multiqc = Channel.empty() - if (!params.skip_assembly && 'unicycler' in assemblers) { - ASSEMBLY_UNICYCLER ( - ch_assembly_fastq, - PREPARE_GENOME.out.fasta, - PREPARE_GENOME.out.gff, - PREPARE_GENOME.out.blast_db, - ch_blast_outfmt6_header - ) - ch_unicycler_quast_multiqc = ASSEMBLY_UNICYCLER.out.quast_tsv - ch_software_versions = ch_software_versions.mix(ASSEMBLY_UNICYCLER.out.unicycler_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_UNICYCLER.out.bandage_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_UNICYCLER.out.blast_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_UNICYCLER.out.quast_version.ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_UNICYCLER.out.abacas_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_UNICYCLER.out.plasmidid_version.first().ifEmpty(null)) - } - - // - // SUBWORKFLOW: Run minia assembly and downstream analysis - // + // if (!params.skip_assembly && 'unicycler' in assemblers) { + // ASSEMBLY_UNICYCLER ( + // ch_assembly_fastq, + // PREPARE_GENOME.out.fasta, + // PREPARE_GENOME.out.gff, + // PREPARE_GENOME.out.blast_db, + // ch_blast_outfmt6_header + // ) + // ch_unicycler_quast_multiqc = ASSEMBLY_UNICYCLER.out.quast_tsv + // ch_versions = ch_versions.mix(ASSEMBLY_UNICYCLER.out.versions) + // } + + // // + // // SUBWORKFLOW: Run minia assembly and downstream analysis + // // ch_minia_quast_multiqc = Channel.empty() - if (!params.skip_assembly && 'minia' in assemblers) { - ASSEMBLY_MINIA ( - ch_assembly_fastq, - PREPARE_GENOME.out.fasta, - PREPARE_GENOME.out.gff, - PREPARE_GENOME.out.blast_db, - ch_blast_outfmt6_header - ) - ch_minia_quast_multiqc = ASSEMBLY_MINIA.out.quast_tsv - ch_software_versions = ch_software_versions.mix(ASSEMBLY_MINIA.out.minia_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_MINIA.out.blast_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_MINIA.out.quast_version.ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_MINIA.out.abacas_version.first().ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(ASSEMBLY_MINIA.out.plasmidid_version.first().ifEmpty(null)) - } + // if (!params.skip_assembly && 'minia' in assemblers) { + // ASSEMBLY_MINIA ( + // ch_assembly_fastq, + // PREPARE_GENOME.out.fasta, + // PREPARE_GENOME.out.gff, + // PREPARE_GENOME.out.blast_db, + // ch_blast_outfmt6_header + // ) + // ch_minia_quast_multiqc = ASSEMBLY_MINIA.out.quast_tsv + // ch_versions = ch_versions.mix(ASSEMBLY_MINIA.out.versions) + // } // // MODULE: Pipeline reporting // - ch_software_versions - .map { it -> if (it) [ it.baseName, it ] } - .groupTuple() - .map { it[1][0] } - .flatten() - .collect() - .set { ch_software_versions } - - GET_SOFTWARE_VERSIONS ( - ch_software_versions + CUSTOM_DUMPSOFTWAREVERSIONS ( + ch_versions.unique().collectFile(name: 'collated_versions.yml') ) // @@ -659,7 +583,7 @@ workflow ILLUMINA { MULTIQC ( ch_multiqc_config, ch_multiqc_custom_config.collect().ifEmpty([]), - GET_SOFTWARE_VERSIONS.out.yaml.collect(), + CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect(), ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml'), ch_fail_reads_multiqc.ifEmpty([]), ch_fail_mapping_multiqc.ifEmpty([]), diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 5ebcfe19..9a5e00b9 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -5,8 +5,8 @@ */ def valid_params = [ - artic_minion_caller : ['nanopolish', 'medaka'], - artic_minion_aligner : ['minimap2', 'bwa'] + artic_minion_caller : ['nanopolish', 'medaka'], + artic_minion_aligner : ['minimap2', 'bwa'] ] def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) @@ -23,10 +23,6 @@ for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true // Stage dummy file to be used as an optional input where required ch_dummy_file = file("$projectDir/assets/dummy_file.txt", checkIfExists: true) -// MultiQC config files -ch_multiqc_config = file("$projectDir/assets/multiqc_config_nanopore.yaml", checkIfExists: true) -ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config) : Channel.empty() - if (params.input) { ch_input = file(params.input) } if (params.fast5_dir) { ch_fast5_dir = file(params.fast5_dir) } else { ch_fast5_dir = ch_dummy_file } if (params.sequencing_summary) { ch_sequencing_summary = file(params.sequencing_summary) } else { ch_sequencing_summary = ch_multiqc_config } @@ -41,42 +37,38 @@ if (params.artic_minion_caller == 'medaka') { /* ======================================================================================== - IMPORT LOCAL MODULES/SUBWORKFLOWS + CONFIG FILES ======================================================================================== */ -// Don't overwrite global params.modules, create a copy instead and use that within the main script. -def modules = params.modules.clone() - -def multiqc_options = modules['nanopore_multiqc'] -multiqc_options.args += params.multiqc_title ? Utils.joinModuleArgs(["--title \"$params.multiqc_title\""]) : '' +ch_multiqc_config = file("$projectDir/assets/multiqc_config_nanopore.yaml", checkIfExists: true) +ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config) : Channel.empty() -include { ASCIIGENOME } from '../modules/local/asciigenome' addParams( options: modules['nanopore_asciigenome'] ) -include { GET_SOFTWARE_VERSIONS } from '../modules/local/get_software_versions' addParams( options: [publish_files: ['tsv':'']] ) -include { MULTIQC } from '../modules/local/multiqc_nanopore' addParams( options: multiqc_options ) +/* +======================================================================================== + IMPORT LOCAL MODULES/SUBWORKFLOWS +======================================================================================== +*/ -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_NO_SAMPLE_NAME } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_NO_BARCODES } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_BARCODE_COUNT } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_GUPPYPLEX_COUNT } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) -include { MULTIQC_CUSTOM_TSV_FROM_STRING as MULTIQC_CUSTOM_TSV_NEXTCLADE } from '../modules/local/multiqc_custom_tsv_from_string' addParams( options: [publish_files: false] ) -include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_GENOME } from '../modules/local/plot_mosdepth_regions' addParams( options: modules['nanopore_plot_mosdepth_regions_genome'] ) -include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_AMPLICON } from '../modules/local/plot_mosdepth_regions' addParams( options: modules['nanopore_plot_mosdepth_regions_amplicon'] ) +// +// MODULE: Loaded from modules/local/ +// +include { ASCIIGENOME } from '../modules/local/asciigenome' +include { MULTIQC } from '../modules/local/multiqc_nanopore' +include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_GENOME } from '../modules/local/plot_mosdepth_regions' +include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_AMPLICON } from '../modules/local/plot_mosdepth_regions' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NO_SAMPLE_NAME } from '../modules/local/multiqc_tsv_from_list' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NO_BARCODES } from '../modules/local/multiqc_tsv_from_list' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_BARCODE_COUNT } from '../modules/local/multiqc_tsv_from_list' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_GUPPYPLEX_COUNT } from '../modules/local/multiqc_tsv_from_list' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NEXTCLADE } from '../modules/local/multiqc_tsv_from_list' // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -def publish_genome_options = params.save_reference ? [publish_dir: 'genome'] : [publish_files: false] -def collapse_primers_options = modules['nanopore_collapse_primers'] -def snpeff_build_options = modules['nanopore_snpeff_build'] -if (!params.save_reference) { - collapse_primers_options['publish_files'] = false - snpeff_build_options['publish_files'] = false -} - -include { INPUT_CHECK } from '../subworkflows/local/input_check' addParams( options: [:] ) -include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_nanopore' addParams( genome_options: publish_genome_options, collapse_primers_options: collapse_primers_options, snpeff_build_options: snpeff_build_options ) -include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' addParams( snpeff_options: modules['nanopore_snpeff'], snpsift_options: modules['nanopore_snpsift'], bgzip_options: modules['nanopore_snpeff_bgzip'], tabix_options: modules['nanopore_snpeff_tabix'], stats_options: modules['nanopore_snpeff_stats'] ) +include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_nanopore' +include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' /* ======================================================================================== @@ -87,40 +79,22 @@ include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' // // MODULE: Installed directly from nf-core/modules // - -def artic_minion_options = modules['nanopore_artic_minion'] -artic_minion_options.args += params.artic_minion_caller == 'medaka' ? Utils.joinModuleArgs(['--medaka']) : '' -artic_minion_options.args += params.artic_minion_aligner == 'bwa' ? Utils.joinModuleArgs(['--bwa']) : Utils.joinModuleArgs(['--minimap2']) - -def artic_guppyplex_options = modules['nanopore_artic_guppyplex'] -if (params.primer_set_version == 1200) { - def args_split = artic_guppyplex_options.args.tokenize() - def min_idx = args_split.indexOf('--min-length') - def max_idx = args_split.indexOf('--max-length') - if (min_idx != -1) { - args_split[min_idx+1] = '250' - } - if (max_idx != -1) { - args_split[max_idx+1] = '1500' - } - artic_guppyplex_options.args = args_split.join(' ') -} - -include { PYCOQC } from '../modules/nf-core/modules/pycoqc/main' addParams( options: modules['nanopore_pycoqc'] ) -include { NANOPLOT } from '../modules/nf-core/modules/nanoplot/main' addParams( options: modules['nanopore_nanoplot'] ) -include { ARTIC_GUPPYPLEX } from '../modules/nf-core/modules/artic/guppyplex/main' addParams( options: artic_guppyplex_options ) -include { ARTIC_MINION } from '../modules/nf-core/modules/artic/minion/main' addParams( options: artic_minion_options ) -include { BCFTOOLS_STATS } from '../modules/nf-core/modules/bcftools/stats/main' addParams( options: modules['nanopore_bcftools_stats'] ) -include { QUAST } from '../modules/nf-core/modules/quast/main' addParams( options: modules['nanopore_quast'] ) -include { PANGOLIN } from '../modules/nf-core/modules/pangolin/main' addParams( options: modules['nanopore_pangolin'] ) -include { NEXTCLADE } from '../modules/nf-core/modules/nextclade/main' addParams( options: modules['nanopore_nextclade'] ) -include { MOSDEPTH as MOSDEPTH_GENOME } from '../modules/nf-core/modules/mosdepth/main' addParams( options: modules['nanopore_mosdepth_genome'] ) -include { MOSDEPTH as MOSDEPTH_AMPLICON } from '../modules/nf-core/modules/mosdepth/main' addParams( options: modules['nanopore_mosdepth_amplicon'] ) +include { PYCOQC } from '../modules/nf-core/modules/pycoqc/main' +include { NANOPLOT } from '../modules/nf-core/modules/nanoplot/main' +include { ARTIC_GUPPYPLEX } from '../modules/nf-core/modules/artic/guppyplex/main' +include { ARTIC_MINION } from '../modules/nf-core/modules/artic/minion/main' +include { BCFTOOLS_STATS } from '../modules/nf-core/modules/bcftools/stats/main' +include { QUAST } from '../modules/nf-core/modules/quast/main' +include { PANGOLIN } from '../modules/nf-core/modules/pangolin/main' +include { NEXTCLADE } from '../modules/nf-core/modules/nextclade/main' +include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' +include { MOSDEPTH as MOSDEPTH_GENOME } from '../modules/nf-core/modules/mosdepth/main' +include { MOSDEPTH as MOSDEPTH_AMPLICON } from '../modules/nf-core/modules/mosdepth/main' // // SUBWORKFLOW: Consisting entirely of nf-core/modules // -include { FILTER_BAM_SAMTOOLS } from '../subworkflows/nf-core/filter_bam_samtools' addParams( samtools_view_options: modules['nanopore_filter_bam'], samtools_index_options: modules['nanopore_filter_bam_stats'] ) +include { FILTER_BAM_SAMTOOLS } from '../subworkflows/nf-core/filter_bam_samtools' /* ======================================================================================== @@ -135,7 +109,7 @@ def fail_barcode_reads = [:] workflow NANOPORE { - ch_software_versions = Channel.empty() + ch_versions = Channel.empty() // // MODULE: PycoQC on sequencing summary file @@ -145,8 +119,8 @@ workflow NANOPORE { PYCOQC ( ch_sequencing_summary ) - ch_pycoqc_multiqc = PYCOQC.out.json - ch_software_versions = ch_software_versions.mix(PYCOQC.out.version.ifEmpty(null)) + ch_pycoqc_multiqc = PYCOQC.out.json + ch_versions = ch_versions.mix(PYCOQC.out.versions) } // @@ -155,6 +129,7 @@ workflow NANOPORE { PREPARE_GENOME ( ch_dummy_file ) + ch_versions = ch_versions.mix(PREPARE_GENOME.out.versions) // Check primer BED file only contains suffixes provided --primer_left_suffix / --primer_right_suffix PREPARE_GENOME @@ -189,8 +164,10 @@ workflow NANOPORE { ch_input, params.platform ) + .sample_info .join(ch_fastq_dirs, remainder: true) .set { ch_fastq_dirs } + ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) // // MODULE: Create custom content file for MultiQC to report barcodes were allocated reads >= params.min_barcode_reads but no sample name in samplesheet @@ -201,12 +178,12 @@ workflow NANOPORE { .map { it -> [ "${it[0]}\t${it[-1]}" ] } .set { ch_barcodes_no_sample } - MULTIQC_CUSTOM_TSV_NO_SAMPLE_NAME ( + MULTIQC_TSV_NO_SAMPLE_NAME ( ch_barcodes_no_sample.collect(), 'Barcode\tRead count', 'fail_barcodes_no_sample' ) - ch_custom_no_sample_name_multiqc = MULTIQC_CUSTOM_TSV_NO_SAMPLE_NAME.out + .set { ch_custom_no_sample_name_multiqc } // // MODULE: Create custom content file for MultiQC to report samples that were in samplesheet but have no barcodes @@ -216,12 +193,12 @@ workflow NANOPORE { .map { it -> [ "${it[1]}\t${it[0]}" ] } .set { ch_samples_no_barcode } - MULTIQC_CUSTOM_TSV_NO_BARCODES ( + MULTIQC_TSV_NO_BARCODES ( ch_samples_no_barcode.collect(), 'Sample\tMissing barcode', 'fail_no_barcode_samples' ) - ch_custom_no_barcodes_multiqc = MULTIQC_CUSTOM_TSV_NO_BARCODES.out + .set { ch_custom_no_barcodes_multiqc } ch_fastq_dirs .filter { (it[1] != null) } @@ -257,7 +234,7 @@ workflow NANOPORE { } .set { ch_pass_fail_barcode_count } - MULTIQC_CUSTOM_TSV_BARCODE_COUNT ( + MULTIQC_TSV_BARCODE_COUNT ( ch_pass_fail_barcode_count.fail.collect(), 'Sample\tBarcode count', 'fail_barcode_count_samples' @@ -275,7 +252,7 @@ workflow NANOPORE { ARTIC_GUPPYPLEX ( ch_fastq_dirs ) - ch_software_versions = ch_software_versions.mix(ARTIC_GUPPYPLEX.out.version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(ARTIC_GUPPYPLEX.out.versions.first().ifEmpty(null)) // // MODULE: Create custom content file for MultiQC to report samples with reads < params.min_guppyplex_reads @@ -292,7 +269,7 @@ workflow NANOPORE { } .set { ch_pass_fail_guppyplex_count } - MULTIQC_CUSTOM_TSV_GUPPYPLEX_COUNT ( + MULTIQC_TSV_GUPPYPLEX_COUNT ( ch_pass_fail_guppyplex_count.fail.collect(), 'Sample\tRead count', 'fail_guppyplex_count_samples' @@ -305,7 +282,7 @@ workflow NANOPORE { NANOPLOT ( ARTIC_GUPPYPLEX.out.fastq ) - ch_software_versions = ch_software_versions.mix(NANOPLOT.out.version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(NANOPLOT.out.versions.first().ifEmpty(null)) } // @@ -321,6 +298,7 @@ workflow NANOPORE { params.artic_scheme, params.primer_set_version ) + ch_versions = ch_versions.mix(ARTIC_MINION.out.versions.first().ifEmpty(null)) // // SUBWORKFLOW: Filter unmapped reads from BAM @@ -328,7 +306,7 @@ workflow NANOPORE { FILTER_BAM_SAMTOOLS ( ARTIC_MINION.out.bam ) - ch_software_versions = ch_software_versions.mix(FILTER_BAM_SAMTOOLS.out.samtools_version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(FILTER_BAM_SAMTOOLS.out.versions) // // MODULE: VCF stats with bcftools stats @@ -336,7 +314,7 @@ workflow NANOPORE { BCFTOOLS_STATS ( ARTIC_MINION.out.vcf ) - ch_software_versions = ch_software_versions.mix(BCFTOOLS_STATS.out.version.ifEmpty(null)) + ch_versions = ch_versions.mix(BCFTOOLS_STATS.out.versions.first().ifEmpty(null)) // // MODULE: Genome-wide and amplicon-specific coverage QC plots @@ -351,22 +329,25 @@ workflow NANOPORE { 200 ) ch_mosdepth_multiqc = MOSDEPTH_GENOME.out.global_txt - ch_software_versions = ch_software_versions.mix(MOSDEPTH_GENOME.out.version.first().ifEmpty(null)) + ch_versions = ch_versions.mix(MOSDEPTH_GENOME.out.versions.first().ifEmpty(null)) PLOT_MOSDEPTH_REGIONS_GENOME ( MOSDEPTH_GENOME.out.regions_bed.collect { it[1] } ) + ch_versions = ch_versions.mix(PLOT_MOSDEPTH_REGIONS_GENOME.out.versions) MOSDEPTH_AMPLICON ( ARTIC_MINION.out.bam_primertrimmed.join(ARTIC_MINION.out.bai_primertrimmed, by: [0]), PREPARE_GENOME.out.primer_collapsed_bed, 0 ) + ch_versions = ch_versions.mix(MOSDEPTH_AMPLICON.out.versions.first().ifEmpty(null)) PLOT_MOSDEPTH_REGIONS_AMPLICON ( MOSDEPTH_AMPLICON.out.regions_bed.collect { it[1] } ) ch_amplicon_heatmap_multiqc = PLOT_MOSDEPTH_REGIONS_AMPLICON.out.heatmap_tsv + ch_versions = ch_versions.mix(PLOT_MOSDEPTH_REGIONS_AMPLICON.out.versions) } // @@ -378,7 +359,7 @@ workflow NANOPORE { ARTIC_MINION.out.fasta ) ch_pangolin_multiqc = PANGOLIN.out.report - ch_software_versions = ch_software_versions.mix(PANGOLIN.out.version.ifEmpty(null)) + ch_versions = ch_versions.mix(PANGOLIN.out.versions.first().ifEmpty(null)) } // @@ -389,7 +370,7 @@ workflow NANOPORE { NEXTCLADE ( ARTIC_MINION.out.fasta ) - ch_software_versions = ch_software_versions.mix(NEXTCLADE.out.version.ifEmpty(null)) + ch_versions = ch_versions.mix(NEXTCLADE.out.versions.first().ifEmpty(null)) // // MODULE: Get Nextclade clade information for MultiQC report @@ -403,7 +384,7 @@ workflow NANOPORE { } .set { ch_nextclade_multiqc } - MULTIQC_CUSTOM_TSV_NEXTCLADE ( + MULTIQC_TSV_NEXTCLADE ( ch_nextclade_multiqc.collect(), 'Sample\tclade', 'nextclade_clade' @@ -424,7 +405,7 @@ workflow NANOPORE { params.gff ) ch_quast_multiqc = QUAST.out.tsv - ch_software_versions = ch_software_versions.mix(QUAST.out.version.ifEmpty(null)) + ch_versions = ch_versions.mix(QUAST.out.versions) } // @@ -439,8 +420,7 @@ workflow NANOPORE { PREPARE_GENOME.out.fasta ) ch_snpeff_multiqc = SNPEFF_SNPSIFT.out.csv - ch_software_versions = ch_software_versions.mix(SNPEFF_SNPSIFT.out.snpeff_version.ifEmpty(null)) - ch_software_versions = ch_software_versions.mix(SNPEFF_SNPSIFT.out.snpsift_version.ifEmpty(null)) + ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) } // @@ -468,22 +448,14 @@ workflow NANOPORE { params.asciigenome_window_size, params.asciigenome_read_depth ) - ch_software_versions = ch_software_versions.mix(ASCIIGENOME.out.version.ifEmpty(null)) + ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first().ifEmpty(null)) } // // MODULE: Pipeline reporting // - ch_software_versions - .map { it -> if (it) [ it.baseName, it ] } - .groupTuple() - .map { it[1][0] } - .flatten() - .collect() - .set { ch_software_versions } - - GET_SOFTWARE_VERSIONS ( - ch_software_versions + CUSTOM_DUMPSOFTWAREVERSIONS ( + ch_versions.unique().collectFile(name: 'collated_versions.yml') ) // @@ -496,12 +468,12 @@ workflow NANOPORE { MULTIQC ( ch_multiqc_config, ch_multiqc_custom_config.collect().ifEmpty([]), - GET_SOFTWARE_VERSIONS.out.yaml.collect(), + CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect(), ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml'), ch_custom_no_sample_name_multiqc.ifEmpty([]), ch_custom_no_barcodes_multiqc.ifEmpty([]), - MULTIQC_CUSTOM_TSV_BARCODE_COUNT.out.ifEmpty([]), - MULTIQC_CUSTOM_TSV_GUPPYPLEX_COUNT.out.ifEmpty([]), + MULTIQC_TSV_BARCODE_COUNT.out.ifEmpty([]), + MULTIQC_TSV_GUPPYPLEX_COUNT.out.ifEmpty([]), ch_amplicon_heatmap_multiqc.ifEmpty([]), ch_pycoqc_multiqc.collect().ifEmpty([]), ARTIC_MINION.out.json.collect{it[1]}.ifEmpty([]), From 00b802bf1b6adcb411e9f57c08de455fb480a9e2 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 6 Jan 2022 18:36:22 +0000 Subject: [PATCH 004/238] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0f13c7f..2914ed33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Enhancements & fixes +* Port pipeline to the updated Nextflow DSL2 syntax adopted on nf-core/modules +* Bump minimum Nextflow version from `21.04.0` -> `21.10.3` +* Updated pipeline template to [nf-core/tools 2.2](https://github.com/nf-core/tools/releases/tag/2.2) + ### Parameters ## [[2.2](https://github.com/nf-core/rnaseq/releases/tag/2.2)] - 2021-07-29 From ce9348ee529969993a5cdbd6823ac4f16802ab91 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 6 Jan 2022 23:15:35 +0000 Subject: [PATCH 005/238] Add ext.args for artic minion --- conf/modules.config | 7 +++++++ workflows/nanopore.nf | 13 ++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 4e724fc7..cd8a5d9f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -135,5 +135,12 @@ if (params.platform == 'nanopore') { withName: SAMTOOLS_VIEW { ext.args = '-b -F 4' } + + withName: ARTIC_MINION { + ext.args = [ + params.artic_minion_caller == 'medaka' ? '--medaka' : '', + params.artic_minion_aligner == 'bwa' ? '--bwa' : '--minimap2' + ].join(' ').trim() + } } } diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 9a5e00b9..73f8ef58 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -23,6 +23,10 @@ for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true // Stage dummy file to be used as an optional input where required ch_dummy_file = file("$projectDir/assets/dummy_file.txt", checkIfExists: true) +// MultiQC config files +ch_multiqc_config = file("$projectDir/assets/multiqc_config_nanopore.yaml", checkIfExists: true) +ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config) : Channel.empty() + if (params.input) { ch_input = file(params.input) } if (params.fast5_dir) { ch_fast5_dir = file(params.fast5_dir) } else { ch_fast5_dir = ch_dummy_file } if (params.sequencing_summary) { ch_sequencing_summary = file(params.sequencing_summary) } else { ch_sequencing_summary = ch_multiqc_config } @@ -35,15 +39,6 @@ if (params.artic_minion_caller == 'medaka') { } } -/* -======================================================================================== - CONFIG FILES -======================================================================================== -*/ - -ch_multiqc_config = file("$projectDir/assets/multiqc_config_nanopore.yaml", checkIfExists: true) -ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config) : Channel.empty() - /* ======================================================================================== IMPORT LOCAL MODULES/SUBWORKFLOWS From 091702a4d65e467fe7db58bb10d1aa47beb14d97 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 7 Jan 2022 11:33:45 +0000 Subject: [PATCH 006/238] Add enums to schema where appropriate --- nextflow_schema.json | 45 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index a79b042d..11c894cf 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -24,12 +24,20 @@ "platform": { "type": "string", "fa_icon": "fas fa-hdd", - "description": "NGS platform used to sequence the samples" + "description": "NGS platform used to sequence the samples.", + "enum": [ + "illumina", + "nanopore" + ] }, "protocol": { "type": "string", - "description": "Specifies the type of protocol used for sequencing i.e. 'metagenomic' or 'amplicon'.", - "fa_icon": "fas fa-vials" + "description": "Specifies the type of protocol used for sequencing.", + "fa_icon": "fas fa-vials", + "enum": [ + "metagenomic", + "amplicon" + ] }, "outdir": { "type": "string", @@ -176,14 +184,22 @@ "artic_minion_caller": { "type": "string", "default": "nanopolish", - "description": "Variant caller used when running artic minion. Available options are 'medaka' and 'nanopolish' (default).", - "fa_icon": "fas fa-phone-volume" + "description": "Variant caller used when running artic minion (default: 'nanopolish').", + "fa_icon": "fas fa-phone-volume", + "enum": [ + "nanopolish", + "medaka" + ] }, "artic_minion_aligner": { "type": "string", "default": "minimap2", - "description": "Aligner used when running artic minion. Available options are 'bwa' and 'minimap2' (default).", - "fa_icon": "fas fa-map-signs" + "description": "Aligner used when running artic minion (default: 'minimap2').", + "fa_icon": "fas fa-map-signs", + "enum": [ + "minimap2", + "bwa" + ] }, "artic_scheme": { "type": "string", @@ -434,7 +450,18 @@ "type": "string", "default": "rnaviral", "fa_icon": "fab fa-digg", - "description": "Specify the SPAdes mode you would like to run. Supported options are 'rnaviral', 'corona', 'metaviral', 'meta', 'metaplasmid', 'plasmid', 'isolate', 'rna', 'bio'." + "description": "Specify the SPAdes mode you would like to run (default: 'rnaviral').", + "enum": [ + "rnaviral", + "corona", + "metaviral", + "meta", + "metaplasmid", + "plasmid", + "isolate", + "rna", + "bio" + ] }, "spades_hmm": { "type": "string", @@ -664,4 +691,4 @@ "$ref": "#/definitions/institutional_config_options" } ] -} +} \ No newline at end of file From 399a4738b9efcf4475231f8bd6ca972d5420548f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 7 Jan 2022 17:53:18 +0000 Subject: [PATCH 007/238] Bump software versions for local modules --- modules/local/bcftools_isec.nf | 6 +++--- modules/local/cutadapt.nf | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/local/bcftools_isec.nf b/modules/local/bcftools_isec.nf index da4cc2d8..a9ce5ea8 100644 --- a/modules/local/bcftools_isec.nf +++ b/modules/local/bcftools_isec.nf @@ -2,10 +2,10 @@ process BCFTOOLS_ISEC { tag "$meta.id" label 'process_low' - conda (params.enable_conda ? 'bioconda::bcftools=1.13' : null) + conda (params.enable_conda ? 'bioconda::bcftools=1.14' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/bcftools:1.13--h3a49de5_0' : - 'quay.io/biocontainers/bcftools:1.13--h3a49de5_0' }" + 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0' : + 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" input: tuple val(meta), path('ivar/*'), path('ivar/*'), path('bcftools/*'), path('bcftools/*') diff --git a/modules/local/cutadapt.nf b/modules/local/cutadapt.nf index a6bf3af3..76a824e7 100644 --- a/modules/local/cutadapt.nf +++ b/modules/local/cutadapt.nf @@ -2,10 +2,10 @@ process CUTADAPT { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? 'bioconda::cutadapt=3.4' : null) + conda (params.enable_conda ? 'bioconda::cutadapt=3.5' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/cutadapt:3.4--py39h38f01e4_1' : - 'quay.io/biocontainers/cutadapt:3.4--py39h38f01e4_1' }" + 'https://depot.galaxyproject.org/singularity/cutadapt:3.5--py39h38f01e4_0' : + 'quay.io/biocontainers/cutadapt:3.5--py39h38f01e4_0' }" input: tuple val(meta), path(reads) From 81724748249b2971e500586903b4950a416fe648 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 7 Jan 2022 17:57:00 +0000 Subject: [PATCH 008/238] Bump software versions for nf-core modules --- modules.json | 32 +++++++++---------- .../modules/bcftools/consensus/main.nf | 6 ++-- .../nf-core/modules/bcftools/mpileup/main.nf | 6 ++-- .../nf-core/modules/bcftools/stats/main.nf | 6 ++-- .../nf-core/modules/bowtie2/align/meta.yml | 3 -- modules/nf-core/modules/fastp/main.nf | 6 ++-- modules/nf-core/modules/minia/main.nf | 6 ++-- modules/nf-core/modules/nanoplot/main.nf | 6 ++-- modules/nf-core/modules/pangolin/main.nf | 6 ++-- .../picard/collectmultiplemetrics/main.nf | 6 ++-- .../modules/picard/markduplicates/main.nf | 6 ++-- .../nf-core/modules/samtools/flagstat/main.nf | 7 +++- .../nf-core/modules/samtools/idxstats/main.nf | 6 +++- .../nf-core/modules/samtools/index/main.nf | 6 +++- modules/nf-core/modules/samtools/sort/main.nf | 1 + .../nf-core/modules/samtools/stats/main.nf | 7 +++- modules/nf-core/modules/samtools/view/main.nf | 9 +++++- 17 files changed, 74 insertions(+), 51 deletions(-) diff --git a/modules.json b/modules.json index 51a5b27f..de6d718f 100644 --- a/modules.json +++ b/modules.json @@ -16,13 +16,13 @@ "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bcftools/consensus": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "bcftools/mpileup": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "bcftools/stats": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "bedtools/genomecov": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" @@ -43,7 +43,7 @@ "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "bowtie2/align": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "bowtie2/build": { "git_sha": "e3285528aca2733ff2d544cb5e5fcc34599226f3" @@ -58,7 +58,7 @@ "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "fastp": { - "git_sha": "cd94731789aa516631e5ea11e0f49469f2ba82dd" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "fastqc": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" @@ -79,25 +79,25 @@ "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "minia": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "mosdepth": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "nanoplot": { - "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "nextclade": { "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" }, "pangolin": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "picard/collectmultiplemetrics": { - "git_sha": "0d1e21686a586447b7592e40da9b3a7cdeedf03c" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "picard/markduplicates": { - "git_sha": "0d1e21686a586447b7592e40da9b3a7cdeedf03c" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "plasmidid": { "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" @@ -109,25 +109,25 @@ "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" }, "samtools/flagstat": { - "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "samtools/idxstats": { - "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "samtools/index": { - "git_sha": "0fafaeebf52cc5ab554b83297ed02a48d852a848" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "samtools/mpileup": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "samtools/sort": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "samtools/stats": { - "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "samtools/view": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "spades": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" diff --git a/modules/nf-core/modules/bcftools/consensus/main.nf b/modules/nf-core/modules/bcftools/consensus/main.nf index 4633790e..5d7cd74f 100644 --- a/modules/nf-core/modules/bcftools/consensus/main.nf +++ b/modules/nf-core/modules/bcftools/consensus/main.nf @@ -2,10 +2,10 @@ process BCFTOOLS_CONSENSUS { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? 'bioconda::bcftools=1.13' : null) + conda (params.enable_conda ? 'bioconda::bcftools=1.14' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/bcftools:1.13--h3a49de5_0' : - 'quay.io/biocontainers/bcftools:1.13--h3a49de5_0' }" + 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0' : + 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" input: tuple val(meta), path(vcf), path(tbi), path(fasta) diff --git a/modules/nf-core/modules/bcftools/mpileup/main.nf b/modules/nf-core/modules/bcftools/mpileup/main.nf index 8a209a66..3583aac4 100644 --- a/modules/nf-core/modules/bcftools/mpileup/main.nf +++ b/modules/nf-core/modules/bcftools/mpileup/main.nf @@ -2,10 +2,10 @@ process BCFTOOLS_MPILEUP { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? 'bioconda::bcftools=1.13' : null) + conda (params.enable_conda ? 'bioconda::bcftools=1.14' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/bcftools:1.13--h3a49de5_0' : - 'quay.io/biocontainers/bcftools:1.13--h3a49de5_0' }" + 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0' : + 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" input: tuple val(meta), path(bam) diff --git a/modules/nf-core/modules/bcftools/stats/main.nf b/modules/nf-core/modules/bcftools/stats/main.nf index 67e8dca7..54a28bce 100644 --- a/modules/nf-core/modules/bcftools/stats/main.nf +++ b/modules/nf-core/modules/bcftools/stats/main.nf @@ -2,10 +2,10 @@ process BCFTOOLS_STATS { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? 'bioconda::bcftools=1.13' : null) + conda (params.enable_conda ? 'bioconda::bcftools=1.14' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/bcftools:1.13--h3a49de5_0' : - 'quay.io/biocontainers/bcftools:1.13--h3a49de5_0' }" + 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0' : + 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" input: tuple val(meta), path(vcf) diff --git a/modules/nf-core/modules/bowtie2/align/meta.yml b/modules/nf-core/modules/bowtie2/align/meta.yml index 2607dea5..77c9e397 100644 --- a/modules/nf-core/modules/bowtie2/align/meta.yml +++ b/modules/nf-core/modules/bowtie2/align/meta.yml @@ -29,9 +29,6 @@ input: type: file description: Bowtie2 genome index files pattern: "*.ebwt" - - save_unaligned: - type: boolean - description: Save unaligned reads to FastQ file(s) output: - bam: type: file diff --git a/modules/nf-core/modules/fastp/main.nf b/modules/nf-core/modules/fastp/main.nf index 33603842..a406036a 100644 --- a/modules/nf-core/modules/fastp/main.nf +++ b/modules/nf-core/modules/fastp/main.nf @@ -2,10 +2,10 @@ process FASTP { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? 'bioconda::fastp=0.20.1' : null) + conda (params.enable_conda ? 'bioconda::fastp=0.23.2' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/fastp:0.20.1--h8b12597_0' : - 'quay.io/biocontainers/fastp:0.20.1--h8b12597_0' }" + 'https://depot.galaxyproject.org/singularity/fastp:0.23.2--h79da9fb_0' : + 'quay.io/biocontainers/fastp:0.23.2--h79da9fb_0' }" input: tuple val(meta), path(reads) diff --git a/modules/nf-core/modules/minia/main.nf b/modules/nf-core/modules/minia/main.nf index ceff67c5..968cafa5 100644 --- a/modules/nf-core/modules/minia/main.nf +++ b/modules/nf-core/modules/minia/main.nf @@ -2,10 +2,10 @@ process MINIA { tag "$meta.id" label 'process_high' - conda (params.enable_conda ? "bioconda::minia=3.2.4" : null) + conda (params.enable_conda ? "bioconda::minia=3.2.6" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/minia:3.2.4--he513fc3_0' : - 'quay.io/biocontainers/minia:3.2.4--he513fc3_0' }" + 'https://depot.galaxyproject.org/singularity/minia:3.2.6--h9a82719_0' : + 'quay.io/biocontainers/minia:3.2.6--h9a82719_0' }" input: tuple val(meta), path(reads) diff --git a/modules/nf-core/modules/nanoplot/main.nf b/modules/nf-core/modules/nanoplot/main.nf index 36577d8a..c3fb8a37 100644 --- a/modules/nf-core/modules/nanoplot/main.nf +++ b/modules/nf-core/modules/nanoplot/main.nf @@ -2,10 +2,10 @@ process NANOPLOT { tag "$meta.id" label 'process_low' - conda (params.enable_conda ? 'bioconda::nanoplot=1.38.0' : null) + conda (params.enable_conda ? 'bioconda::nanoplot=1.39.0' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/nanoplot:1.38.0--pyhdfd78af_0' : - 'quay.io/biocontainers/nanoplot:1.38.0--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/nanoplot:1.39.0--pyhdfd78af_0' : + 'quay.io/biocontainers/nanoplot:1.39.0--pyhdfd78af_0' }" input: tuple val(meta), path(ontfile) diff --git a/modules/nf-core/modules/pangolin/main.nf b/modules/nf-core/modules/pangolin/main.nf index 5ee2b2e0..40d6d78e 100644 --- a/modules/nf-core/modules/pangolin/main.nf +++ b/modules/nf-core/modules/pangolin/main.nf @@ -2,10 +2,10 @@ process PANGOLIN { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? 'bioconda::pangolin=3.1.11' : null) + conda (params.enable_conda ? 'bioconda::pangolin=3.1.17' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pangolin:3.1.11--pyhdfd78af_1' : - 'quay.io/biocontainers/pangolin:3.1.11--pyhdfd78af_1' }" + 'https://depot.galaxyproject.org/singularity/pangolin:3.1.17--pyhdfd78af_0' : + 'quay.io/biocontainers/pangolin:3.1.17--pyhdfd78af_0' }" input: tuple val(meta), path(fasta) diff --git a/modules/nf-core/modules/picard/collectmultiplemetrics/main.nf b/modules/nf-core/modules/picard/collectmultiplemetrics/main.nf index 481761e3..9511f7a4 100644 --- a/modules/nf-core/modules/picard/collectmultiplemetrics/main.nf +++ b/modules/nf-core/modules/picard/collectmultiplemetrics/main.nf @@ -2,10 +2,10 @@ process PICARD_COLLECTMULTIPLEMETRICS { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? "bioconda::picard=2.26.7" : null) + conda (params.enable_conda ? "bioconda::picard=2.26.10" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/picard:2.26.7--hdfd78af_0' : - 'quay.io/biocontainers/picard:2.26.7--hdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/picard:2.26.10--hdfd78af_0' : + 'quay.io/biocontainers/picard:2.26.10--hdfd78af_0' }" input: tuple val(meta), path(bam) diff --git a/modules/nf-core/modules/picard/markduplicates/main.nf b/modules/nf-core/modules/picard/markduplicates/main.nf index 3087bff4..7990d7e6 100644 --- a/modules/nf-core/modules/picard/markduplicates/main.nf +++ b/modules/nf-core/modules/picard/markduplicates/main.nf @@ -2,10 +2,10 @@ process PICARD_MARKDUPLICATES { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? "bioconda::picard=2.26.7" : null) + conda (params.enable_conda ? "bioconda::picard=2.26.10" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/picard:2.26.7--hdfd78af_0' : - 'quay.io/biocontainers/picard:2.26.7--hdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/picard:2.26.10--hdfd78af_0' : + 'quay.io/biocontainers/picard:2.26.10--hdfd78af_0' }" input: tuple val(meta), path(bam) diff --git a/modules/nf-core/modules/samtools/flagstat/main.nf b/modules/nf-core/modules/samtools/flagstat/main.nf index 03721d0b..119adf77 100644 --- a/modules/nf-core/modules/samtools/flagstat/main.nf +++ b/modules/nf-core/modules/samtools/flagstat/main.nf @@ -17,7 +17,12 @@ process SAMTOOLS_FLAGSTAT { script: def args = task.ext.args ?: '' """ - samtools flagstat --threads ${task.cpus-1} $bam > ${bam}.flagstat + samtools \\ + flagstat \\ + --threads ${task.cpus-1} \\ + $bam \\ + > ${bam}.flagstat + cat <<-END_VERSIONS > versions.yml "${task.process}": samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') diff --git a/modules/nf-core/modules/samtools/idxstats/main.nf b/modules/nf-core/modules/samtools/idxstats/main.nf index cd068679..fc54e676 100644 --- a/modules/nf-core/modules/samtools/idxstats/main.nf +++ b/modules/nf-core/modules/samtools/idxstats/main.nf @@ -17,7 +17,11 @@ process SAMTOOLS_IDXSTATS { script: def args = task.ext.args ?: '' """ - samtools idxstats $bam > ${bam}.idxstats + samtools \\ + idxstats \\ + $bam \\ + > ${bam}.idxstats + cat <<-END_VERSIONS > versions.yml "${task.process}": samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') diff --git a/modules/nf-core/modules/samtools/index/main.nf b/modules/nf-core/modules/samtools/index/main.nf index db025a8f..c4fa2c63 100644 --- a/modules/nf-core/modules/samtools/index/main.nf +++ b/modules/nf-core/modules/samtools/index/main.nf @@ -19,7 +19,11 @@ process SAMTOOLS_INDEX { script: def args = task.ext.args ?: '' """ - samtools index -@ ${task.cpus-1} $args $input + samtools \\ + index \\ + -@ ${task.cpus-1} \\ + $args \\ + $input cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/samtools/sort/main.nf b/modules/nf-core/modules/samtools/sort/main.nf index 0c2cf25e..42c7bbf4 100644 --- a/modules/nf-core/modules/samtools/sort/main.nf +++ b/modules/nf-core/modules/samtools/sort/main.nf @@ -17,6 +17,7 @@ process SAMTOOLS_SORT { script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" + if ("$bam" == "${prefix}.bam") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" """ samtools sort $args -@ $task.cpus -o ${prefix}.bam -T $prefix $bam cat <<-END_VERSIONS > versions.yml diff --git a/modules/nf-core/modules/samtools/stats/main.nf b/modules/nf-core/modules/samtools/stats/main.nf index 83c87002..7209070d 100644 --- a/modules/nf-core/modules/samtools/stats/main.nf +++ b/modules/nf-core/modules/samtools/stats/main.nf @@ -19,7 +19,12 @@ process SAMTOOLS_STATS { def args = task.ext.args ?: '' def reference = fasta ? "--reference ${fasta}" : "" """ - samtools stats --threads ${task.cpus-1} ${reference} ${input} > ${input}.stats + samtools \\ + stats \\ + --threads ${task.cpus-1} \\ + ${reference} \\ + ${input} \\ + > ${input}.stats cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/samtools/view/main.nf b/modules/nf-core/modules/samtools/view/main.nf index 619b84dc..fb31f70b 100644 --- a/modules/nf-core/modules/samtools/view/main.nf +++ b/modules/nf-core/modules/samtools/view/main.nf @@ -21,8 +21,15 @@ process SAMTOOLS_VIEW { def prefix = task.ext.prefix ?: "${meta.id}" def reference = fasta ? "--reference ${fasta} -C" : "" def file_type = input.getExtension() + if ("$input" == "${prefix}.${file_type}") error "Input and output names are the same, use \"task.ext.prefix\" to disambiguate!" """ - samtools view --threads ${task.cpus-1} ${reference} $args $input > ${prefix}.${file_type} + samtools \\ + view \\ + --threads ${task.cpus-1} \\ + ${reference} \\ + $args \\ + $input \\ + > ${prefix}.${file_type} cat <<-END_VERSIONS > versions.yml "${task.process}": From c7eec6408a3bc00c5a6beec1d4fcd3a165107081 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 7 Jan 2022 21:59:03 +0000 Subject: [PATCH 009/238] Add ONT configuration to modules.config --- conf/modules.config | 251 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 249 insertions(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index cd8a5d9f..b05f95b2 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -130,17 +130,264 @@ if (params.platform == 'illumina') { } } +// +// Nanopore configuration options +// + if (params.platform == 'nanopore') { + if (params.sequencing_summary && !params.skip_pycoqc) { + process { + withName: PYCOQC { + publishDir = [ + path: { "${params.outdir}/pycoqc" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_nanoplot) { + process { + withName: NANOPLOT { + publishDir = [ + path: { "${params.outdir}/nanoplot/${meta.id}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + process { - withName: SAMTOOLS_VIEW { - ext.args = '-b -F 4' + withName: GUNZIP_GFF { + publishDir = [ + path: { "${params.outdir}/genome" }, + enabled: false + ] + } + + withName: ARTIC_GUPPYPLEX { + ext.args = '--min-length 400 --max-length 700' + publishDir = [ + path: { "${params.outdir}/guppyplex" }, + enabled: false + ] } withName: ARTIC_MINION { ext.args = [ + '--normalise 500', params.artic_minion_caller == 'medaka' ? '--medaka' : '', params.artic_minion_aligner == 'bwa' ? '--bwa' : '--minimap2' ].join(' ').trim() + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + pattern: "*.{sorted.bam,sorted.bam.bai,fail.vcf,merged.vcf,primers.vcf,gz,tbi,consensus.fasta}" + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_VIEW' { + ext.args = '-b -F 4' + ext.prefix = { "${meta.id}.mapped.sorted" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_INDEX' { + ext.prefix = { "${meta.id}.mapped.sorted" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:BAM_STATS_SAMTOOLS:.*' { + ext.prefix = { "${meta.id}.mapped.sorted" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/samtools_stats" }, + mode: 'copy', + pattern: "*.{stats,flagstat,idxstats}" + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:BCFTOOLS_STATS' { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + + if (!params.skip_mosdepth) { + process { + withName: COLLAPSE_PRIMERS { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: MOSDEPTH_GENOME { + ext.args = '--fast-mode' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, + mode: 'copy', + pattern: "*.{summary.txt}" + ] + } + + withName: PLOT_MOSDEPTH_REGIONS_GENOME { + ext.args = '--input_suffix .regions.bed.gz' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, + mode: 'copy', + pattern: "*.{tsv,pdf}" + ] + } + + withName: MOSDEPTH_AMPLICON { + ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, + mode: 'copy', + pattern: "*.{summary.txt}" + ] + } + + withName: PLOT_MOSDEPTH_REGIONS_AMPLICON { + ext.args = '--input_suffix .regions.bed.gz' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, + mode: 'copy', + pattern: "*.{tsv,pdf}" + ] + } + } + } + + if (!params.skip_pangolin) { + process { + withName: PANGOLIN { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/pangolin" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_nextclade) { + process { + withName: NEXTCLADE { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/nextclade" }, + mode: 'copy', + pattern: "*.{csv}" + ] + } + } + } + + if (!params.skip_variants_quast) { + process { + withName: QUAST { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_snpeff) { + process { + withName: SNPEFF_BUILD { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: SNPEFF_ANN { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, + mode: 'copy', + pattern: "*.{csv,txt,html}" + ] + } + + withName: TABIX_BGZIP { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: TABIX_TABIX { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:.*:.*:BCFTOOLS_STATS' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: SNPSIFT_EXTRACTFIELDS { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_asciigenome) { + process { + withName: ASCIIGENOME { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/asciigenome/${meta.id}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_multiqc) { + process { + withName: MULTIQC { + ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' + publishDir = [ + path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } } } From aca884d28097cdcd2fceb85cbdcc5e034e68a05a Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 10 Jan 2022 15:58:33 +0000 Subject: [PATCH 010/238] Test ONT config and split into separate file --- conf/modules.config | 362 +---------------------------------- conf/modules_illumina.config | 96 ++++++++++ conf/modules_nanopore.config | 294 ++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+), 355 deletions(-) create mode 100644 conf/modules_illumina.config create mode 100644 conf/modules_nanopore.config diff --git a/conf/modules.config b/conf/modules.config index b05f95b2..38e8579e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -3,10 +3,10 @@ Config file for defining DSL2 per module options and publishing paths ======================================================================================== Available keys to override module options: - ext.args = Additional arguments appended to command in module. - ext.args2 = Second set of arguments appended to command in module (multi-tool modules). - ext.args3 = Third set of arguments appended to command in module (multi-tool modules). - ext.prefix = File name prefix for output files. + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. ---------------------------------------------------------------------------------------- */ @@ -38,356 +38,8 @@ process { } } -// -// Generic genome preparation options -// - -process { - if (!params.skip_asciigenome) { - withName: CUSTOM_GETCHROMSIZES { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - } -} - -// -// Illumina genome preparation options -// - -if (params.platform == 'illumina') { - process { - - withName: 'UNTAR_.*' { - ext.args2 = '--no-same-owner' - } - - withName: BLAST_MAKEBLASTDB { - ext.args = '-parse_seqids -dbtype nucl' - publishDir = [ - path: { "${params.outdir}/genome/db" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - } -} - -if (params.platform == 'illumina') { - if (!params.skip_variants) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { - publishDir = [ - path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, - mode: 'copy', - pattern: "*.{stats,flagstat,idxstats}" - ] - } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { - ext.prefix = { "${meta.id}.sorted" } - publishDir = [ - path: { "${params.outdir}/variants/bowtie2" }, - mode: 'copy', - pattern: "*.bam" - ] - } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { - publishDir = [ - path: { "${params.outdir}/variants/bowtie2" }, - mode: 'copy', - pattern: "*.bai" - ] - } - } - - process { - withName: BCFTOOLS_MPILEUP { - ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' - ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' - ext.args3 = "--include 'INFO/DP>=10'" - } - - withName: BEDTOOLS_GENOMECOV { - ext.args = "-bga | awk '\$4 < 10'" - ext.prefix = { "${meta.id}.coverage" } - } - - withName: BEDTOOLS_MERGE { - ext.prefix = { "${meta.id}.coverage.merged" } - } - - withName: BCFTOOLS_CONSENSUS { - ext.prefix = { "${meta.id}.consensus" } - } - } - } -} - -// -// Nanopore configuration options -// - if (params.platform == 'nanopore') { - if (params.sequencing_summary && !params.skip_pycoqc) { - process { - withName: PYCOQC { - publishDir = [ - path: { "${params.outdir}/pycoqc" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - - if (!params.skip_nanoplot) { - process { - withName: NANOPLOT { - publishDir = [ - path: { "${params.outdir}/nanoplot/${meta.id}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - - process { - withName: GUNZIP_GFF { - publishDir = [ - path: { "${params.outdir}/genome" }, - enabled: false - ] - } - - withName: ARTIC_GUPPYPLEX { - ext.args = '--min-length 400 --max-length 700' - publishDir = [ - path: { "${params.outdir}/guppyplex" }, - enabled: false - ] - } - - withName: ARTIC_MINION { - ext.args = [ - '--normalise 500', - params.artic_minion_caller == 'medaka' ? '--medaka' : '', - params.artic_minion_aligner == 'bwa' ? '--bwa' : '--minimap2' - ].join(' ').trim() - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}" }, - mode: 'copy', - pattern: "*.{sorted.bam,sorted.bam.bai,fail.vcf,merged.vcf,primers.vcf,gz,tbi,consensus.fasta}" - ] - } - - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_VIEW' { - ext.args = '-b -F 4' - ext.prefix = { "${meta.id}.mapped.sorted" } - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_INDEX' { - ext.prefix = { "${meta.id}.mapped.sorted" } - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:BAM_STATS_SAMTOOLS:.*' { - ext.prefix = { "${meta.id}.mapped.sorted" } - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/samtools_stats" }, - mode: 'copy', - pattern: "*.{stats,flagstat,idxstats}" - ] - } - - withName: 'NFCORE_VIRALRECON:NANOPORE:BCFTOOLS_STATS' { - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/bcftools_stats" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - - if (!params.skip_mosdepth) { - process { - withName: COLLAPSE_PRIMERS { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - - withName: MOSDEPTH_GENOME { - ext.args = '--fast-mode' - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, - mode: 'copy', - pattern: "*.{summary.txt}" - ] - } - - withName: PLOT_MOSDEPTH_REGIONS_GENOME { - ext.args = '--input_suffix .regions.bed.gz' - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, - mode: 'copy', - pattern: "*.{tsv,pdf}" - ] - } - - withName: MOSDEPTH_AMPLICON { - ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, - mode: 'copy', - pattern: "*.{summary.txt}" - ] - } - - withName: PLOT_MOSDEPTH_REGIONS_AMPLICON { - ext.args = '--input_suffix .regions.bed.gz' - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, - mode: 'copy', - pattern: "*.{tsv,pdf}" - ] - } - } - } - - if (!params.skip_pangolin) { - process { - withName: PANGOLIN { - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/pangolin" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - - if (!params.skip_nextclade) { - process { - withName: NEXTCLADE { - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/nextclade" }, - mode: 'copy', - pattern: "*.{csv}" - ] - } - } - } - - if (!params.skip_variants_quast) { - process { - withName: QUAST { - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - - if (!params.skip_snpeff) { - process { - withName: SNPEFF_BUILD { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - - withName: SNPEFF_ANN { - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, - mode: 'copy', - pattern: "*.{csv,txt,html}" - ] - } - - withName: TABIX_BGZIP { - ext.prefix = { "${meta.id}.snpeff" } - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: TABIX_TABIX { - ext.args = '-p vcf -f' - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:.*:.*:BCFTOOLS_STATS' { - ext.prefix = { "${meta.id}.snpeff" } - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/snpeff/bcftools_stats" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: SNPSIFT_EXTRACTFIELDS { - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - - if (!params.skip_asciigenome) { - process { - withName: ASCIIGENOME { - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}/asciigenome/${meta.id}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - - if (!params.skip_multiqc) { - process { - withName: MULTIQC { - ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' - publishDir = [ - path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } + includeConfig 'modules_nanopore.config' +} else if (params.platform == 'illumina') { + includeConfig 'modules_illumina.config' } diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config new file mode 100644 index 00000000..76a7d41b --- /dev/null +++ b/conf/modules_illumina.config @@ -0,0 +1,96 @@ +/* +======================================================================================== + Config file for defining DSL2 per module options and publishing paths +======================================================================================== + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +// +// General configuration options +// + +process { + withName: 'UNTAR_.*' { + ext.args2 = '--no-same-owner' + } + + withName: BLAST_MAKEBLASTDB { + ext.args = '-parse_seqids -dbtype nucl' + publishDir = [ + path: { "${params.outdir}/genome/db" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } +} + +// +// Variant calling configuration options +// + +if (!params.skip_variants) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, + mode: 'copy', + pattern: "*.{stats,flagstat,idxstats}" + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { + ext.prefix = { "${meta.id}.sorted" } + publishDir = [ + path: { "${params.outdir}/variants/bowtie2" }, + mode: 'copy', + pattern: "*.bam" + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { + publishDir = [ + path: { "${params.outdir}/variants/bowtie2" }, + mode: 'copy', + pattern: "*.bai" + ] + } + + withName: BCFTOOLS_MPILEUP { + ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' + ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' + ext.args3 = "--include 'INFO/DP>=10'" + } + + withName: BEDTOOLS_GENOMECOV { + ext.args = "-bga | awk '\$4 < 10'" + ext.prefix = { "${meta.id}.coverage" } + } + + withName: BEDTOOLS_MERGE { + ext.prefix = { "${meta.id}.coverage.merged" } + } + + withName: BCFTOOLS_CONSENSUS { + ext.prefix = { "${meta.id}.consensus" } + } + } +} + +if (!params.skip_asciigenome) { + process { + withName: CUSTOM_GETCHROMSIZES { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + } +} diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config new file mode 100644 index 00000000..ecba8325 --- /dev/null +++ b/conf/modules_nanopore.config @@ -0,0 +1,294 @@ +/* +======================================================================================== + Config file for defining DSL2 per module options and publishing paths +======================================================================================== + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +// +// General configuration options +// + +process { + withName: 'GUNZIP_.*' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: 'MULTIQC_TSV_.*' { + publishDir = [ + path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, + enabled: false + ] + } + + withName: ARTIC_GUPPYPLEX { + cache = false + ext.args = params.primer_set_version == 1200 ? '--min-length 250 --max-length 1500' : '--min-length 400 --max-length 700' + publishDir = [ + path: { "${params.outdir}/guppyplex" }, + enabled: false + ] + } + + withName: ARTIC_MINION { + ext.args = [ + '--normalise 500', + params.artic_minion_caller == 'medaka' ? '--medaka' : '', + params.artic_minion_aligner == 'bwa' ? '--bwa' : '--minimap2' + ].join(' ').trim() + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + pattern: "*.{sorted.bam,sorted.bam.bai,fail.vcf,merged.vcf,primers.vcf,gz,tbi,consensus.fasta}" + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_VIEW' { + ext.args = '-b -F 4' + ext.prefix = { "${meta.id}.mapped.sorted" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_INDEX' { + ext.prefix = { "${meta.id}.mapped.sorted" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:BAM_STATS_SAMTOOLS:.*' { + ext.prefix = { "${meta.id}.mapped.sorted" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/samtools_stats" }, + mode: 'copy', + pattern: "*.{stats,flagstat,idxstats}" + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:BCFTOOLS_STATS' { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } +} + +// +// Optional configuration options +// + +if (params.sequencing_summary && !params.skip_pycoqc) { + process { + withName: PYCOQC { + publishDir = [ + path: { "${params.outdir}/pycoqc" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } +} + +if (!params.skip_nanoplot) { + process { + withName: NANOPLOT { + publishDir = [ + path: { "${params.outdir}/nanoplot/${meta.id}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } +} + +if (!params.skip_mosdepth) { + process { + withName: COLLAPSE_PRIMERS { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: MOSDEPTH_GENOME { + ext.args = '--fast-mode' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, + mode: 'copy', + pattern: "*.{summary.txt}" + ] + } + + withName: PLOT_MOSDEPTH_REGIONS_GENOME { + ext.args = '--input_suffix .regions.bed.gz' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, + mode: 'copy', + pattern: "*.{tsv,pdf}" + ] + } + + withName: MOSDEPTH_AMPLICON { + ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, + mode: 'copy', + pattern: "*.{summary.txt}" + ] + } + + withName: PLOT_MOSDEPTH_REGIONS_AMPLICON { + ext.args = '--input_suffix .regions.bed.gz' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, + mode: 'copy', + pattern: "*.{tsv,pdf}" + ] + } + } +} + +if (!params.skip_pangolin) { + process { + withName: PANGOLIN { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/pangolin" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } +} + +if (!params.skip_nextclade) { + process { + withName: NEXTCLADE { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/nextclade" }, + mode: 'copy', + pattern: "*.{csv}" + ] + } + } +} + +if (!params.skip_variants_quast) { + process { + withName: QUAST { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } +} + +if (!params.skip_snpeff) { + process { + withName: SNPEFF_BUILD { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: SNPEFF_ANN { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, + mode: 'copy', + pattern: "*.{csv,txt,html}" + ] + } + + withName: TABIX_BGZIP { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: TABIX_TABIX { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:.*:.*:BCFTOOLS_STATS' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: SNPSIFT_EXTRACTFIELDS { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } +} + +if (!params.skip_asciigenome) { + process { + withName: CUSTOM_GETCHROMSIZES { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: ASCIIGENOME { + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}/asciigenome/${meta.id}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } +} + +if (!params.skip_multiqc) { + process { + withName: MULTIQC { + ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' + publishDir = [ + path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } +} From 006f4c9fcec7d6c456c2337fc36278faec66f4e5 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 10 Jan 2022 16:51:29 +0000 Subject: [PATCH 011/238] Remove cache statement --- conf/modules_nanopore.config | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index ecba8325..6a544ae8 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -32,7 +32,6 @@ process { } withName: ARTIC_GUPPYPLEX { - cache = false ext.args = params.primer_set_version == 1200 ? '--min-length 250 --max-length 1500' : '--min-length 400 --max-length 700' publishDir = [ path: { "${params.outdir}/guppyplex" }, @@ -283,7 +282,7 @@ if (!params.skip_asciigenome) { if (!params.skip_multiqc) { process { withName: MULTIQC { - ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' + ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' publishDir = [ path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, mode: 'copy', From bb285458579a8dfaad776f01809b2321a51c4b92 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 10 Jan 2022 17:26:34 +0000 Subject: [PATCH 012/238] Add config for annotation processes --- conf/modules_illumina.config | 559 ++++++++++++++++++++++++++++++++++- 1 file changed, 551 insertions(+), 8 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 76a7d41b..9da41c81 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -10,13 +10,38 @@ ---------------------------------------------------------------------------------------- */ +def assemblers = params.assemblers ? params.assemblers.split(',').collect{ it.trim().toLowerCase() } : [] +def callers = params.callers ? params.callers.split(',').collect{ it.trim().toLowerCase() } : [] + // // General configuration options // process { + withName: 'GUNZIP_.*' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + withName: 'UNTAR_.*' { ext.args2 = '--no-same-owner' + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: 'MULTIQC_TSV_.*' { + publishDir = [ + path: { "${params.outdir}/multiqc" }, + enabled: false + ] } withName: BLAST_MAKEBLASTDB { @@ -34,6 +59,7 @@ process { // Variant calling configuration options // +/// TODO if (!params.skip_variants) { process { withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { @@ -82,15 +108,532 @@ if (!params.skip_variants) { } } -if (!params.skip_asciigenome) { +// +// Optional configuration options +// + +if (!params.skip_variants) { + + if (!params.skip_pangolin) { + if ('ivar' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PANGOLIN' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/pangolin" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if ('bcftools' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:PANGOLIN' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/pangolin" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + } + + if (!params.skip_nextclade) { + if ('ivar' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/nextclade" }, + mode: 'copy', + pattern: "*.{csv}" + ] + } + } + } + + if ('bcftools' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/nextclade" }, + mode: 'copy', + pattern: "*.{csv}" + ] + } + } + } + } + + if (!params.skip_variants_quast) { + if ('ivar' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:QUAST' { + publishDir = [ + path: { "${params.outdir}/variants/ivar" }, + mode: 'copy', + pattern: "quast" + ] + } + } + } + + if ('bcftools' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:QUAST' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: "quast" + ] + } + } + } + } + + if (!params.skip_asciigenome) { + process { + withName: CUSTOM_GETCHROMSIZES { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + } + + if ('ivar' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:ASCIIGENOME' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/asciigenome/${meta.id}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if ('bcftools' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:ASCIIGENOME' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/asciigenome/${meta.id}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + } +} + +if (!params.skip_multiqc) { process { - withName: CUSTOM_GETCHROMSIZES { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] + withName: MULTIQC { + ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' } } } + +/// TO DO +// [93/ea0897] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPEFF_ANN (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [0b/d97a14] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [77/4071f0] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX (SAMP... [100%] 3 of 3, cached: 3 ✔ +// [8e/c01c45] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS (S... [100%] 3 of 3, cached: 3 ✔ +// [61/1cb303] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [7b/5e0302] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPEFF_ANN (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [b4/288f58] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [11/7412d6] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX (... [100%] 3 of 3, cached: 3 ✔ +// [4a/ed30ad] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STAT... [100%] 3 of 3, cached: 3 ✔ +// [ac/79f849] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [93/aa472e] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC_TSV_IVAR_NEXTCLADE [100%] 1 of 1, cached: 1 ✔ +// [8f/6c0551] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC_TSV_BCFTOOLS_NEXTCLADE [100%] 1 of 1, cached: 1 ✔ +// [21/cd7130] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:CUSTOM_GETCHROMSIZES (nCoV-2019.reference.fasta) [100%] 1 of 1, cached: 1 ✔ +// [7e/357886] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:UNTAR_KRAKEN2_DB (kraken2_hs22.tar.gz) [100%] 1 of 1, cached: 1 ✔ +// [0a/2ad620] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:COLLAPSE_PRIMERS (nCoV-2019.primer.bed) [100%] 1 of 1, cached: 1 ✔ +// [e0/a1eb24] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:BEDTOOLS_GETFASTA (nCoV-2019.primer.bed) [100%] 1 of 1, cached: 1 ✔ +// [f1/e1533f] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:BOWTIE2_BUILD (nCoV-2019.reference.fasta) [100%] 1 of 1, cached: 1 ✔ +// [98/51f7a0] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:BLAST_MAKEBLASTDB (nCoV-2019.reference.fasta) [100%] 1 of 1, cached: 1 ✔ +// [3f/d6ca92] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:SNPEFF_BUILD (nCoV-2019.reference.fasta) [100%] 1 of 1, cached: 1 ✔ +// [bd/ef7dbb] process > NFCORE_VIRALRECON:ILLUMINA:CAT_FASTQ (SAMPLE3_SE) [100%] 1 of 1, cached: 1 ✔ +// [81/30f839] process > NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_RAW (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [c2/96c7f7] process > NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTP (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [c7/74c320] process > NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_TRIM (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [- ] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC_TSV_FAIL_READS - +// [ce/942714] process > NFCORE_VIRALRECON:ILLUMINA:KRAKEN2_KRAKEN2 (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [91/b5ff71] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BOWTIE2_ALIGN (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [64/fe7afd] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [3e/c23afd] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [59/4fa196] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_STATS (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [bf/cf3287] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_FLAGSTAT (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [7f/d5a75e] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_IDXSTATS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [- ] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC_TSV_FAIL_MAPPED - +// [4d/24d695] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:IVAR_TRIM (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [10/349910] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [ae/fca4ac] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [34/5187eb] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_STATS (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [b0/1240eb] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_FLAGSTAT (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [56/f23b7a] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_IDXSTATS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [19/b14f8d] process > NFCORE_VIRALRECON:ILLUMINA:PICARD_COLLECTMULTIPLEMETRICS (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [81/ca1f92] process > NFCORE_VIRALRECON:ILLUMINA:MOSDEPTH_GENOME (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [90/0377d9] process > NFCORE_VIRALRECON:ILLUMINA:PLOT_MOSDEPTH_REGIONS_GENOME [100%] 1 of 1, cached: 1 ✔ +// [10/9c237e] process > NFCORE_VIRALRECON:ILLUMINA:MOSDEPTH_AMPLICON (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [c3/53f277] process > NFCORE_VIRALRECON:ILLUMINA:PLOT_MOSDEPTH_REGIONS_AMPLICON [100%] 1 of 1, cached: 1 ✔ +// [00/4935d7] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:IVAR_VARIANTS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [91/4a4b5a] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:IVAR_VARIANTS_TO_VCF (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [1b/f1c791] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [67/5d86e8] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [ad/b6b890] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [db/9b0548] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:IVAR_CONSENSUS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [e8/931336] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PLOT_BASE_DENSITY (SAMPLE1_PE.fa) [100%] 3 of 3, cached: 3 ✔ +// [0f/2c07f7] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:BCFTOOLS_MPILEUP (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [30/99709e] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:BEDTOOLS_GENOMECOV (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [1f/98d75f] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:BEDTOOLS_MERGE (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [c0/150f35] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:MAKE_BED_MASK (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [cb/9ff9c5] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:BEDTOOLS_MASKFASTA (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [c3/b3093f] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:BCFTOOLS_CONSENSUS (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [0e/c228d8] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:PLOT_BASE_DENSITY (SAMPLE2_PE.consensus.fa) [100%] 3 of 3, cached: 3 ✔ +// [52/e549df] process > NFCORE_VIRALRECON:ILLUMINA:BCFTOOLS_ISEC (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [c2/3daa1f] process > NFCORE_VIRALRECON:ILLUMINA:CUTADAPT (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [e6/2e9f10] process > NFCORE_VIRALRECON:ILLUMINA:FASTQC (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ + + +// TO CHECK +// [9d/c26c56] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:GUNZIP_GFF (GCA_009858895.3_ASM985889v3_genomic.200409.gff.gz) [100%] 1 of 1, cached: 1 ✔ + + +/// DONE +// [db/851f73] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC (1) [100%] 1 of 1 ✔ +// [48/ea84f5] process > NFCORE_VIRALRECON:ILLUMINA:INPUT_CHECK:SAMPLESHEET_CHECK (samplesheet_test_illumina_amplicon.csv) [100%] 1 of 1, cached: 1 ✔ +// [5c/9bfbb1] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:QUAST [100%] 1 of 1, cached: 1 ✔ +// [c3/e76dbb] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PANGOLIN (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [ed/6db1b4] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ +// [9c/5a39db] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:QUAST [100%] 1 of 1, cached: 1 ✔ +// [e7/264a0a] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:PANGOLIN (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [dc/9166e3] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ +// [14/d24c1b] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:ASCIIGENOME (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ +// [f1/1aa54b] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:ASCIIGENOME (SAMPLE3_SE) +// [1a/d818b0] process > NFCORE_VIRALRECON:ILLUMINA:CUSTOM_DUMPSOFTWAREVERSIONS (1) [100%] 1 of 1 ✔ + + + + + +// 'illumina_bedtools_getfasta' { +// args = '-s -nameOnly' +// publish_dir = 'genome' +// } +// 'illumina_collapse_primers_illumina' { +// publish_dir = 'genome' +// } +// 'illumina_bowtie2_build' { +// args = '--seed 1' +// publish_dir = 'genome/index' +// } +// 'illumina_snpeff_build' { +// publish_dir = 'genome/db' +// } +// 'illumina_blast_makeblastdb' { +// args = '-parse_seqids -dbtype nucl' +// publish_dir = 'genome/db' +// } +// 'illumina_kraken2_build' { +// args = '' +// args2 = '' +// args3 = '' +// publish_dir = 'genome/db' +// } +// 'illumina_cat_fastq' { +// publish_files = false +// publish_dir = 'fastq' +// } +// 'illumina_fastqc_raw' { +// args = '--quiet' +// publish_dir = 'fastqc/raw' +// } +// 'illumina_fastqc_trim' { +// args = '--quiet' +// publish_dir = 'fastqc/trim' +// } +// 'illumina_fastp' { +// args = '--cut_front --cut_tail --trim_poly_x --cut_mean_quality 30 --qualified_quality_phred 30 --unqualified_percent_limit 10 --length_required 50' +// publish_files = ['json':'', 'html':'', 'log': 'log'] +// } +// 'illumina_kraken2_kraken2' { +// args = '--report-zero-counts' +// publish_files = ['txt':''] +// } +// 'illumina_bowtie2_align' { +// args = '--local --very-sensitive-local --seed 1' +// args2 = '-F4' +// publish_files = ['log':'log'] +// publish_dir = 'variants/bowtie2' +// } +// 'illumina_bowtie2_sort_bam' { +// suffix = '.sorted' +// publish_files = ['bam':'', 'bai':'', 'stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] +// publish_dir = 'variants/bowtie2' +// } +// 'illumina_ivar_trim' { +// args = '-m 30 -q 20' +// suffix = '.ivar_trim' +// publish_files = ['log':'log'] +// publish_dir = 'variants/bowtie2' +// } +// 'illumina_ivar_trim_sort_bam' { +// suffix = '.ivar_trim.sorted' +// publish_files = ['stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] +// publish_dir = 'variants/bowtie2' +// } +// 'illumina_picard_markduplicates' { +// args = 'ASSUME_SORTED=true VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' +// suffix = '.markduplicates.sorted' +// publish_files = ['bam': '', 'metrics.txt':'picard_metrics'] +// publish_dir = 'variants/bowtie2' +// } +// 'illumina_picard_markduplicates_sort_bam' { +// suffix = '.markduplicates.sorted' +// publish_files = ['bai':'', 'stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] +// publish_dir = 'variants/bowtie2' +// } +// 'illumina_picard_collectmultiplemetrics' { +// args = 'VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' +// publish_files = ['metrics':'picard_metrics', 'pdf': 'picard_metrics/pdf'] +// publish_dir = 'variants/bowtie2' +// } +// 'illumina_mosdepth_genome' { +// args = '--fast-mode' +// publish_files = ['summary.txt':''] +// publish_dir = 'variants/bowtie2/mosdepth/genome' +// } +// 'illumina_plot_mosdepth_regions_genome' { +// args = '--input_suffix .regions.bed.gz' +// publish_files = ['tsv':'', 'pdf': ''] +// publish_dir = 'variants/bowtie2/mosdepth/genome' +// } +// 'illumina_mosdepth_amplicon' { +// args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' +// publish_files = ['summary.txt':''] +// publish_dir = 'variants/bowtie2/mosdepth/amplicon' +// } +// 'illumina_plot_mosdepth_regions_amplicon' { +// args = '--input_suffix .regions.bed.gz' +// publish_files = ['tsv':'', 'pdf': ''] +// publish_dir = 'variants/bowtie2/mosdepth/amplicon' +// } +// 'illumina_ivar_variants' { +// args = '-t 0.25 -q 20 -m 10' +// args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0' +// publish_dir = 'variants/ivar' +// } +// 'illumina_ivar_variants_to_vcf' { +// publish_files = ['log':'log'] +// publish_dir = 'variants/ivar' +// } +// 'illumina_ivar_tabix_bgzip' { +// publish_dir = 'variants/ivar' +// } +// 'illumina_ivar_tabix_tabix' { +// args = '-p vcf -f' +// publish_dir = 'variants/ivar' +// } +// 'illumina_ivar_bcftools_stats' { +// publish_files = ['txt':'bcftools_stats'] +// publish_dir = 'variants/ivar' +// } +// 'illumina_ivar_consensus' { +// args = '-t 0.75 -q 20 -m 10 -n N' +// args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' +// suffix = '.consensus' +// publish_dir = 'variants/ivar/consensus' +// } +// 'illumina_ivar_consensus_plot' { +// suffix = '.consensus' +// publish_dir = 'variants/ivar/consensus/base_qc' +// } +// 'illumina_ivar_snpeff' { +// publish_files = ['csv':'', 'txt':'', 'html':''] +// publish_dir = 'variants/ivar/snpeff' +// } +// 'illumina_ivar_snpeff_bgzip' { +// suffix = '.snpeff' +// publish_dir = 'variants/ivar/snpeff' +// } +// 'illumina_ivar_snpeff_tabix' { +// args = '-p vcf -f' +// suffix = '.snpeff' +// publish_dir = 'variants/ivar/snpeff' +// } +// 'illumina_ivar_snpeff_stats' { +// suffix = '.snpeff' +// publish_files = ['txt':'bcftools_stats'] +// publish_dir = 'variants/ivar/snpeff' +// } +// 'illumina_ivar_snpsift' { +// publish_dir = 'variants/ivar/snpeff' +// } +// 'illumina_bcftools_mpileup' { +// args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' +// args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' +// args3 = "--include 'INFO/DP>=10'" +// publish_files = ['gz':'', 'gz.tbi':'', 'stats.txt':'bcftools_stats'] +// publish_dir = 'variants/bcftools' +// } +// 'illumina_bcftools_consensus_genomecov' { +// args = "-bga | awk '\$4 < 10'" +// suffix = '.coverage' +// publish_files = false +// publish_dir = 'variants/bcftools' +// } +// 'illumina_bcftools_consensus_merge' { +// suffix = '.coverage.merged' +// publish_files = false +// publish_dir = 'variants/bcftools' +// } +// 'illumina_bcftools_consensus_mask' { +// suffix = '.coverage.masked' +// publish_files = false +// publish_dir = 'variants/bcftools' +// } +// 'illumina_bcftools_consensus_maskfasta' { +// suffix = '.masked' +// publish_files = false +// publish_dir = 'variants/bcftools' +// } +// 'illumina_bcftools_consensus_bcftools' { +// suffix = '.consensus' +// publish_dir = 'variants/bcftools/consensus' +// } +// 'illumina_bcftools_consensus_plot' { +// suffix = '.consensus' +// publish_dir = 'variants/bcftools/consensus/base_qc' +// } +// 'illumina_bcftools_snpeff' { +// publish_files = ['csv':'', 'txt':'', 'html':''] +// publish_dir = 'variants/bcftools/snpeff' +// } +// 'illumina_bcftools_snpeff_bgzip' { +// suffix = '.snpeff' +// publish_dir = 'variants/bcftools/snpeff' +// } +// 'illumina_bcftools_snpeff_tabix' { +// args = '-p vcf -f' +// suffix = '.snpeff' +// publish_dir = 'variants/bcftools/snpeff' +// } +// 'illumina_bcftools_snpeff_stats' { +// suffix = '.snpeff' +// publish_files = ['txt':'bcftools_stats'] +// publish_dir = 'variants/bcftools/snpeff' +// } +// 'illumina_bcftools_snpsift' { +// publish_dir = 'variants/bcftools/snpeff' +// } +// 'illumina_bcftools_isec' { +// args = '--nfiles +2 --output-type z' +// publish_dir = 'variants/intersect' +// } + + + + + + +// 'illumina_cutadapt' { +// args = '--overlap 5 --minimum-length 30 --error-rate 0.1' +// suffix = '.primer_trim' +// publish_files = ['log':'log'] +// publish_dir = 'assembly/cutadapt' +// } +// 'illumina_cutadapt_fastqc' { +// args = '--quiet' +// suffix = 'primer_trim' +// publish_dir = 'assembly/cutadapt/fastqc' +// } +// 'illumina_spades' { +// args = '' +// publish_files = ['log':'log', 'fa':'', 'gfa':''] +// publish_dir = "assembly/spades/${params.spades_mode}" +// } +// 'illumina_spades_bandage' { +// args = '--height 1000' +// publish_dir = "assembly/spades/${params.spades_mode}/bandage" +// } +// 'illumina_spades_blastn' { +// args = "-outfmt '6 stitle std slen qlen qcovs'" +// publish_dir = "assembly/spades/${params.spades_mode}/blastn" +// } +// 'illumina_spades_blastn_filter' { +// suffix = '.filter.blastn' +// publish_dir = "assembly/spades/${params.spades_mode}/blastn" +// } +// 'illumina_spades_abacas' { +// args = '-m -p nucmer' +// publish_dir = "assembly/spades/${params.spades_mode}/abacas" +// } +// 'illumina_spades_plasmidid' { +// args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' +// publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] +// publish_dir = "assembly/spades/${params.spades_mode}/plasmidid" +// } +// 'illumina_spades_quast' { +// publish_files = ['quast':''] +// publish_dir = "assembly/spades/${params.spades_mode}" +// } +// 'illumina_unicycler' { +// publish_files = ['log':'log', 'fa':'', 'gfa':''] +// publish_dir = 'assembly/unicycler' +// } +// 'illumina_unicycler_bandage' { +// args = '--height 1000' +// publish_dir = 'assembly/unicycler/bandage' +// } +// 'illumina_unicycler_blastn' { +// args = "-outfmt '6 stitle std slen qlen qcovs'" +// publish_dir = 'assembly/unicycler/blastn' +// } +// 'illumina_unicycler_blastn_filter' { +// suffix = '.filter.blastn' +// publish_dir = 'assembly/unicycler/blastn' +// } +// 'illumina_unicycler_abacas' { +// args = '-m -p nucmer' +// publish_dir = 'assembly/unicycler/abacas' +// } +// 'illumina_unicycler_plasmidid' { +// args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' +// publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] +// publish_dir = 'assembly/unicycler/plasmidid' +// } +// 'illumina_unicycler_quast' { +// publish_files = ['quast':''] +// publish_dir = 'assembly/unicycler' +// } +// 'illumina_minia' { +// args = '-kmer-size 31 -abundance-min 20' +// publish_dir = 'assembly/minia' +// } +// 'illumina_minia_blastn' { +// args = "-outfmt '6 stitle std slen qlen qcovs'" +// publish_dir = 'assembly/minia/blastn' +// } +// 'illumina_minia_blastn_filter' { +// suffix = '.filter.blastn' +// publish_dir = 'assembly/minia/blastn' +// } +// 'illumina_minia_abacas' { +// args = '-m -p nucmer' +// publish_dir = 'assembly/minia/abacas' +// } +// 'illumina_minia_plasmidid' { +// args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' +// publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] +// publish_dir = 'assembly/minia/plasmidid' +// } +// 'illumina_minia_quast' { +// publish_files = ['quast':''] +// publish_dir = 'assembly/minia' +// } From 695755616e4a6ed1f506ecba1af666ea956954eb Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 10 Jan 2022 23:16:32 +0000 Subject: [PATCH 013/238] Get halfway through illumina config options --- conf/modules_illumina.config | 849 +++++++++++++++++------------------ 1 file changed, 418 insertions(+), 431 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 9da41c81..91eb8a57 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -37,40 +37,127 @@ process { ] } - withName: 'MULTIQC_TSV_.*' { + withName: CAT_FASTQ { publishDir = [ - path: { "${params.outdir}/multiqc" }, + path: { "${params.outdir}/fastq" }, enabled: false ] } - withName: BLAST_MAKEBLASTDB { - ext.args = '-parse_seqids -dbtype nucl' + withName: 'MULTIQC_TSV_.*' { publishDir = [ - path: { "${params.outdir}/genome/db" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference + path: { "${params.outdir}/multiqc" }, + enabled: false ] } } +if (!params.skip_fastqc) { + process { + withName: FASTQC_RAW { + ext.args = '--quiet' + publishDir = [ + path: { "${params.outdir}/fastqc/raw" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: FASTQC_TRIM { + ext.args = '--quiet' + publishDir = [ + path: { "${params.outdir}/fastqc/trim" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } +} + +if (!params.skip_fastp) { + process { + withName: FASTP { + ext.args = '--cut_front --cut_tail --trim_poly_x --cut_mean_quality 30 --qualified_quality_phred 30 --unqualified_percent_limit 10 --length_required 50' + publishDir = [ + [ + path: { "${params.outdir}/fastp" }, + mode: 'copy', + pattern: "*.{json,html}" + ], + [ + path: { "${params.outdir}/fastp/log" }, + mode: 'copy', + pattern: "*.log" + ], + [ + path: { "${params.outdir}/fastp" }, + mode: 'copy', + pattern: "*.fail.fastq.gz", + enabled: params.save_trimmed_fail + ] + ] + } + } +} + +if (!params.skip_kraken2) { + process { + withName: KRAKEN2_BUILD { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: KRAKEN2_KRAKEN2 { + ext.args = '--report-zero-counts' + publishDir = [ + path: { "${params.outdir}/kraken2" }, + mode: 'copy', + pattern: "*.txt" + ] + } + } +} + // -// Variant calling configuration options +// Optional configuration options // -/// TODO if (!params.skip_variants) { + process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + withName: BOWTIE2_BUILD { + ext.args = '--seed 1' publishDir = [ - path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, + path: { "${params.outdir}/genome" }, mode: 'copy', - pattern: "*.{stats,flagstat,idxstats}" + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: BOWTIE2_ALIGN { + ext.args = '--local --very-sensitive-local --seed 1' + ext.args2 = '-F4' + publishDir = [ + [ + path: { "${params.outdir}/variants/bowtie2/log" }, + mode: 'copy', + pattern: "*.log" + ], + [ + path: { "${params.outdir}/variants/bowtie2/unmapped" }, + mode: 'copy', + pattern: "*.fastq.gz", + enabled: params.save_unaligned + ] ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { ext.prefix = { "${meta.id}.sorted" } publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, @@ -79,7 +166,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:.*:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, mode: 'copy', @@ -87,32 +174,149 @@ if (!params.skip_variants) { ] } - withName: BCFTOOLS_MPILEUP { - ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' - ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' - ext.args3 = "--include 'INFO/DP>=10'" + withName: 'NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, + mode: 'copy', + pattern: "*.{stats,flagstat,idxstats}" + ] } + } - withName: BEDTOOLS_GENOMECOV { - ext.args = "-bga | awk '\$4 < 10'" - ext.prefix = { "${meta.id}.coverage" } - } + if (!params.skip_ivar_trim && params.protocol == 'amplicon') { + process { + withName: IVAR_TRIM { + ext.args = [ + '-m 30 -q 20', + params.ivar_trim_noprimer ? '' : '-e', + params.ivar_trim_offset ? "-x ${params.ivar_trim_offset}" : '' + ].join(' ').trim() + ext.prefix = { "${meta.id}.ivar_trim" } + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/log" }, + mode: 'copy', + pattern: '*.log' + ] + } - withName: BEDTOOLS_MERGE { - ext.prefix = { "${meta.id}.coverage.merged" } + withName: 'NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { + ext.prefix = { "${meta.id}.ivar_trim.sorted" } + publishDir = [ + path: { "${params.outdir}/variants/bowtie2" }, + mode: 'copy', + pattern: "*.bam", + enabled: params.skip_markduplicates + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { + publishDir = [ + path: { "${params.outdir}/variants/bowtie2" }, + mode: 'copy', + pattern: "*.bai", + enabled: params.skip_markduplicates + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, + mode: 'copy', + pattern: "*.{stats,flagstat,idxstats}" + ] + } } + } - withName: BCFTOOLS_CONSENSUS { - ext.prefix = { "${meta.id}.consensus" } + if (!params.skip_markduplicates) { + process { + withName: '.*:MARK_DUPLICATES_PICARD:PICARD_MARKDUPLICATES' { + ext.args = [ + 'ASSUME_SORTED=true VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp', + params.filter_duplicates ? 'REMOVE_DUPLICATES=true' : '' + ].join(' ').trim() + ext.prefix = { "${meta.id}.markduplicates.sorted" } + publishDir = [ + [ + path: { "${params.outdir}/variants/bowtie2/picard_metrics" }, + mode: 'copy', + pattern: '*metrics.txt' + ], + [ + path: { "${params.outdir}/variants/bowtie2" }, + mode: 'copy', + pattern: '*.bam' + ] + ] + } + + withName: '.*:MARK_DUPLICATES_PICARD:SAMTOOLS_INDEX' { + ext.prefix = { "${meta.id}.markduplicates.sorted" } + publishDir = [ + path: { "${params.outdir}/variants/bowtie2" }, + mode: 'copy', + pattern: '*.bai' + ] + } + + withName: '.*:MARK_DUPLICATES_PICARD:BAM_STATS_SAMTOOLS:.*' { + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, + mode: 'copy', + pattern: '*.{stats,flagstat,idxstats}' + ] + } } } -} -// -// Optional configuration options -// + if (!params.skip_mosdepth) { + process { + withName: COLLAPSE_PRIMERS { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } -if (!params.skip_variants) { + withName: MOSDEPTH_GENOME { + ext.args = '--fast-mode' + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/mosdepth/genome" }, + mode: 'copy', + pattern: "*.{summary.txt}" + ] + } + + withName: PLOT_MOSDEPTH_REGIONS_GENOME { + ext.args = '--input_suffix .regions.bed.gz' + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/mosdepth/genome" }, + mode: 'copy', + pattern: "*.{tsv,pdf}" + ] + } + + withName: MOSDEPTH_AMPLICON { + ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, + mode: 'copy', + pattern: "*.{summary.txt}" + ] + } + + withName: PLOT_MOSDEPTH_REGIONS_AMPLICON { + ext.args = '--input_suffix .regions.bed.gz' + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, + mode: 'copy', + pattern: "*.{tsv,pdf}" + ] + } + } + } if (!params.skip_pangolin) { if ('ivar' in callers) { @@ -228,412 +432,195 @@ if (!params.skip_variants) { } } } + + if (!params.skip_snpeff) { + process { + withName: SNPEFF_BUILD { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + } + + if ('ivar' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPEFF_ANN' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff" }, + mode: 'copy', + pattern: "*.{csv,txt,html}" + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if ('bcftools' in callers) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPEFF_ANN' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/snpeff" }, + mode: 'copy', + pattern: "*.{csv,txt,html}" + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/variants/bcftools/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools/snpeff/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + } } if (!params.skip_multiqc) { process { withName: MULTIQC { ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' + publishDir = [ + [ + path: { "${params.outdir}/multiqc" }, + mode: 'copy', + pattern: 'multiqc*' + ], + [ + path: { "${params.outdir}/multiqc" }, + mode: 'copy', + pattern: '*variants_metrics_mqc.csv', + enabled: !params.skip_variants + ], + [ + path: { "${params.outdir}/multiqc" }, + mode: 'copy', + pattern: '*assembly_metrics_mqc.csv', + enabled: !params.skip_assembly + ] + ] } } } -/// TO DO -// [93/ea0897] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPEFF_ANN (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [0b/d97a14] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [77/4071f0] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX (SAMP... [100%] 3 of 3, cached: 3 ✔ -// [8e/c01c45] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS (S... [100%] 3 of 3, cached: 3 ✔ -// [61/1cb303] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [7b/5e0302] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPEFF_ANN (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [b4/288f58] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [11/7412d6] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX (... [100%] 3 of 3, cached: 3 ✔ -// [4a/ed30ad] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STAT... [100%] 3 of 3, cached: 3 ✔ -// [ac/79f849] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [93/aa472e] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC_TSV_IVAR_NEXTCLADE [100%] 1 of 1, cached: 1 ✔ -// [8f/6c0551] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC_TSV_BCFTOOLS_NEXTCLADE [100%] 1 of 1, cached: 1 ✔ -// [21/cd7130] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:CUSTOM_GETCHROMSIZES (nCoV-2019.reference.fasta) [100%] 1 of 1, cached: 1 ✔ -// [7e/357886] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:UNTAR_KRAKEN2_DB (kraken2_hs22.tar.gz) [100%] 1 of 1, cached: 1 ✔ -// [0a/2ad620] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:COLLAPSE_PRIMERS (nCoV-2019.primer.bed) [100%] 1 of 1, cached: 1 ✔ -// [e0/a1eb24] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:BEDTOOLS_GETFASTA (nCoV-2019.primer.bed) [100%] 1 of 1, cached: 1 ✔ -// [f1/e1533f] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:BOWTIE2_BUILD (nCoV-2019.reference.fasta) [100%] 1 of 1, cached: 1 ✔ -// [98/51f7a0] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:BLAST_MAKEBLASTDB (nCoV-2019.reference.fasta) [100%] 1 of 1, cached: 1 ✔ -// [3f/d6ca92] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:SNPEFF_BUILD (nCoV-2019.reference.fasta) [100%] 1 of 1, cached: 1 ✔ -// [bd/ef7dbb] process > NFCORE_VIRALRECON:ILLUMINA:CAT_FASTQ (SAMPLE3_SE) [100%] 1 of 1, cached: 1 ✔ -// [81/30f839] process > NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_RAW (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [c2/96c7f7] process > NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTP (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [c7/74c320] process > NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_TRIM (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [- ] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC_TSV_FAIL_READS - -// [ce/942714] process > NFCORE_VIRALRECON:ILLUMINA:KRAKEN2_KRAKEN2 (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [91/b5ff71] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BOWTIE2_ALIGN (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [64/fe7afd] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [3e/c23afd] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [59/4fa196] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_STATS (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [bf/cf3287] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_FLAGSTAT (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [7f/d5a75e] process > NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_IDXSTATS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [- ] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC_TSV_FAIL_MAPPED - -// [4d/24d695] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:IVAR_TRIM (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [10/349910] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [ae/fca4ac] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [34/5187eb] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_STATS (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [b0/1240eb] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_FLAGSTAT (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [56/f23b7a] process > NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:SAMTOOLS_IDXSTATS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [19/b14f8d] process > NFCORE_VIRALRECON:ILLUMINA:PICARD_COLLECTMULTIPLEMETRICS (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [81/ca1f92] process > NFCORE_VIRALRECON:ILLUMINA:MOSDEPTH_GENOME (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [90/0377d9] process > NFCORE_VIRALRECON:ILLUMINA:PLOT_MOSDEPTH_REGIONS_GENOME [100%] 1 of 1, cached: 1 ✔ -// [10/9c237e] process > NFCORE_VIRALRECON:ILLUMINA:MOSDEPTH_AMPLICON (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [c3/53f277] process > NFCORE_VIRALRECON:ILLUMINA:PLOT_MOSDEPTH_REGIONS_AMPLICON [100%] 1 of 1, cached: 1 ✔ -// [00/4935d7] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:IVAR_VARIANTS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [91/4a4b5a] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:IVAR_VARIANTS_TO_VCF (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [1b/f1c791] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [67/5d86e8] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [ad/b6b890] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [db/9b0548] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:IVAR_CONSENSUS (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [e8/931336] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PLOT_BASE_DENSITY (SAMPLE1_PE.fa) [100%] 3 of 3, cached: 3 ✔ -// [0f/2c07f7] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:BCFTOOLS_MPILEUP (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [30/99709e] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:BEDTOOLS_GENOMECOV (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [1f/98d75f] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:BEDTOOLS_MERGE (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [c0/150f35] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:MAKE_BED_MASK (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [cb/9ff9c5] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:BEDTOOLS_MASKFASTA (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [c3/b3093f] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:BCFTOOLS_CONSENSUS (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [0e/c228d8] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:MAKE_CONSENSUS:PLOT_BASE_DENSITY (SAMPLE2_PE.consensus.fa) [100%] 3 of 3, cached: 3 ✔ -// [52/e549df] process > NFCORE_VIRALRECON:ILLUMINA:BCFTOOLS_ISEC (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [c2/3daa1f] process > NFCORE_VIRALRECON:ILLUMINA:CUTADAPT (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [e6/2e9f10] process > NFCORE_VIRALRECON:ILLUMINA:FASTQC (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ - - -// TO CHECK -// [9d/c26c56] process > NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:GUNZIP_GFF (GCA_009858895.3_ASM985889v3_genomic.200409.gff.gz) [100%] 1 of 1, cached: 1 ✔ - - -/// DONE -// [db/851f73] process > NFCORE_VIRALRECON:ILLUMINA:MULTIQC (1) [100%] 1 of 1 ✔ -// [48/ea84f5] process > NFCORE_VIRALRECON:ILLUMINA:INPUT_CHECK:SAMPLESHEET_CHECK (samplesheet_test_illumina_amplicon.csv) [100%] 1 of 1, cached: 1 ✔ -// [5c/9bfbb1] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:QUAST [100%] 1 of 1, cached: 1 ✔ -// [c3/e76dbb] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PANGOLIN (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [ed/6db1b4] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE (SAMPLE1_PE) [100%] 3 of 3, cached: 3 ✔ -// [9c/5a39db] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:QUAST [100%] 1 of 1, cached: 1 ✔ -// [e7/264a0a] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:PANGOLIN (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [dc/9166e3] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE (SAMPLE2_PE) [100%] 3 of 3, cached: 3 ✔ -// [14/d24c1b] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:ASCIIGENOME (SAMPLE3_SE) [100%] 3 of 3, cached: 3 ✔ -// [f1/1aa54b] process > NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:ASCIIGENOME (SAMPLE3_SE) -// [1a/d818b0] process > NFCORE_VIRALRECON:ILLUMINA:CUSTOM_DUMPSOFTWAREVERSIONS (1) [100%] 1 of 1 ✔ - - - - - -// 'illumina_bedtools_getfasta' { -// args = '-s -nameOnly' -// publish_dir = 'genome' -// } -// 'illumina_collapse_primers_illumina' { -// publish_dir = 'genome' -// } -// 'illumina_bowtie2_build' { -// args = '--seed 1' -// publish_dir = 'genome/index' -// } -// 'illumina_snpeff_build' { -// publish_dir = 'genome/db' -// } -// 'illumina_blast_makeblastdb' { -// args = '-parse_seqids -dbtype nucl' -// publish_dir = 'genome/db' -// } -// 'illumina_kraken2_build' { -// args = '' -// args2 = '' -// args3 = '' -// publish_dir = 'genome/db' -// } -// 'illumina_cat_fastq' { -// publish_files = false -// publish_dir = 'fastq' -// } -// 'illumina_fastqc_raw' { -// args = '--quiet' -// publish_dir = 'fastqc/raw' -// } -// 'illumina_fastqc_trim' { -// args = '--quiet' -// publish_dir = 'fastqc/trim' -// } -// 'illumina_fastp' { -// args = '--cut_front --cut_tail --trim_poly_x --cut_mean_quality 30 --qualified_quality_phred 30 --unqualified_percent_limit 10 --length_required 50' -// publish_files = ['json':'', 'html':'', 'log': 'log'] -// } -// 'illumina_kraken2_kraken2' { -// args = '--report-zero-counts' -// publish_files = ['txt':''] -// } -// 'illumina_bowtie2_align' { -// args = '--local --very-sensitive-local --seed 1' -// args2 = '-F4' -// publish_files = ['log':'log'] -// publish_dir = 'variants/bowtie2' -// } -// 'illumina_bowtie2_sort_bam' { -// suffix = '.sorted' -// publish_files = ['bam':'', 'bai':'', 'stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] -// publish_dir = 'variants/bowtie2' -// } -// 'illumina_ivar_trim' { -// args = '-m 30 -q 20' -// suffix = '.ivar_trim' -// publish_files = ['log':'log'] -// publish_dir = 'variants/bowtie2' -// } -// 'illumina_ivar_trim_sort_bam' { -// suffix = '.ivar_trim.sorted' -// publish_files = ['stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] -// publish_dir = 'variants/bowtie2' -// } -// 'illumina_picard_markduplicates' { -// args = 'ASSUME_SORTED=true VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' -// suffix = '.markduplicates.sorted' -// publish_files = ['bam': '', 'metrics.txt':'picard_metrics'] -// publish_dir = 'variants/bowtie2' -// } -// 'illumina_picard_markduplicates_sort_bam' { -// suffix = '.markduplicates.sorted' -// publish_files = ['bai':'', 'stats':'samtools_stats', 'flagstat':'samtools_stats', 'idxstats':'samtools_stats'] -// publish_dir = 'variants/bowtie2' -// } -// 'illumina_picard_collectmultiplemetrics' { -// args = 'VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' -// publish_files = ['metrics':'picard_metrics', 'pdf': 'picard_metrics/pdf'] -// publish_dir = 'variants/bowtie2' -// } -// 'illumina_mosdepth_genome' { -// args = '--fast-mode' -// publish_files = ['summary.txt':''] -// publish_dir = 'variants/bowtie2/mosdepth/genome' -// } -// 'illumina_plot_mosdepth_regions_genome' { -// args = '--input_suffix .regions.bed.gz' -// publish_files = ['tsv':'', 'pdf': ''] -// publish_dir = 'variants/bowtie2/mosdepth/genome' -// } -// 'illumina_mosdepth_amplicon' { -// args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' -// publish_files = ['summary.txt':''] -// publish_dir = 'variants/bowtie2/mosdepth/amplicon' -// } -// 'illumina_plot_mosdepth_regions_amplicon' { -// args = '--input_suffix .regions.bed.gz' -// publish_files = ['tsv':'', 'pdf': ''] -// publish_dir = 'variants/bowtie2/mosdepth/amplicon' -// } -// 'illumina_ivar_variants' { -// args = '-t 0.25 -q 20 -m 10' -// args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0' -// publish_dir = 'variants/ivar' -// } -// 'illumina_ivar_variants_to_vcf' { -// publish_files = ['log':'log'] -// publish_dir = 'variants/ivar' -// } -// 'illumina_ivar_tabix_bgzip' { -// publish_dir = 'variants/ivar' -// } -// 'illumina_ivar_tabix_tabix' { -// args = '-p vcf -f' -// publish_dir = 'variants/ivar' -// } -// 'illumina_ivar_bcftools_stats' { -// publish_files = ['txt':'bcftools_stats'] -// publish_dir = 'variants/ivar' -// } -// 'illumina_ivar_consensus' { -// args = '-t 0.75 -q 20 -m 10 -n N' -// args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' -// suffix = '.consensus' -// publish_dir = 'variants/ivar/consensus' -// } -// 'illumina_ivar_consensus_plot' { -// suffix = '.consensus' -// publish_dir = 'variants/ivar/consensus/base_qc' -// } -// 'illumina_ivar_snpeff' { -// publish_files = ['csv':'', 'txt':'', 'html':''] -// publish_dir = 'variants/ivar/snpeff' -// } -// 'illumina_ivar_snpeff_bgzip' { -// suffix = '.snpeff' -// publish_dir = 'variants/ivar/snpeff' -// } -// 'illumina_ivar_snpeff_tabix' { -// args = '-p vcf -f' -// suffix = '.snpeff' -// publish_dir = 'variants/ivar/snpeff' -// } -// 'illumina_ivar_snpeff_stats' { -// suffix = '.snpeff' -// publish_files = ['txt':'bcftools_stats'] -// publish_dir = 'variants/ivar/snpeff' -// } -// 'illumina_ivar_snpsift' { -// publish_dir = 'variants/ivar/snpeff' -// } -// 'illumina_bcftools_mpileup' { -// args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' -// args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' -// args3 = "--include 'INFO/DP>=10'" -// publish_files = ['gz':'', 'gz.tbi':'', 'stats.txt':'bcftools_stats'] -// publish_dir = 'variants/bcftools' -// } -// 'illumina_bcftools_consensus_genomecov' { -// args = "-bga | awk '\$4 < 10'" -// suffix = '.coverage' -// publish_files = false -// publish_dir = 'variants/bcftools' -// } -// 'illumina_bcftools_consensus_merge' { -// suffix = '.coverage.merged' -// publish_files = false -// publish_dir = 'variants/bcftools' -// } -// 'illumina_bcftools_consensus_mask' { -// suffix = '.coverage.masked' -// publish_files = false -// publish_dir = 'variants/bcftools' -// } -// 'illumina_bcftools_consensus_maskfasta' { -// suffix = '.masked' -// publish_files = false -// publish_dir = 'variants/bcftools' -// } -// 'illumina_bcftools_consensus_bcftools' { -// suffix = '.consensus' -// publish_dir = 'variants/bcftools/consensus' -// } -// 'illumina_bcftools_consensus_plot' { -// suffix = '.consensus' -// publish_dir = 'variants/bcftools/consensus/base_qc' -// } -// 'illumina_bcftools_snpeff' { -// publish_files = ['csv':'', 'txt':'', 'html':''] -// publish_dir = 'variants/bcftools/snpeff' -// } -// 'illumina_bcftools_snpeff_bgzip' { -// suffix = '.snpeff' -// publish_dir = 'variants/bcftools/snpeff' -// } -// 'illumina_bcftools_snpeff_tabix' { -// args = '-p vcf -f' -// suffix = '.snpeff' -// publish_dir = 'variants/bcftools/snpeff' -// } -// 'illumina_bcftools_snpeff_stats' { -// suffix = '.snpeff' -// publish_files = ['txt':'bcftools_stats'] -// publish_dir = 'variants/bcftools/snpeff' -// } -// 'illumina_bcftools_snpsift' { -// publish_dir = 'variants/bcftools/snpeff' -// } -// 'illumina_bcftools_isec' { -// args = '--nfiles +2 --output-type z' -// publish_dir = 'variants/intersect' -// } - - - - - - -// 'illumina_cutadapt' { -// args = '--overlap 5 --minimum-length 30 --error-rate 0.1' -// suffix = '.primer_trim' -// publish_files = ['log':'log'] -// publish_dir = 'assembly/cutadapt' -// } -// 'illumina_cutadapt_fastqc' { -// args = '--quiet' -// suffix = 'primer_trim' -// publish_dir = 'assembly/cutadapt/fastqc' -// } -// 'illumina_spades' { -// args = '' -// publish_files = ['log':'log', 'fa':'', 'gfa':''] -// publish_dir = "assembly/spades/${params.spades_mode}" -// } -// 'illumina_spades_bandage' { -// args = '--height 1000' -// publish_dir = "assembly/spades/${params.spades_mode}/bandage" -// } -// 'illumina_spades_blastn' { -// args = "-outfmt '6 stitle std slen qlen qcovs'" -// publish_dir = "assembly/spades/${params.spades_mode}/blastn" -// } -// 'illumina_spades_blastn_filter' { -// suffix = '.filter.blastn' -// publish_dir = "assembly/spades/${params.spades_mode}/blastn" -// } -// 'illumina_spades_abacas' { -// args = '-m -p nucmer' -// publish_dir = "assembly/spades/${params.spades_mode}/abacas" -// } -// 'illumina_spades_plasmidid' { -// args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' -// publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] -// publish_dir = "assembly/spades/${params.spades_mode}/plasmidid" -// } -// 'illumina_spades_quast' { -// publish_files = ['quast':''] -// publish_dir = "assembly/spades/${params.spades_mode}" -// } -// 'illumina_unicycler' { -// publish_files = ['log':'log', 'fa':'', 'gfa':''] -// publish_dir = 'assembly/unicycler' -// } -// 'illumina_unicycler_bandage' { -// args = '--height 1000' -// publish_dir = 'assembly/unicycler/bandage' -// } -// 'illumina_unicycler_blastn' { -// args = "-outfmt '6 stitle std slen qlen qcovs'" -// publish_dir = 'assembly/unicycler/blastn' -// } -// 'illumina_unicycler_blastn_filter' { -// suffix = '.filter.blastn' -// publish_dir = 'assembly/unicycler/blastn' -// } -// 'illumina_unicycler_abacas' { -// args = '-m -p nucmer' -// publish_dir = 'assembly/unicycler/abacas' -// } -// 'illumina_unicycler_plasmidid' { -// args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' -// publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] -// publish_dir = 'assembly/unicycler/plasmidid' -// } -// 'illumina_unicycler_quast' { -// publish_files = ['quast':''] -// publish_dir = 'assembly/unicycler' -// } -// 'illumina_minia' { -// args = '-kmer-size 31 -abundance-min 20' -// publish_dir = 'assembly/minia' -// } -// 'illumina_minia_blastn' { -// args = "-outfmt '6 stitle std slen qlen qcovs'" -// publish_dir = 'assembly/minia/blastn' -// } -// 'illumina_minia_blastn_filter' { -// suffix = '.filter.blastn' -// publish_dir = 'assembly/minia/blastn' -// } -// 'illumina_minia_abacas' { -// args = '-m -p nucmer' -// publish_dir = 'assembly/minia/abacas' -// } -// 'illumina_minia_plasmidid' { -// args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' -// publish_files = ['html':'', 'tab':'', 'logs':'', 'images':''] -// publish_dir = 'assembly/minia/plasmidid' -// } -// 'illumina_minia_quast' { -// publish_files = ['quast':''] -// publish_dir = 'assembly/minia' -// } +if (!params.skip_assembly) { + if (!params.skip_cutadapt) { + process { + withName: BEDTOOLS_GETFASTA { + ext.args = '-s -nameOnly' + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + } + } + + if (!params.skip_blast) { + process { + withName: BLAST_MAKEBLASTDB { + ext.args = '-parse_seqids -dbtype nucl' + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + } + } +} + +// +// TODO +// + +if (!params.skip_variants) { + process { + withName: BCFTOOLS_MPILEUP { + ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' + ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' + ext.args3 = "--include 'INFO/DP>=10'" + } + + withName: BEDTOOLS_GENOMECOV { + ext.args = "-bga | awk '\$4 < 10'" + ext.prefix = { "${meta.id}.coverage" } + } + + withName: BEDTOOLS_MERGE { + ext.prefix = { "${meta.id}.coverage.merged" } + } + + withName: BCFTOOLS_CONSENSUS { + ext.prefix = { "${meta.id}.consensus" } + } + } +} From 7229c5a898e66a3a352ebfb5b54b15d5cd8855df Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 11 Jan 2022 15:29:22 +0000 Subject: [PATCH 014/238] Change channel structure for SPAdes --- subworkflows/local/assembly_spades.nf | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/subworkflows/local/assembly_spades.nf b/subworkflows/local/assembly_spades.nf index 01fa0b96..76ed233e 100644 --- a/subworkflows/local/assembly_spades.nf +++ b/subworkflows/local/assembly_spades.nf @@ -9,12 +9,13 @@ include { ASSEMBLY_QC } from './assembly_qc' workflow ASSEMBLY_SPADES { take: - reads // channel: [ val(meta), [ reads ] ] - hmm // channel: /path/to/spades.hmm - fasta // channel: /path/to/genome.fasta - gff // channel: /path/to/genome.gff - blast_db // channel: /path/to/blast_db/ - blast_header // channel: /path/to/blast_header.txt + reads // channel: [ val(meta), [ reads ] ] + mode // string : spades assembly mode e.g. 'rnaviral' + hmm // channel: /path/to/spades.hmm + fasta // channel: /path/to/genome.fasta + gff // channel: /path/to/genome.gff + blast_db // channel: /path/to/blast_db/ + blast_header // channel: /path/to/blast_header.txt main: @@ -24,11 +25,11 @@ workflow ASSEMBLY_SPADES { // Filter for paired-end samples if running metaSPAdes / metaviralSPAdes / metaplasmidSPAdes // ch_reads = reads - // if (params.spades_options.args.contains('--meta') || params.spades_options.args.contains('--bio')) { - // reads - // .filter { meta, fastq -> !meta.single_end } - // .set { ch_reads } - // } + if (mode.contains('meta') || mode.contains('bio')) { + reads + .filter { meta, fastq -> !meta.single_end } + .set { ch_reads } + } // // Assemble reads with SPAdes From af11f44e9daa18375e3829169855e1d74d5bd6ea Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 11 Jan 2022 15:36:09 +0000 Subject: [PATCH 015/238] Get end-to-end config working for Illumina --- conf/modules_illumina.config | 521 +++++++++++++++++++++++++++++++---- workflows/illumina.nf | 95 +++---- 2 files changed, 517 insertions(+), 99 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 91eb8a57..9458b941 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -14,7 +14,7 @@ def assemblers = params.assemblers ? params.assemblers.split(',').collect{ it.tr def callers = params.callers ? params.callers.split(',').collect{ it.trim().toLowerCase() } : [] // -// General configuration options +// Pre-processing and general configuration options // process { @@ -54,7 +54,7 @@ process { if (!params.skip_fastqc) { process { - withName: FASTQC_RAW { + withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_RAW' { ext.args = '--quiet' publishDir = [ path: { "${params.outdir}/fastqc/raw" }, @@ -63,7 +63,7 @@ if (!params.skip_fastqc) { ] } - withName: FASTQC_TRIM { + withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_TRIM' { ext.args = '--quiet' publishDir = [ path: { "${params.outdir}/fastqc/trim" }, @@ -123,7 +123,7 @@ if (!params.skip_kraken2) { } // -// Optional configuration options +// Variant calling configuration options // if (!params.skip_variants) { @@ -269,6 +269,27 @@ if (!params.skip_variants) { } } + if (!params.skip_picard_metrics) { + process { + withName: PICARD_COLLECTMULTIPLEMETRICS { + ext.args = 'VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' + ext.prefix = { "${meta.id}.markduplicates.sorted" } + publishDir = [ + [ + path: { "${params.outdir}/variants/bowtie2/picard_metrics" }, + mode: 'copy', + pattern: '*metrics' + ], + [ + path: { "${params.outdir}/variants/bowtie2/picard_metrics/pdf" }, + mode: 'copy', + pattern: '*.pdf' + ] + ] + } + } + } + if (!params.skip_mosdepth) { process { withName: COLLAPSE_PRIMERS { @@ -318,6 +339,167 @@ if (!params.skip_variants) { } } + if ('ivar' in callers) { + process { + withName: IVAR_VARIANTS { + ext.args = '-t 0.25 -q 20 -m 10' + ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0' + publishDir = [ + path: { "${params.outdir}/variants/ivar" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: IVAR_VARIANTS_TO_VCF { + publishDir = [ + path: { "${params.outdir}/variants/ivar/log" }, + mode: 'copy', + pattern: 'log' + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:TABIX_BGZIP' { + publishDir = [ + path: { "${params.outdir}/variants/ivar" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:.*:TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/variants/ivar" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:.*:BCFTOOLS_STATS' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: IVAR_CONSENSUS { + ext.args = '-t 0.75 -q 20 -m 10 -n N' + ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' + ext.prefix = { "${meta.id}.consensus" } + publishDir = [ + path: { "${params.outdir}/variants/ivar/consensus" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PLOT_BASE_DENSITY' { + ext.prefix = { "${meta.id}.consensus" } + publishDir = [ + path: { "${params.outdir}/variants/ivar/consensus/base_qc" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if ('bcftools' in callers) { + process { + withName: BCFTOOLS_MPILEUP { + ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' + ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' + ext.args3 = "--include 'INFO/DP>=10'" + publishDir = [ + [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: '*.{gz,tbi}' + ], + [ + path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, + mode: 'copy', + pattern: '*stats.txt' + ] + ] + } + + withName: BEDTOOLS_GENOMECOV { + ext.args = "-bga | awk '\$4 < 10'" + ext.prefix = { "${meta.id}.coverage" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } + + withName: BEDTOOLS_MERGE { + ext.prefix = { "${meta.id}.coverage.merged" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } + + withName: MAKE_BED_MASK { + ext.prefix = { "${meta.id}.coverage.masked" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } + + withName: BEDTOOLS_MASKFASTA { + ext.prefix = { "${meta.id}.masked" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } + + withName: BEDTOOLS_MASKFASTA { + ext.prefix = { "${meta.id}.masked" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } + + withName: BCFTOOLS_CONSENSUS { + ext.prefix = { "${meta.id}.consensus" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools/consensus" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:PLOT_BASE_DENSITY' { + ext.prefix = { "${meta.id}.consensus" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools/consensus/base_qc" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (callers.size() > 1) { + process { + withName: BCFTOOLS_ISEC { + ext.args = '--nfiles +2 --output-type z' + publishDir = [ + path: { "${params.outdir}/variants/intersect" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + if (!params.skip_pangolin) { if ('ivar' in callers) { process { @@ -541,35 +723,22 @@ if (!params.skip_variants) { } } -if (!params.skip_multiqc) { - process { - withName: MULTIQC { - ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' - publishDir = [ - [ - path: { "${params.outdir}/multiqc" }, - mode: 'copy', - pattern: 'multiqc*' - ], - [ - path: { "${params.outdir}/multiqc" }, - mode: 'copy', - pattern: '*variants_metrics_mqc.csv', - enabled: !params.skip_variants - ], - [ - path: { "${params.outdir}/multiqc" }, +if (!params.skip_assembly) { + if (!params.skip_blast) { + process { + withName: BLAST_MAKEBLASTDB { + ext.args = '-parse_seqids -dbtype nucl' + publishDir = [ + path: { "${params.outdir}/genome" }, mode: 'copy', - pattern: '*assembly_metrics_mqc.csv', - enabled: !params.skip_assembly + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference ] - ] + } } } -} -if (!params.skip_assembly) { - if (!params.skip_cutadapt) { + if (params.protocol == 'amplicon' && !params.skip_cutadapt) { process { withName: BEDTOOLS_GETFASTA { ext.args = '-s -nameOnly' @@ -580,47 +749,295 @@ if (!params.skip_assembly) { enabled: params.save_reference ] } + + withName: CUTADAPT { + ext.args = '--overlap 5 --minimum-length 30 --error-rate 0.1' + ext.prefix = { "${meta.id}.primer_trim" } + publishDir = [ + path: { "${params.outdir}/cutadapt/log" }, + mode: 'copy', + pattern: '*.log' + ] + } + } + + if (!params.skip_fastqc) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC' { + ext.args = '--quiet' + ext.prefix = { "${meta.id}.primer_trim" } + publishDir = [ + path: { "${params.outdir}/cutadapt/fastqc" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } } } - if (!params.skip_blast) { + if ('spades' in assemblers) { process { - withName: BLAST_MAKEBLASTDB { - ext.args = '-parse_seqids -dbtype nucl' + withName: SPADES { + ext.args = params.spades_mode ? "--${params.spades_mode}" : '' publishDir = [ - path: { "${params.outdir}/genome" }, + [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, + mode: 'copy', + pattern: '*.{fa,gfa}' + ], + [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/log" }, + mode: 'copy', + pattern: '*.log' + ] + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:BANDAGE_IMAGE' { + ext.args = '--height 1000' + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/bandage" }, mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:BLAST_BLASTN' { + ext.args = "-outfmt '6 stitle std slen qlen qcovs'" + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:FILTER_BLASTN' { + ext.prefix = { "${meta.id}.filter.blastn" } + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:QUAST' { + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, + mode: 'copy', + pattern: "quast" + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:ABACAS' { + ext.args = '-m -p nucmer' + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/abacas" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:PLASMIDID' { + ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' + publishDir = [ + [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, + mode: 'copy', + pattern: '*.{html,tab}' + ], + [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, + mode: 'copy', + pattern: 'logs' + ], + [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, + mode: 'copy', + pattern: 'images' + ] ] } } } -} -// -// TODO -// + if ('unicycler' in assemblers) { + process { + withName: UNICYCLER { + publishDir = [ + [ + path: { "${params.outdir}/assembly/unicycler" }, + mode: 'copy', + pattern: '*.{fa,gfa}' + ], + [ + path: { "${params.outdir}/assembly/unicycler/log" }, + mode: 'copy', + pattern: '*.log' + ] + ] + } -if (!params.skip_variants) { - process { - withName: BCFTOOLS_MPILEUP { - ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' - ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' - ext.args3 = "--include 'INFO/DP>=10'" - } + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:BANDAGE_IMAGE' { + ext.args = '--height 1000' + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/bandage" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:BLAST_BLASTN' { + ext.args = "-outfmt '6 stitle std slen qlen qcovs'" + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:FILTER_BLASTN' { + ext.prefix = { "${meta.id}.filter.blastn" } + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:QUAST' { + publishDir = [ + path: { "${params.outdir}/assembly/unicycler" }, + mode: 'copy', + pattern: "quast" + ] + } - withName: BEDTOOLS_GENOMECOV { - ext.args = "-bga | awk '\$4 < 10'" - ext.prefix = { "${meta.id}.coverage" } + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:ABACAS' { + ext.args = '-m -p nucmer' + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/abacas" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:PLASMIDID' { + ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' + publishDir = [ + [ + path: { "${params.outdir}/assembly/unicycler/plasmidid" }, + mode: 'copy', + pattern: '*.{html,tab}' + ], + [ + path: { "${params.outdir}/assembly/unicycler/plasmidid" }, + mode: 'copy', + pattern: 'logs' + ], + [ + path: { "${params.outdir}/assembly/unicycler/plasmidid" }, + mode: 'copy', + pattern: 'images' + ] + ] + } } + } + + if ('minia' in assemblers) { + process { + withName: MINIA { + ext.args = '-kmer-size 31 -abundance-min 20' + publishDir = [ + path: { "${params.outdir}/assembly/minia" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:BLAST_BLASTN' { + ext.args = "-outfmt '6 stitle std slen qlen qcovs'" + publishDir = [ + path: { "${params.outdir}/assembly/minia/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:FILTER_BLASTN' { + ext.prefix = { "${meta.id}.filter.blastn" } + publishDir = [ + path: { "${params.outdir}/assembly/minia/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:QUAST' { + publishDir = [ + path: { "${params.outdir}/assembly/minia" }, + mode: 'copy', + pattern: "quast" + ] + } - withName: BEDTOOLS_MERGE { - ext.prefix = { "${meta.id}.coverage.merged" } + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:ABACAS' { + ext.args = '-m -p nucmer' + publishDir = [ + path: { "${params.outdir}/assembly/minia/abacas" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:PLASMIDID' { + ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' + publishDir = [ + [ + path: { "${params.outdir}/assembly/minia/plasmidid" }, + mode: 'copy', + pattern: '*.{html,tab}' + ], + [ + path: { "${params.outdir}/assembly/minia/plasmidid" }, + mode: 'copy', + pattern: 'logs' + ], + [ + path: { "${params.outdir}/assembly/minia/plasmidid" }, + mode: 'copy', + pattern: 'images' + ] + ] + } } + } +} - withName: BCFTOOLS_CONSENSUS { - ext.prefix = { "${meta.id}.consensus" } +if (!params.skip_multiqc) { + process { + withName: MULTIQC { + ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' + publishDir = [ + [ + path: { "${params.outdir}/multiqc" }, + mode: 'copy', + pattern: 'multiqc*' + ], + [ + path: { "${params.outdir}/multiqc" }, + mode: 'copy', + pattern: '*variants_metrics_mqc.csv', + enabled: !params.skip_variants + ], + [ + path: { "${params.outdir}/multiqc" }, + mode: 'copy', + pattern: '*assembly_metrics_mqc.csv', + enabled: !params.skip_assembly + ] + ] } } } diff --git a/workflows/illumina.nf b/workflows/illumina.nf index b0ae3df5..bd7a6091 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -28,7 +28,7 @@ for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true ch_dummy_file = file("$projectDir/assets/dummy_file.txt", checkIfExists: true) if (params.input) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet file not specified!' } -if (params.spades_hmm) { ch_spades_hmm = file(params.spades_hmm) } else { ch_spades_hmm = ch_dummy_file } +if (params.spades_hmm) { ch_spades_hmm = file(params.spades_hmm) } else { ch_spades_hmm = [] } def assemblers = params.assemblers ? params.assemblers.split(',').collect{ it.trim().toLowerCase() } : [] def callers = params.callers ? params.callers.split(',').collect{ it.trim().toLowerCase() } : [] @@ -359,7 +359,7 @@ workflow ILLUMINA { MOSDEPTH_GENOME ( ch_bam.join(ch_bai, by: [0]), - ch_dummy_file, + [], 200 ) ch_mosdepth_multiqc = MOSDEPTH_GENOME.out.global_txt @@ -517,54 +517,55 @@ workflow ILLUMINA { } } - // // - // // SUBWORKFLOW: Run SPAdes assembly and downstream analysis - // // + // + // SUBWORKFLOW: Run SPAdes assembly and downstream analysis + // ch_spades_quast_multiqc = Channel.empty() - // if (!params.skip_assembly && 'spades' in assemblers) { - // ASSEMBLY_SPADES ( - // ch_assembly_fastq, - // ch_spades_hmm, - // PREPARE_GENOME.out.fasta, - // PREPARE_GENOME.out.gff, - // PREPARE_GENOME.out.blast_db, - // ch_blast_outfmt6_header - // ) - // ch_spades_quast_multiqc = ASSEMBLY_SPADES.out.quast_tsv - // ch_versions = ch_versions.mix(ASSEMBLY_SPADES.out.versions) - // } - - // // - // // SUBWORKFLOW: Run Unicycler assembly and downstream analysis - // // + if (!params.skip_assembly && 'spades' in assemblers) { + ASSEMBLY_SPADES ( + ch_assembly_fastq.map { meta, fastq -> [ meta, fastq, [], [] ] }, + params.spades_mode, + ch_spades_hmm, + PREPARE_GENOME.out.fasta, + PREPARE_GENOME.out.gff, + PREPARE_GENOME.out.blast_db, + ch_blast_outfmt6_header + ) + ch_spades_quast_multiqc = ASSEMBLY_SPADES.out.quast_tsv + ch_versions = ch_versions.mix(ASSEMBLY_SPADES.out.versions) + } + + // + // SUBWORKFLOW: Run Unicycler assembly and downstream analysis + // ch_unicycler_quast_multiqc = Channel.empty() - // if (!params.skip_assembly && 'unicycler' in assemblers) { - // ASSEMBLY_UNICYCLER ( - // ch_assembly_fastq, - // PREPARE_GENOME.out.fasta, - // PREPARE_GENOME.out.gff, - // PREPARE_GENOME.out.blast_db, - // ch_blast_outfmt6_header - // ) - // ch_unicycler_quast_multiqc = ASSEMBLY_UNICYCLER.out.quast_tsv - // ch_versions = ch_versions.mix(ASSEMBLY_UNICYCLER.out.versions) - // } - - // // - // // SUBWORKFLOW: Run minia assembly and downstream analysis - // // + if (!params.skip_assembly && 'unicycler' in assemblers) { + ASSEMBLY_UNICYCLER ( + ch_assembly_fastq.map { meta, fastq -> [ meta, fastq, [] ] }, + PREPARE_GENOME.out.fasta, + PREPARE_GENOME.out.gff, + PREPARE_GENOME.out.blast_db, + ch_blast_outfmt6_header + ) + ch_unicycler_quast_multiqc = ASSEMBLY_UNICYCLER.out.quast_tsv + ch_versions = ch_versions.mix(ASSEMBLY_UNICYCLER.out.versions) + } + + // + // SUBWORKFLOW: Run minia assembly and downstream analysis + // ch_minia_quast_multiqc = Channel.empty() - // if (!params.skip_assembly && 'minia' in assemblers) { - // ASSEMBLY_MINIA ( - // ch_assembly_fastq, - // PREPARE_GENOME.out.fasta, - // PREPARE_GENOME.out.gff, - // PREPARE_GENOME.out.blast_db, - // ch_blast_outfmt6_header - // ) - // ch_minia_quast_multiqc = ASSEMBLY_MINIA.out.quast_tsv - // ch_versions = ch_versions.mix(ASSEMBLY_MINIA.out.versions) - // } + if (!params.skip_assembly && 'minia' in assemblers) { + ASSEMBLY_MINIA ( + ch_assembly_fastq, + PREPARE_GENOME.out.fasta, + PREPARE_GENOME.out.gff, + PREPARE_GENOME.out.blast_db, + ch_blast_outfmt6_header + ) + ch_minia_quast_multiqc = ASSEMBLY_MINIA.out.quast_tsv + ch_versions = ch_versions.mix(ASSEMBLY_MINIA.out.versions) + } // // MODULE: Pipeline reporting From 4503b00bf6f54776fabf233e14a94cbc97f098e5 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 11 Jan 2022 17:32:14 +0000 Subject: [PATCH 016/238] Tweak config options and get assemblers working --- conf/modules_illumina.config | 38 ++++++++++++++++++++--- subworkflows/local/assembly_spades.nf | 29 ++++++++++++++---- subworkflows/local/assembly_unicycler.nf | 39 +++++++++++++++++------- 3 files changed, 84 insertions(+), 22 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 9458b941..0f69d00f 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -18,7 +18,8 @@ def callers = params.callers ? params.callers.split(',').collect{ it.trim( // process { - withName: 'GUNZIP_.*' { + + withName: 'NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:GUNZIP_.*' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -27,7 +28,7 @@ process { ] } - withName: 'UNTAR_.*' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:UNTAR_.*' { ext.args2 = '--no-same-owner' publishDir = [ path: { "${params.outdir}/genome" }, @@ -273,7 +274,6 @@ if (!params.skip_variants) { process { withName: PICARD_COLLECTMULTIPLEMETRICS { ext.args = 'VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' - ext.prefix = { "${meta.id}.markduplicates.sorted" } publishDir = [ [ path: { "${params.outdir}/variants/bowtie2/picard_metrics" }, @@ -754,7 +754,7 @@ if (!params.skip_assembly) { ext.args = '--overlap 5 --minimum-length 30 --error-rate 0.1' ext.prefix = { "${meta.id}.primer_trim" } publishDir = [ - path: { "${params.outdir}/cutadapt/log" }, + path: { "${params.outdir}/assembly/cutadapt/log" }, mode: 'copy', pattern: '*.log' ] @@ -767,7 +767,7 @@ if (!params.skip_assembly) { ext.args = '--quiet' ext.prefix = { "${meta.id}.primer_trim" } publishDir = [ - path: { "${params.outdir}/cutadapt/fastqc" }, + path: { "${params.outdir}/assembly/cutadapt/fastqc" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] @@ -794,6 +794,20 @@ if (!params.skip_assembly) { ] } + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:GUNZIP_SCAFFOLDS' { + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, + enabled: false + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:GUNZIP_GFA' { + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, + enabled: false + ] + } + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:BANDAGE_IMAGE' { ext.args = '--height 1000' publishDir = [ @@ -878,6 +892,20 @@ if (!params.skip_assembly) { ] } + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:GUNZIP_SCAFFOLDS' { + publishDir = [ + path: { "${params.outdir}/assembly/unicycler" }, + enabled: false + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:GUNZIP_GFA' { + publishDir = [ + path: { "${params.outdir}/assembly/unicycler" }, + enabled: false + ] + } + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:BANDAGE_IMAGE' { ext.args = '--height 1000' publishDir = [ diff --git a/subworkflows/local/assembly_spades.nf b/subworkflows/local/assembly_spades.nf index 76ed233e..d89624d2 100644 --- a/subworkflows/local/assembly_spades.nf +++ b/subworkflows/local/assembly_spades.nf @@ -2,8 +2,10 @@ // Assembly and downstream processing for SPAdes scaffolds // -include { SPADES } from '../../modules/nf-core/modules/spades/main' -include { BANDAGE_IMAGE } from '../../modules/nf-core/modules/bandage/image/main' +include { SPADES } from '../../modules/nf-core/modules/spades/main' +include { BANDAGE_IMAGE } from '../../modules/nf-core/modules/bandage/image/main' +include { GUNZIP as GUNZIP_SCAFFOLDS } from '../../modules/nf-core/modules/gunzip/main' +include { GUNZIP as GUNZIP_GFA } from '../../modules/nf-core/modules/gunzip/main' include { ASSEMBLY_QC } from './assembly_qc' @@ -40,18 +42,33 @@ workflow ASSEMBLY_SPADES { ) ch_versions = ch_versions.mix(SPADES.out.versions.first()) + // + // Unzip scaffolds file + // + GUNZIP_SCAFFOLDS ( + SPADES.out.scaffolds + ) + ch_versions = ch_versions.mix(GUNZIP_SCAFFOLDS.out.versions.first()) + + // + // Unzip gfa file + // + GUNZIP_GFA ( + SPADES.out.gfa + ) + // // Filter for empty scaffold files // - SPADES + GUNZIP_SCAFFOLDS .out - .scaffolds + .gunzip .filter { meta, scaffold -> scaffold.size() > 0 } .set { ch_scaffolds } - SPADES + GUNZIP_GFA .out - .gfa + .gunzip .filter { meta, gfa -> gfa.size() > 0 } .set { ch_gfa } diff --git a/subworkflows/local/assembly_unicycler.nf b/subworkflows/local/assembly_unicycler.nf index 79265a54..88df5ef5 100644 --- a/subworkflows/local/assembly_unicycler.nf +++ b/subworkflows/local/assembly_unicycler.nf @@ -2,18 +2,20 @@ // Assembly and downstream processing for Unicycler scaffolds // -include { UNICYCLER } from '../../modules/nf-core/modules/unicycler/main' -include { BANDAGE_IMAGE } from '../../modules/nf-core/modules/bandage/image/main' +include { UNICYCLER } from '../../modules/nf-core/modules/unicycler/main' +include { BANDAGE_IMAGE } from '../../modules/nf-core/modules/bandage/image/main' +include { GUNZIP as GUNZIP_SCAFFOLDS } from '../../modules/nf-core/modules/gunzip/main' +include { GUNZIP as GUNZIP_GFA } from '../../modules/nf-core/modules/gunzip/main' include { ASSEMBLY_QC } from './assembly_qc' workflow ASSEMBLY_UNICYCLER { take: - reads // channel: [ val(meta), [ reads ] ] - fasta // channel: /path/to/genome.fasta - gff // channel: /path/to/genome.gff - blast_db // channel: /path/to/blast_db/ - blast_header // channel: /path/to/blast_header.txt + reads // channel: [ val(meta), [ reads ] ] + fasta // channel: /path/to/genome.fasta + gff // channel: /path/to/genome.gff + blast_db // channel: /path/to/blast_db/ + blast_header // channel: /path/to/blast_header.txt main: @@ -27,18 +29,33 @@ workflow ASSEMBLY_UNICYCLER { ) ch_versions = ch_versions.mix(UNICYCLER.out.versions.first()) + // + // Unzip scaffolds file + // + GUNZIP_SCAFFOLDS ( + UNICYCLER.out.scaffolds + ) + ch_versions = ch_versions.mix(GUNZIP_SCAFFOLDS.out.versions.first()) + + // + // Unzip gfa file + // + GUNZIP_GFA ( + UNICYCLER.out.gfa + ) + // // Filter for empty scaffold files // - UNICYCLER + GUNZIP_SCAFFOLDS .out - .scaffolds + .gunzip .filter { meta, scaffold -> scaffold.size() > 0 } .set { ch_scaffolds } - UNICYCLER + GUNZIP_GFA .out - .gfa + .gunzip .filter { meta, gfa -> gfa.size() > 0 } .set { ch_gfa } From ec4b311def0e5250973d48e7a7a080137b544924 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 11 Jan 2022 18:16:39 +0000 Subject: [PATCH 017/238] Add quotes for process selectors --- conf/modules.config | 4 +- conf/modules_illumina.config | 72 ++++++++++++++++++------------------ conf/modules_nanopore.config | 40 ++++++++++---------- docs/output.md | 10 ++--- 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 38e8579e..64efa4c1 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -21,7 +21,7 @@ process { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - withName: SAMPLESHEET_CHECK { + withName: 'SAMPLESHEET_CHECK' { publishDir = [ path: { "${params.outdir}/pipeline_info" }, mode: 'copy', @@ -29,7 +29,7 @@ process { ] } - withName: CUSTOM_DUMPSOFTWAREVERSIONS { + withName: 'CUSTOM_DUMPSOFTWAREVERSIONS' { publishDir = [ path: { "${params.outdir}/pipeline_info" }, mode: 'copy', diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 0f69d00f..2c8ebb2b 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -38,7 +38,7 @@ process { ] } - withName: CAT_FASTQ { + withName: 'CAT_FASTQ' { publishDir = [ path: { "${params.outdir}/fastq" }, enabled: false @@ -77,7 +77,7 @@ if (!params.skip_fastqc) { if (!params.skip_fastp) { process { - withName: FASTP { + withName: 'FASTP' { ext.args = '--cut_front --cut_tail --trim_poly_x --cut_mean_quality 30 --qualified_quality_phred 30 --unqualified_percent_limit 10 --length_required 50' publishDir = [ [ @@ -103,7 +103,7 @@ if (!params.skip_fastp) { if (!params.skip_kraken2) { process { - withName: KRAKEN2_BUILD { + withName: 'KRAKEN2_BUILD' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -112,7 +112,7 @@ if (!params.skip_kraken2) { ] } - withName: KRAKEN2_KRAKEN2 { + withName: 'KRAKEN2_KRAKEN2' { ext.args = '--report-zero-counts' publishDir = [ path: { "${params.outdir}/kraken2" }, @@ -130,7 +130,7 @@ if (!params.skip_kraken2) { if (!params.skip_variants) { process { - withName: BOWTIE2_BUILD { + withName: 'BOWTIE2_BUILD' { ext.args = '--seed 1' publishDir = [ path: { "${params.outdir}/genome" }, @@ -140,7 +140,7 @@ if (!params.skip_variants) { ] } - withName: BOWTIE2_ALIGN { + withName: 'BOWTIE2_ALIGN' { ext.args = '--local --very-sensitive-local --seed 1' ext.args2 = '-F4' publishDir = [ @@ -186,7 +186,7 @@ if (!params.skip_variants) { if (!params.skip_ivar_trim && params.protocol == 'amplicon') { process { - withName: IVAR_TRIM { + withName: 'IVAR_TRIM' { ext.args = [ '-m 30 -q 20', params.ivar_trim_noprimer ? '' : '-e', @@ -272,7 +272,7 @@ if (!params.skip_variants) { if (!params.skip_picard_metrics) { process { - withName: PICARD_COLLECTMULTIPLEMETRICS { + withName: 'PICARD_COLLECTMULTIPLEMETRICS' { ext.args = 'VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp' publishDir = [ [ @@ -292,7 +292,7 @@ if (!params.skip_variants) { if (!params.skip_mosdepth) { process { - withName: COLLAPSE_PRIMERS { + withName: 'COLLAPSE_PRIMERS' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -301,7 +301,7 @@ if (!params.skip_variants) { ] } - withName: MOSDEPTH_GENOME { + withName: 'MOSDEPTH_GENOME' { ext.args = '--fast-mode' publishDir = [ path: { "${params.outdir}/variants/bowtie2/mosdepth/genome" }, @@ -310,7 +310,7 @@ if (!params.skip_variants) { ] } - withName: PLOT_MOSDEPTH_REGIONS_GENOME { + withName: 'PLOT_MOSDEPTH_REGIONS_GENOME' { ext.args = '--input_suffix .regions.bed.gz' publishDir = [ path: { "${params.outdir}/variants/bowtie2/mosdepth/genome" }, @@ -319,7 +319,7 @@ if (!params.skip_variants) { ] } - withName: MOSDEPTH_AMPLICON { + withName: 'MOSDEPTH_AMPLICON' { ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' publishDir = [ path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, @@ -328,7 +328,7 @@ if (!params.skip_variants) { ] } - withName: PLOT_MOSDEPTH_REGIONS_AMPLICON { + withName: 'PLOT_MOSDEPTH_REGIONS_AMPLICON' { ext.args = '--input_suffix .regions.bed.gz' publishDir = [ path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, @@ -341,7 +341,7 @@ if (!params.skip_variants) { if ('ivar' in callers) { process { - withName: IVAR_VARIANTS { + withName: 'IVAR_VARIANTS' { ext.args = '-t 0.25 -q 20 -m 10' ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0' publishDir = [ @@ -351,11 +351,11 @@ if (!params.skip_variants) { ] } - withName: IVAR_VARIANTS_TO_VCF { + withName: 'IVAR_VARIANTS_TO_VCF' { publishDir = [ path: { "${params.outdir}/variants/ivar/log" }, mode: 'copy', - pattern: 'log' + pattern: '*.log' ] } @@ -384,7 +384,7 @@ if (!params.skip_variants) { ] } - withName: IVAR_CONSENSUS { + withName: 'IVAR_CONSENSUS' { ext.args = '-t 0.75 -q 20 -m 10 -n N' ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' ext.prefix = { "${meta.id}.consensus" } @@ -408,7 +408,7 @@ if (!params.skip_variants) { if ('bcftools' in callers) { process { - withName: BCFTOOLS_MPILEUP { + withName: 'BCFTOOLS_MPILEUP' { ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' ext.args3 = "--include 'INFO/DP>=10'" @@ -426,7 +426,7 @@ if (!params.skip_variants) { ] } - withName: BEDTOOLS_GENOMECOV { + withName: 'BEDTOOLS_GENOMECOV' { ext.args = "-bga | awk '\$4 < 10'" ext.prefix = { "${meta.id}.coverage" } publishDir = [ @@ -435,7 +435,7 @@ if (!params.skip_variants) { ] } - withName: BEDTOOLS_MERGE { + withName: 'BEDTOOLS_MERGE' { ext.prefix = { "${meta.id}.coverage.merged" } publishDir = [ path: { "${params.outdir}/variants/bcftools" }, @@ -443,7 +443,7 @@ if (!params.skip_variants) { ] } - withName: MAKE_BED_MASK { + withName: 'MAKE_BED_MASK' { ext.prefix = { "${meta.id}.coverage.masked" } publishDir = [ path: { "${params.outdir}/variants/bcftools" }, @@ -451,7 +451,7 @@ if (!params.skip_variants) { ] } - withName: BEDTOOLS_MASKFASTA { + withName: 'BEDTOOLS_MASKFASTA' { ext.prefix = { "${meta.id}.masked" } publishDir = [ path: { "${params.outdir}/variants/bcftools" }, @@ -459,7 +459,7 @@ if (!params.skip_variants) { ] } - withName: BEDTOOLS_MASKFASTA { + withName: 'BEDTOOLS_MASKFASTA' { ext.prefix = { "${meta.id}.masked" } publishDir = [ path: { "${params.outdir}/variants/bcftools" }, @@ -467,7 +467,7 @@ if (!params.skip_variants) { ] } - withName: BCFTOOLS_CONSENSUS { + withName: 'BCFTOOLS_CONSENSUS' { ext.prefix = { "${meta.id}.consensus" } publishDir = [ path: { "${params.outdir}/variants/bcftools/consensus" }, @@ -489,7 +489,7 @@ if (!params.skip_variants) { if (callers.size() > 1) { process { - withName: BCFTOOLS_ISEC { + withName: 'BCFTOOLS_ISEC' { ext.args = '--nfiles +2 --output-type z' publishDir = [ path: { "${params.outdir}/variants/intersect" }, @@ -580,7 +580,7 @@ if (!params.skip_variants) { if (!params.skip_asciigenome) { process { - withName: CUSTOM_GETCHROMSIZES { + withName: 'CUSTOM_GETCHROMSIZES' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -617,7 +617,7 @@ if (!params.skip_variants) { if (!params.skip_snpeff) { process { - withName: SNPEFF_BUILD { + withName: 'SNPEFF_BUILD' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -726,7 +726,7 @@ if (!params.skip_variants) { if (!params.skip_assembly) { if (!params.skip_blast) { process { - withName: BLAST_MAKEBLASTDB { + withName: 'BLAST_MAKEBLASTDB' { ext.args = '-parse_seqids -dbtype nucl' publishDir = [ path: { "${params.outdir}/genome" }, @@ -740,7 +740,7 @@ if (!params.skip_assembly) { if (params.protocol == 'amplicon' && !params.skip_cutadapt) { process { - withName: BEDTOOLS_GETFASTA { + withName: 'BEDTOOLS_GETFASTA' { ext.args = '-s -nameOnly' publishDir = [ path: { "${params.outdir}/genome" }, @@ -750,7 +750,7 @@ if (!params.skip_assembly) { ] } - withName: CUTADAPT { + withName: 'CUTADAPT' { ext.args = '--overlap 5 --minimum-length 30 --error-rate 0.1' ext.prefix = { "${meta.id}.primer_trim" } publishDir = [ @@ -778,13 +778,13 @@ if (!params.skip_assembly) { if ('spades' in assemblers) { process { - withName: SPADES { + withName: 'SPADES' { ext.args = params.spades_mode ? "--${params.spades_mode}" : '' publishDir = [ [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, mode: 'copy', - pattern: '*.{fa,gfa}' + pattern: '*.{fa.gz,gfa.gz}' ], [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}/log" }, @@ -877,12 +877,12 @@ if (!params.skip_assembly) { if ('unicycler' in assemblers) { process { - withName: UNICYCLER { + withName: 'UNICYCLER' { publishDir = [ [ path: { "${params.outdir}/assembly/unicycler" }, mode: 'copy', - pattern: '*.{fa,gfa}' + pattern: '*.{fa.gz,gfa.gz}' ], [ path: { "${params.outdir}/assembly/unicycler/log" }, @@ -975,7 +975,7 @@ if (!params.skip_assembly) { if ('minia' in assemblers) { process { - withName: MINIA { + withName: 'MINIA' { ext.args = '-kmer-size 31 -abundance-min 20' publishDir = [ path: { "${params.outdir}/assembly/minia" }, @@ -1045,7 +1045,7 @@ if (!params.skip_assembly) { if (!params.skip_multiqc) { process { - withName: MULTIQC { + withName: 'MULTIQC' { ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' publishDir = [ [ diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 6a544ae8..9e59e252 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -31,7 +31,7 @@ process { ] } - withName: ARTIC_GUPPYPLEX { + withName: 'ARTIC_GUPPYPLEX' { ext.args = params.primer_set_version == 1200 ? '--min-length 250 --max-length 1500' : '--min-length 400 --max-length 700' publishDir = [ path: { "${params.outdir}/guppyplex" }, @@ -39,7 +39,7 @@ process { ] } - withName: ARTIC_MINION { + withName: 'ARTIC_MINION' { ext.args = [ '--normalise 500', params.artic_minion_caller == 'medaka' ? '--medaka' : '', @@ -95,7 +95,7 @@ process { if (params.sequencing_summary && !params.skip_pycoqc) { process { - withName: PYCOQC { + withName: 'PYCOQC' { publishDir = [ path: { "${params.outdir}/pycoqc" }, mode: 'copy', @@ -107,7 +107,7 @@ if (params.sequencing_summary && !params.skip_pycoqc) { if (!params.skip_nanoplot) { process { - withName: NANOPLOT { + withName: 'NANOPLOT' { publishDir = [ path: { "${params.outdir}/nanoplot/${meta.id}" }, mode: 'copy', @@ -119,7 +119,7 @@ if (!params.skip_nanoplot) { if (!params.skip_mosdepth) { process { - withName: COLLAPSE_PRIMERS { + withName: 'COLLAPSE_PRIMERS' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -128,7 +128,7 @@ if (!params.skip_mosdepth) { ] } - withName: MOSDEPTH_GENOME { + withName: 'MOSDEPTH_GENOME' { ext.args = '--fast-mode' publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, @@ -137,7 +137,7 @@ if (!params.skip_mosdepth) { ] } - withName: PLOT_MOSDEPTH_REGIONS_GENOME { + withName: 'PLOT_MOSDEPTH_REGIONS_GENOME' { ext.args = '--input_suffix .regions.bed.gz' publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, @@ -146,7 +146,7 @@ if (!params.skip_mosdepth) { ] } - withName: MOSDEPTH_AMPLICON { + withName: 'MOSDEPTH_AMPLICON' { ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, @@ -155,7 +155,7 @@ if (!params.skip_mosdepth) { ] } - withName: PLOT_MOSDEPTH_REGIONS_AMPLICON { + withName: 'PLOT_MOSDEPTH_REGIONS_AMPLICON' { ext.args = '--input_suffix .regions.bed.gz' publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, @@ -168,7 +168,7 @@ if (!params.skip_mosdepth) { if (!params.skip_pangolin) { process { - withName: PANGOLIN { + withName: 'PANGOLIN' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/pangolin" }, mode: 'copy', @@ -180,7 +180,7 @@ if (!params.skip_pangolin) { if (!params.skip_nextclade) { process { - withName: NEXTCLADE { + withName: 'NEXTCLADE' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/nextclade" }, mode: 'copy', @@ -192,7 +192,7 @@ if (!params.skip_nextclade) { if (!params.skip_variants_quast) { process { - withName: QUAST { + withName: 'QUAST' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}" }, mode: 'copy', @@ -204,7 +204,7 @@ if (!params.skip_variants_quast) { if (!params.skip_snpeff) { process { - withName: SNPEFF_BUILD { + withName: 'SNPEFF_BUILD' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -213,7 +213,7 @@ if (!params.skip_snpeff) { ] } - withName: SNPEFF_ANN { + withName: 'SNPEFF_ANN' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, mode: 'copy', @@ -221,7 +221,7 @@ if (!params.skip_snpeff) { ] } - withName: TABIX_BGZIP { + withName: 'TABIX_BGZIP' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, @@ -230,7 +230,7 @@ if (!params.skip_snpeff) { ] } - withName: TABIX_TABIX { + withName: 'TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, @@ -248,7 +248,7 @@ if (!params.skip_snpeff) { ] } - withName: SNPSIFT_EXTRACTFIELDS { + withName: 'SNPSIFT_EXTRACTFIELDS' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, mode: 'copy', @@ -260,7 +260,7 @@ if (!params.skip_snpeff) { if (!params.skip_asciigenome) { process { - withName: CUSTOM_GETCHROMSIZES { + withName: 'CUSTOM_GETCHROMSIZES' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -269,7 +269,7 @@ if (!params.skip_asciigenome) { ] } - withName: ASCIIGENOME { + withName: 'ASCIIGENOME' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/asciigenome/${meta.id}" }, mode: 'copy', @@ -281,7 +281,7 @@ if (!params.skip_asciigenome) { if (!params.skip_multiqc) { process { - withName: MULTIQC { + withName: 'MULTIQC' { ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' publishDir = [ path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, diff --git a/docs/output.md b/docs/output.md index 2f06f4b6..e0dcdf42 100644 --- a/docs/output.md +++ b/docs/output.md @@ -687,9 +687,9 @@ In the variant calling branch of the pipeline we are using [iVar trim](#ivar-tri

Output files * `assembly/spades//` - * `*.scaffolds.fa`: SPAdes scaffold assembly. - * `*.contigs.fa`: SPAdes assembly contigs. - * `*.assembly.gfa`: SPAdes assembly graph in [GFA](https://github.com/GFA-spec/GFA-spec/blob/master/GFA1.md) format. + * `*.scaffolds.fa.gz`: SPAdes scaffold assembly. + * `*.contigs.fa.gz`: SPAdes assembly contigs. + * `*.assembly.gfa.gz`: SPAdes assembly graph in [GFA](https://github.com/GFA-spec/GFA-spec/blob/master/GFA1.md) format. * `assembly/spades//bandage/` * `*.png`: Bandage visualisation for SPAdes assembly graph in PNG format. * `*.svg`: Bandage visualisation for SPAdes assembly graph in SVG format. @@ -708,8 +708,8 @@ In the variant calling branch of the pipeline we are using [iVar trim](#ivar-tri Output files * `assembly/unicycler/` - * `*.scaffolds.fa`: Unicycler scaffold assembly. - * `*.assembly.gfa`: Unicycler assembly graph in GFA format. + * `*.scaffolds.fa.gz`: Unicycler scaffold assembly. + * `*.assembly.gfa.gz`: Unicycler assembly graph in GFA format. * `assembly/unicycler/bandage/` * `*.png`: Bandage visualisation for Unicycler assembly graph in PNG format. * `*.svg`: Bandage visualisation for Unicycler assembly graph in SVG format. From d3cf23d4caacda66eeff9b1ffaabcc76410028bf Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 11 Jan 2022 18:18:20 +0000 Subject: [PATCH 018/238] Publish everything for plasmidID --- conf/modules_illumina.config | 54 ++++++------------------------------ 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 2c8ebb2b..40852d31 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -855,21 +855,9 @@ if (!params.skip_assembly) { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ - [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, - mode: 'copy', - pattern: '*.{html,tab}' - ], - [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, - mode: 'copy', - pattern: 'logs' - ], - [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, - mode: 'copy', - pattern: 'images' - ] + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } @@ -953,21 +941,9 @@ if (!params.skip_assembly) { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ - [ - path: { "${params.outdir}/assembly/unicycler/plasmidid" }, - mode: 'copy', - pattern: '*.{html,tab}' - ], - [ - path: { "${params.outdir}/assembly/unicycler/plasmidid" }, - mode: 'copy', - pattern: 'logs' - ], - [ - path: { "${params.outdir}/assembly/unicycler/plasmidid" }, - mode: 'copy', - pattern: 'images' - ] + path: { "${params.outdir}/assembly/unicycler/plasmidid" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } @@ -1022,21 +998,9 @@ if (!params.skip_assembly) { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ - [ - path: { "${params.outdir}/assembly/minia/plasmidid" }, - mode: 'copy', - pattern: '*.{html,tab}' - ], - [ - path: { "${params.outdir}/assembly/minia/plasmidid" }, - mode: 'copy', - pattern: 'logs' - ], - [ - path: { "${params.outdir}/assembly/minia/plasmidid" }, - mode: 'copy', - pattern: 'images' - ] + path: { "${params.outdir}/assembly/minia/plasmidid" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } From 8e1d5282a41caf245239e5fd8b63d6857e9ad7d1 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 11 Jan 2022 20:47:27 +0000 Subject: [PATCH 019/238] Update output docs --- docs/output.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/output.md b/docs/output.md index e0dcdf42..115fbe04 100644 --- a/docs/output.md +++ b/docs/output.md @@ -833,15 +833,13 @@ An example MultiQC report generated from a full-sized dataset can be viewed on t Output files * `genome/` - * Unzipped genome fasta file for viral genome - * Unzipped genome annotation GFF file for viral genome -* `genome/index/` * `bowtie2/`: Bowtie 2 index for viral genome. -* `genome/db/` * `blast_db/`: BLAST database for viral genome. * `kraken2_db/`: Kraken 2 database for host genome. * `snpeff_db/`: SnpEff database for viral genome. * `snpeff.config`: SnpEff config file for viral genome. + * Unzipped genome fasta file for viral genome + * Unzipped genome annotation GFF file for viral genome From 95eeceb70f79647c32d3969ec3a80318a9cb3d69 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 11 Jan 2022 21:38:37 +0000 Subject: [PATCH 020/238] Re-install artic minion module --- modules.json | 2 +- modules/nf-core/modules/artic/minion/main.nf | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules.json b/modules.json index de6d718f..11bb1a81 100644 --- a/modules.json +++ b/modules.json @@ -10,7 +10,7 @@ "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "artic/minion": { - "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" + "git_sha": "bdd8159c535e740ae6b831de6f28cc2ceda14ba9" }, "bandage/image": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" diff --git a/modules/nf-core/modules/artic/minion/main.nf b/modules/nf-core/modules/artic/minion/main.nf index ce04fcc8..ed3c64d7 100644 --- a/modules/nf-core/modules/artic/minion/main.nf +++ b/modules/nf-core/modules/artic/minion/main.nf @@ -43,7 +43,10 @@ process ARTIC_MINION { summary = "" model = file(medaka_model).exists() ? "--medaka-model ./$medaka_model" : "--medaka-model $medaka_model" } + def hd5_plugin_path = task.ext.hd5_plugin_path ? "HDF5_PLUGIN_PATH=" + task.ext.hd5_plugin_path : "HDF5_PLUGIN_PATH=/usr/local/lib/python3.6/site-packages/ont_fast5_api/vbz_plugin" """ + $hd5_plugin_path + artic \\ minion \\ $args \\ From c9e671f22fb48443986276200b1f04bec841c88f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 11 Jan 2022 21:49:44 +0000 Subject: [PATCH 021/238] Fix SPAdes filtering --- subworkflows/local/assembly_spades.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/assembly_spades.nf b/subworkflows/local/assembly_spades.nf index d89624d2..46457c23 100644 --- a/subworkflows/local/assembly_spades.nf +++ b/subworkflows/local/assembly_spades.nf @@ -29,7 +29,7 @@ workflow ASSEMBLY_SPADES { ch_reads = reads if (mode.contains('meta') || mode.contains('bio')) { reads - .filter { meta, fastq -> !meta.single_end } + .filter { meta, illumina, pacbio, nanopore -> !meta.single_end } .set { ch_reads } } From 0fb3b5e39be4d37cd29e35f50165afe9d7176687 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 12 Jan 2022 11:20:39 +0000 Subject: [PATCH 022/238] Remove dummy file from illumina workflow --- subworkflows/local/prepare_genome_illumina.nf | 5 +---- workflows/illumina.nf | 11 +++-------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/subworkflows/local/prepare_genome_illumina.nf b/subworkflows/local/prepare_genome_illumina.nf index 7febda1d..09363f4c 100644 --- a/subworkflows/local/prepare_genome_illumina.nf +++ b/subworkflows/local/prepare_genome_illumina.nf @@ -18,9 +18,6 @@ include { KRAKEN2_BUILD } from '../../modules/local/kraken2_buil include { SNPEFF_BUILD } from '../../modules/local/snpeff_build' workflow PREPARE_GENOME { - take: - dummy_file - main: ch_versions = Channel.empty() @@ -52,7 +49,7 @@ workflow PREPARE_GENOME { ch_gff = file(params.gff) } } else { - ch_gff = dummy_file + ch_gff = [] } // diff --git a/workflows/illumina.nf b/workflows/illumina.nf index bd7a6091..b1c7e0df 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -24,9 +24,6 @@ def checkPathParamList = [ ] for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } -// Stage dummy file to be used as an optional input where required -ch_dummy_file = file("$projectDir/assets/dummy_file.txt", checkIfExists: true) - if (params.input) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet file not specified!' } if (params.spades_hmm) { ch_spades_hmm = file(params.spades_hmm) } else { ch_spades_hmm = [] } @@ -120,9 +117,7 @@ workflow ILLUMINA { // // SUBWORKFLOW: Uncompress and prepare reference genome files // - PREPARE_GENOME ( - ch_dummy_file - ) + PREPARE_GENOME () ch_versions = ch_versions.mix(PREPARE_GENOME.out.versions) // Check genome fasta only contains a single contig @@ -402,7 +397,7 @@ workflow ILLUMINA { ch_bam, PREPARE_GENOME.out.fasta, PREPARE_GENOME.out.chrom_sizes, - params.gff ? PREPARE_GENOME.out.gff : [], + PREPARE_GENOME.out.gff, (params.protocol == 'amplicon' && params.primer_bed) ? PREPARE_GENOME.out.primer_bed : [], PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config, @@ -451,7 +446,7 @@ workflow ILLUMINA { ch_bam, PREPARE_GENOME.out.fasta, PREPARE_GENOME.out.chrom_sizes, - params.gff ? PREPARE_GENOME.out.gff : [], + PREPARE_GENOME.out.gff, (params.protocol == 'amplicon' && params.primer_bed) ? PREPARE_GENOME.out.primer_bed : [], PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config From 4f0b2a551a8d7e8a1052ca8439ecb789efa9b90f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 12 Jan 2022 11:31:41 +0000 Subject: [PATCH 023/238] Remove dummy file from ONT workflow --- assets/dummy_file.txt | 0 subworkflows/local/prepare_genome_nanopore.nf | 5 +--- workflows/nanopore.nf | 28 +++++++++---------- 3 files changed, 15 insertions(+), 18 deletions(-) delete mode 100644 assets/dummy_file.txt diff --git a/assets/dummy_file.txt b/assets/dummy_file.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/subworkflows/local/prepare_genome_nanopore.nf b/subworkflows/local/prepare_genome_nanopore.nf index f5520c5e..6d18c487 100644 --- a/subworkflows/local/prepare_genome_nanopore.nf +++ b/subworkflows/local/prepare_genome_nanopore.nf @@ -10,9 +10,6 @@ include { COLLAPSE_PRIMERS } from '../../modules/local/collapse_prime include { SNPEFF_BUILD } from '../../modules/local/snpeff_build' workflow PREPARE_GENOME { - take: - dummy_file - main: ch_versions = Channel.empty() @@ -44,7 +41,7 @@ workflow PREPARE_GENOME { ch_gff = file(params.gff) } } else { - ch_gff = dummy_file + ch_gff = [] } // diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 73f8ef58..5c1ebac2 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -20,16 +20,9 @@ def checkPathParamList = [ ] for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } -// Stage dummy file to be used as an optional input where required -ch_dummy_file = file("$projectDir/assets/dummy_file.txt", checkIfExists: true) - -// MultiQC config files -ch_multiqc_config = file("$projectDir/assets/multiqc_config_nanopore.yaml", checkIfExists: true) -ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config) : Channel.empty() - if (params.input) { ch_input = file(params.input) } -if (params.fast5_dir) { ch_fast5_dir = file(params.fast5_dir) } else { ch_fast5_dir = ch_dummy_file } -if (params.sequencing_summary) { ch_sequencing_summary = file(params.sequencing_summary) } else { ch_sequencing_summary = ch_multiqc_config } +if (params.fast5_dir) { ch_fast5_dir = file(params.fast5_dir) } else { ch_fast5_dir = [] } +if (params.sequencing_summary) { ch_sequencing_summary = file(params.sequencing_summary) } else { ch_sequencing_summary = [] } // Need to stage medaka model properly depending on whether it is a string or a file ch_medaka_model = Channel.empty() @@ -39,6 +32,15 @@ if (params.artic_minion_caller == 'medaka') { } } +/* +======================================================================================== + CONFIG FILES +======================================================================================== +*/ + +ch_multiqc_config = file("$projectDir/assets/multiqc_config_nanopore.yaml", checkIfExists: true) +ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config) : Channel.empty() + /* ======================================================================================== IMPORT LOCAL MODULES/SUBWORKFLOWS @@ -121,9 +123,7 @@ workflow NANOPORE { // // SUBWORKFLOW: Uncompress and prepare reference genome files // - PREPARE_GENOME ( - ch_dummy_file - ) + PREPARE_GENOME () ch_versions = ch_versions.mix(PREPARE_GENOME.out.versions) // Check primer BED file only contains suffixes provided --primer_left_suffix / --primer_right_suffix @@ -320,7 +320,7 @@ workflow NANOPORE { MOSDEPTH_GENOME ( ARTIC_MINION.out.bam_primertrimmed.join(ARTIC_MINION.out.bai_primertrimmed, by: [0]), - ch_dummy_file, + [], 200 ) ch_mosdepth_multiqc = MOSDEPTH_GENOME.out.global_txt @@ -438,7 +438,7 @@ workflow NANOPORE { ch_asciigenome, PREPARE_GENOME.out.fasta, PREPARE_GENOME.out.chrom_sizes, - params.gff ? PREPARE_GENOME.out.gff : [], + PREPARE_GENOME.out.gff, PREPARE_GENOME.out.primer_bed, params.asciigenome_window_size, params.asciigenome_read_depth From 35bf4b6bf7aaf3e9788e6543d97f13a4d92c46a2 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 12 Jan 2022 13:39:13 +0000 Subject: [PATCH 024/238] merge stash --- modules/local/multiqc_nanopore.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/local/multiqc_nanopore.nf b/modules/local/multiqc_nanopore.nf index 698bf0e6..ea09fa64 100644 --- a/modules/local/multiqc_nanopore.nf +++ b/modules/local/multiqc_nanopore.nf @@ -41,10 +41,10 @@ process MULTIQC { multiqc -f $args $custom_config . ## Parse YAML files dumped by MultiQC to obtain metrics - multiqc_to_custom_csv.py --platform nanopore + #multiqc_to_custom_csv.py --platform nanopore ## Manually remove files that we don't want in the report - rm -rf quast + #rm -rf quast ## Run MultiQC a second time multiqc -f $args -e general_stats --ignore *nextclade_clade_mqc.tsv $custom_config . From e1dbdd8e0cc65b54d319044df34cda831f4bbb0a Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 12 Jan 2022 16:30:32 +0000 Subject: [PATCH 025/238] Remove remaining params in modules --- modules/local/kraken2_build.nf | 6 +++--- modules/local/multiqc_illumina.nf | 2 +- modules/local/multiqc_nanopore.nf | 2 +- workflows/illumina.nf | 4 ++-- workflows/nanopore.nf | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/local/kraken2_build.nf b/modules/local/kraken2_build.nf index 48cd575e..22c9c2de 100644 --- a/modules/local/kraken2_build.nf +++ b/modules/local/kraken2_build.nf @@ -2,10 +2,10 @@ process KRAKEN2_BUILD { tag "$library" label 'process_high' - conda (params.enable_conda ? 'bioconda::kraken2=2.1.1 conda-forge::pigz=2.6' : null) + conda (params.enable_conda ? 'bioconda::kraken2=2.1.2 conda-forge::pigz=2.6' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' : - 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0' : + 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0' }" input: val library diff --git a/modules/local/multiqc_illumina.nf b/modules/local/multiqc_illumina.nf index d0637f8c..2a4a9be2 100644 --- a/modules/local/multiqc_illumina.nf +++ b/modules/local/multiqc_illumina.nf @@ -48,7 +48,7 @@ process MULTIQC { script: def args = task.ext.args ?: '' - def custom_config = params.multiqc_config ? "--config $multiqc_custom_config" : '' + def custom_config = multiqc_custom_config ? "--config $multiqc_custom_config" : '' """ ## Run MultiQC once to parse tool logs multiqc -f $args $custom_config . diff --git a/modules/local/multiqc_nanopore.nf b/modules/local/multiqc_nanopore.nf index 698bf0e6..f1cb728d 100644 --- a/modules/local/multiqc_nanopore.nf +++ b/modules/local/multiqc_nanopore.nf @@ -35,7 +35,7 @@ process MULTIQC { script: def args = task.ext.args ?: '' - def custom_config = params.multiqc_config ? "--config $multiqc_custom_config" : '' + def custom_config = multiqc_custom_config ? "--config $multiqc_custom_config" : '' """ ## Run MultiQC once to parse tool logs multiqc -f $args $custom_config . diff --git a/workflows/illumina.nf b/workflows/illumina.nf index b1c7e0df..21d8b8f9 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -38,7 +38,7 @@ if (!callers) { callers = params.protocol == 'amplicon' ? ['ivar'] : ['bcftools */ ch_multiqc_config = file("$projectDir/assets/multiqc_config_illumina.yaml", checkIfExists: true) -ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config) : Channel.empty() +ch_multiqc_custom_config = params.multiqc_config ? file(params.multiqc_config) : [] // Header files ch_blast_outfmt6_header = file("$projectDir/assets/headers/blast_outfmt6_header.txt", checkIfExists: true) @@ -578,7 +578,7 @@ workflow ILLUMINA { MULTIQC ( ch_multiqc_config, - ch_multiqc_custom_config.collect().ifEmpty([]), + ch_multiqc_custom_config, CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect(), ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml'), ch_fail_reads_multiqc.ifEmpty([]), diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 5c1ebac2..a127e2df 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -39,7 +39,7 @@ if (params.artic_minion_caller == 'medaka') { */ ch_multiqc_config = file("$projectDir/assets/multiqc_config_nanopore.yaml", checkIfExists: true) -ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath(params.multiqc_config) : Channel.empty() +ch_multiqc_custom_config = params.multiqc_config ? file(params.multiqc_config) : [] /* ======================================================================================== @@ -462,7 +462,7 @@ workflow NANOPORE { MULTIQC ( ch_multiqc_config, - ch_multiqc_custom_config.collect().ifEmpty([]), + ch_multiqc_custom_config, CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect(), ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml'), ch_custom_no_sample_name_multiqc.ifEmpty([]), From b1d209dae2c8a22cbf103448037278ff607e503e Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 12 Jan 2022 19:44:43 +0000 Subject: [PATCH 026/238] Update nf-core ivar/variants module --- modules.json | 2 +- modules/nf-core/modules/ivar/variants/main.nf | 7 ++++--- modules/nf-core/modules/ivar/variants/meta.yml | 3 +++ subworkflows/local/variants_ivar.nf | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/modules.json b/modules.json index 11bb1a81..d35fa61b 100644 --- a/modules.json +++ b/modules.json @@ -73,7 +73,7 @@ "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "ivar/variants": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "b4c6b430d0ae4899551bb44dbb78d6270c31e0b2" }, "kraken2/kraken2": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" diff --git a/modules/nf-core/modules/ivar/variants/main.nf b/modules/nf-core/modules/ivar/variants/main.nf index ce4abd4d..fda6e0cc 100644 --- a/modules/nf-core/modules/ivar/variants/main.nf +++ b/modules/nf-core/modules/ivar/variants/main.nf @@ -11,6 +11,7 @@ process IVAR_VARIANTS { tuple val(meta), path(bam) path fasta path gff + val save_mpileup output: tuple val(meta), path("*.tsv") , emit: tsv @@ -21,14 +22,14 @@ process IVAR_VARIANTS { def args = task.ext.args ?: '' def args2 = task.ext.args2 ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - def save_mpileup = params.save_mpileup ? "tee ${prefix}.mpileup |" : "" - def features = params.gff ? "-g $gff" : "" + def features = gff ? "-g $gff" : "" + def mpileup = save_mpileup ? "tee ${prefix}.mpileup |" : "" """ samtools mpileup \\ $args2 \\ --reference $fasta \\ $bam | \\ - $save_mpileup \\ + $mpileup \\ ivar variants \\ $args \\ $features \\ diff --git a/modules/nf-core/modules/ivar/variants/meta.yml b/modules/nf-core/modules/ivar/variants/meta.yml index fd3fce9e..7c4297ca 100644 --- a/modules/nf-core/modules/ivar/variants/meta.yml +++ b/modules/nf-core/modules/ivar/variants/meta.yml @@ -29,6 +29,9 @@ input: type: file description: A GFF file in the GFF3 format can be supplied to specify coordinates of open reading frames (ORFs). In absence of GFF file, amino acid translation will not be done. patter: "*.gff" + - save_mpileup: + type: boolean + description: Save mpileup file generated by ivar variants output: - meta: type: map diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index 18949e99..480f1ff3 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -35,7 +35,8 @@ workflow VARIANTS_IVAR { IVAR_VARIANTS ( bam, fasta, - gff + gff, + params.save_mpileup ) ch_versions = ch_versions.mix(IVAR_VARIANTS.out.versions.first()) From 61702d0907f2620fb9945244852159e9d99bbf60 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 12 Jan 2022 19:53:32 +0000 Subject: [PATCH 027/238] export the HDF5_PLUGIN_PATH variable --- modules/nf-core/modules/artic/minion/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nf-core/modules/artic/minion/main.nf b/modules/nf-core/modules/artic/minion/main.nf index ed3c64d7..c25ec0db 100644 --- a/modules/nf-core/modules/artic/minion/main.nf +++ b/modules/nf-core/modules/artic/minion/main.nf @@ -43,7 +43,7 @@ process ARTIC_MINION { summary = "" model = file(medaka_model).exists() ? "--medaka-model ./$medaka_model" : "--medaka-model $medaka_model" } - def hd5_plugin_path = task.ext.hd5_plugin_path ? "HDF5_PLUGIN_PATH=" + task.ext.hd5_plugin_path : "HDF5_PLUGIN_PATH=/usr/local/lib/python3.6/site-packages/ont_fast5_api/vbz_plugin" + def hd5_plugin_path = task.ext.hd5_plugin_path ? "export HDF5_PLUGIN_PATH=" + task.ext.hd5_plugin_path : "export HDF5_PLUGIN_PATH=/usr/local/lib/python3.6/site-packages/ont_fast5_api/vbz_plugin" """ $hd5_plugin_path From f97861fe622855378669e0e6adacaf79c7b941ce Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 12 Jan 2022 19:55:10 +0000 Subject: [PATCH 028/238] supply MULTIQC_TSVs processes argument as a list --- modules/local/multiqc_nanopore.nf | 4 ++-- workflows/illumina.nf | 8 ++++---- workflows/nanopore.nf | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/local/multiqc_nanopore.nf b/modules/local/multiqc_nanopore.nf index ea09fa64..698bf0e6 100644 --- a/modules/local/multiqc_nanopore.nf +++ b/modules/local/multiqc_nanopore.nf @@ -41,10 +41,10 @@ process MULTIQC { multiqc -f $args $custom_config . ## Parse YAML files dumped by MultiQC to obtain metrics - #multiqc_to_custom_csv.py --platform nanopore + multiqc_to_custom_csv.py --platform nanopore ## Manually remove files that we don't want in the report - #rm -rf quast + rm -rf quast ## Run MultiQC a second time multiqc -f $args -e general_stats --ignore *nextclade_clade_mqc.tsv $custom_config . diff --git a/workflows/illumina.nf b/workflows/illumina.nf index bd7a6091..309bd6d2 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -224,7 +224,7 @@ workflow ILLUMINA { MULTIQC_TSV_FAIL_READS ( ch_pass_fail_reads.collect(), - 'Sample\tReads before trimming', + ['Sample', 'Reads before trimming'], 'fail_mapped_reads' ) .set { ch_fail_reads_multiqc } @@ -304,7 +304,7 @@ workflow ILLUMINA { MULTIQC_TSV_FAIL_MAPPED ( ch_pass_fail_mapped.fail.collect(), - 'Sample\tMapped reads', + ['Sample', 'Mapped reads'], 'fail_mapped_samples' ) .set { ch_fail_mapping_multiqc } @@ -430,7 +430,7 @@ workflow ILLUMINA { MULTIQC_TSV_IVAR_NEXTCLADE ( ch_ivar_nextclade_multiqc.collect(), - 'Sample\tclade', + ['Sample', 'clade'], 'ivar_nextclade_clade' ) .set { ch_ivar_nextclade_multiqc } @@ -477,7 +477,7 @@ workflow ILLUMINA { MULTIQC_TSV_BCFTOOLS_NEXTCLADE ( ch_bcftools_nextclade_multiqc.collect(), - 'Sample\tclade', + ['Sample', 'clade'], 'bcftools_nextclade_clade' ) .set { ch_bcftools_nextclade_multiqc } diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 73f8ef58..7924ba21 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -143,7 +143,7 @@ workflow NANOPORE { .map { dir -> def count = 0 for (x in dir.listFiles()) { - if (x.isFile() && x.toString().endsWith('.fastq')) { + if (x.isFile() && x.toString().contains('.fastq')) { count += x.countFastq() } } @@ -175,7 +175,7 @@ workflow NANOPORE { MULTIQC_TSV_NO_SAMPLE_NAME ( ch_barcodes_no_sample.collect(), - 'Barcode\tRead count', + ['Barcode', 'Read count'], 'fail_barcodes_no_sample' ) .set { ch_custom_no_sample_name_multiqc } @@ -190,7 +190,7 @@ workflow NANOPORE { MULTIQC_TSV_NO_BARCODES ( ch_samples_no_barcode.collect(), - 'Sample\tMissing barcode', + ['Sample', 'Missing barcode'], 'fail_no_barcode_samples' ) .set { ch_custom_no_barcodes_multiqc } @@ -231,7 +231,7 @@ workflow NANOPORE { MULTIQC_TSV_BARCODE_COUNT ( ch_pass_fail_barcode_count.fail.collect(), - 'Sample\tBarcode count', + ['Sample', 'Barcode count'], 'fail_barcode_count_samples' ) @@ -266,7 +266,7 @@ workflow NANOPORE { MULTIQC_TSV_GUPPYPLEX_COUNT ( ch_pass_fail_guppyplex_count.fail.collect(), - 'Sample\tRead count', + ['Sample', 'Read count'], 'fail_guppyplex_count_samples' ) @@ -381,7 +381,7 @@ workflow NANOPORE { MULTIQC_TSV_NEXTCLADE ( ch_nextclade_multiqc.collect(), - 'Sample\tclade', + ['Sample', 'clade'], 'nextclade_clade' ) .set { ch_nextclade_multiqc } From c3c9142ae3346db6629748db27d5e69c8cba4a01 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 13 Jan 2022 12:03:13 +0000 Subject: [PATCH 029/238] Bump mulled containers and update modules --- modules.json | 8 ++++---- modules/nf-core/modules/artic/minion/main.nf | 2 +- modules/nf-core/modules/bowtie2/align/main.nf | 14 +++++++------- modules/nf-core/modules/kraken2/kraken2/main.nf | 6 +++--- modules/nf-core/modules/samtools/view/main.nf | 2 ++ 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/modules.json b/modules.json index d35fa61b..59d7ff84 100644 --- a/modules.json +++ b/modules.json @@ -10,7 +10,7 @@ "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "artic/minion": { - "git_sha": "bdd8159c535e740ae6b831de6f28cc2ceda14ba9" + "git_sha": "104f896a268df93876c284fdc9e604015bcca243" }, "bandage/image": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" @@ -43,7 +43,7 @@ "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "bowtie2/align": { - "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + "git_sha": "3f9a0285816a2e0ae73a3875fb5ae4b409da5952" }, "bowtie2/build": { "git_sha": "e3285528aca2733ff2d544cb5e5fcc34599226f3" @@ -76,7 +76,7 @@ "git_sha": "b4c6b430d0ae4899551bb44dbb78d6270c31e0b2" }, "kraken2/kraken2": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "3f9a0285816a2e0ae73a3875fb5ae4b409da5952" }, "minia": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" @@ -127,7 +127,7 @@ "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "samtools/view": { - "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + "git_sha": "05ba4d901db380c6def3bc242ab18a2d88b25819" }, "spades": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" diff --git a/modules/nf-core/modules/artic/minion/main.nf b/modules/nf-core/modules/artic/minion/main.nf index ed3c64d7..c25ec0db 100644 --- a/modules/nf-core/modules/artic/minion/main.nf +++ b/modules/nf-core/modules/artic/minion/main.nf @@ -43,7 +43,7 @@ process ARTIC_MINION { summary = "" model = file(medaka_model).exists() ? "--medaka-model ./$medaka_model" : "--medaka-model $medaka_model" } - def hd5_plugin_path = task.ext.hd5_plugin_path ? "HDF5_PLUGIN_PATH=" + task.ext.hd5_plugin_path : "HDF5_PLUGIN_PATH=/usr/local/lib/python3.6/site-packages/ont_fast5_api/vbz_plugin" + def hd5_plugin_path = task.ext.hd5_plugin_path ? "export HDF5_PLUGIN_PATH=" + task.ext.hd5_plugin_path : "export HDF5_PLUGIN_PATH=/usr/local/lib/python3.6/site-packages/ont_fast5_api/vbz_plugin" """ $hd5_plugin_path diff --git a/modules/nf-core/modules/bowtie2/align/main.nf b/modules/nf-core/modules/bowtie2/align/main.nf index 04f817ec..20b08f72 100644 --- a/modules/nf-core/modules/bowtie2/align/main.nf +++ b/modules/nf-core/modules/bowtie2/align/main.nf @@ -2,10 +2,10 @@ process BOWTIE2_ALIGN { tag "$meta.id" label 'process_high' - conda (params.enable_conda ? 'bioconda::bowtie2=2.4.2 bioconda::samtools=1.11 conda-forge::pigz=2.3.4' : null) + conda (params.enable_conda ? 'bioconda::bowtie2=2.4.4 bioconda::samtools=1.14 conda-forge::pigz=2.6' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0' : - 'quay.io/biocontainers/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:4d235f41348a00533f18e47c9669f1ecb327f629-0' : + 'quay.io/biocontainers/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:4d235f41348a00533f18e47c9669f1ecb327f629-0' }" input: tuple val(meta), path(reads) @@ -13,10 +13,10 @@ process BOWTIE2_ALIGN { val save_unaligned output: - tuple val(meta), path('*.bam'), emit: bam - tuple val(meta), path('*.log'), emit: log - path "versions.yml" , emit: versions - tuple val(meta), path('*fastq.gz'), optional:true, emit: fastq + tuple val(meta), path('*.bam') , emit: bam + tuple val(meta), path('*.log') , emit: log + tuple val(meta), path('*fastq.gz'), emit: fastq, optional:true + path "versions.yml" , emit: versions script: def args = task.ext.args ?: '' diff --git a/modules/nf-core/modules/kraken2/kraken2/main.nf b/modules/nf-core/modules/kraken2/kraken2/main.nf index 3c4d1caf..eaabb229 100644 --- a/modules/nf-core/modules/kraken2/kraken2/main.nf +++ b/modules/nf-core/modules/kraken2/kraken2/main.nf @@ -2,10 +2,10 @@ process KRAKEN2_KRAKEN2 { tag "$meta.id" label 'process_high' - conda (params.enable_conda ? 'bioconda::kraken2=2.1.1 conda-forge::pigz=2.6' : null) + conda (params.enable_conda ? 'bioconda::kraken2=2.1.2 conda-forge::pigz=2.6' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' : - 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0' : + 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0' }" input: tuple val(meta), path(reads) diff --git a/modules/nf-core/modules/samtools/view/main.nf b/modules/nf-core/modules/samtools/view/main.nf index fb31f70b..cb205d0b 100644 --- a/modules/nf-core/modules/samtools/view/main.nf +++ b/modules/nf-core/modules/samtools/view/main.nf @@ -18,6 +18,7 @@ process SAMTOOLS_VIEW { script: def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' def prefix = task.ext.prefix ?: "${meta.id}" def reference = fasta ? "--reference ${fasta} -C" : "" def file_type = input.getExtension() @@ -29,6 +30,7 @@ process SAMTOOLS_VIEW { ${reference} \\ $args \\ $input \\ + $args2 \\ > ${prefix}.${file_type} cat <<-END_VERSIONS > versions.yml From ca24d7aae24854313a7d8631979a348f1f2de2bc Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 13 Jan 2022 17:22:10 +0000 Subject: [PATCH 030/238] Finish syncing publishing logic in config with workflow --- conf/modules_illumina.config | 647 +++++++++++++++++++---------------- 1 file changed, 360 insertions(+), 287 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 40852d31..cbccb72f 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -18,7 +18,6 @@ def callers = params.callers ? params.callers.split(',').collect{ it.trim( // process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:GUNZIP_.*' { publishDir = [ path: { "${params.outdir}/genome" }, @@ -44,13 +43,6 @@ process { enabled: false ] } - - withName: 'MULTIQC_TSV_.*' { - publishDir = [ - path: { "${params.outdir}/multiqc" }, - enabled: false - ] - } } if (!params.skip_fastqc) { @@ -63,15 +55,6 @@ if (!params.skip_fastqc) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_TRIM' { - ext.args = '--quiet' - publishDir = [ - path: { "${params.outdir}/fastqc/trim" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } } } @@ -98,6 +81,24 @@ if (!params.skip_fastp) { ] ] } + + withName: 'MULTIQC_TSV_FAIL_READS' { + publishDir = [ + path: { "${params.outdir}/multiqc" }, + enabled: false + ] + } + } + + if (!params.skip_fastqc) { + withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_TRIM' { + ext.args = '--quiet' + publishDir = [ + path: { "${params.outdir}/fastqc/trim" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } } @@ -128,7 +129,6 @@ if (!params.skip_kraken2) { // if (!params.skip_variants) { - process { withName: 'BOWTIE2_BUILD' { ext.args = '--seed 1' @@ -182,6 +182,39 @@ if (!params.skip_variants) { pattern: "*.{stats,flagstat,idxstats}" ] } + + withName: 'MULTIQC_TSV_FAIL_MAPPED' { + publishDir = [ + path: { "${params.outdir}/multiqc" }, + enabled: false + ] + } + } + + if (!params.skip_asciigenome) { + process { + withName: 'CUSTOM_GETCHROMSIZES' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + } + } + + if (!params.skip_snpeff) { + process { + withName: 'SNPEFF_BUILD' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + } } if (!params.skip_ivar_trim && params.protocol == 'amplicon') { @@ -292,15 +325,6 @@ if (!params.skip_variants) { if (!params.skip_mosdepth) { process { - withName: 'COLLAPSE_PRIMERS' { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - withName: 'MOSDEPTH_GENOME' { ext.args = '--fast-mode' publishDir = [ @@ -318,6 +342,17 @@ if (!params.skip_variants) { pattern: "*.{tsv,pdf}" ] } + } + + if (params.protocol == 'amplicon') { + withName: 'COLLAPSE_PRIMERS' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } withName: 'MOSDEPTH_AMPLICON' { ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' @@ -403,6 +438,108 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } + + withName: 'MULTIQC_TSV_IVAR_NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/multiqc" }, + enabled: false + ] + } + } + + if (!params.skip_pangolin) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PANGOLIN' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/pangolin" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_nextclade) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/nextclade" }, + mode: 'copy', + pattern: "*.{csv}" + ] + } + } + } + + if (!params.skip_variants_quast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:QUAST' { + publishDir = [ + path: { "${params.outdir}/variants/ivar" }, + mode: 'copy', + pattern: "quast" + ] + } + } + } + + if (!params.skip_asciigenome) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:ASCIIGENOME' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/asciigenome/${meta.id}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_snpeff) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPEFF_ANN' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff" }, + mode: 'copy', + pattern: "*.{csv,txt,html}" + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } } } @@ -484,36 +621,16 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - } - } - if (callers.size() > 1) { - process { - withName: 'BCFTOOLS_ISEC' { - ext.args = '--nfiles +2 --output-type z' + withName: 'MULTIQC_TSV_BCFTOOLS_NEXTCLADE' { publishDir = [ - path: { "${params.outdir}/variants/intersect" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + path: { "${params.outdir}/multiqc" }, + enabled: false ] } } - } - - if (!params.skip_pangolin) { - if ('ivar' in callers) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PANGOLIN' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/pangolin" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - if ('bcftools' in callers) { + if (!params.skip_pangolin) { process { withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:PANGOLIN' { publishDir = [ @@ -524,22 +641,8 @@ if (!params.skip_variants) { } } } - } - if (!params.skip_nextclade) { - if ('ivar' in callers) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/nextclade" }, - mode: 'copy', - pattern: "*.{csv}" - ] - } - } - } - - if ('bcftools' in callers) { + if (!params.skip_nextclade) { process { withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE' { publishDir = [ @@ -550,22 +653,8 @@ if (!params.skip_variants) { } } } - } - - if (!params.skip_variants_quast) { - if ('ivar' in callers) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:QUAST' { - publishDir = [ - path: { "${params.outdir}/variants/ivar" }, - mode: 'copy', - pattern: "quast" - ] - } - } - } - if ('bcftools' in callers) { + if (!params.skip_variants_quast) { process { withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:QUAST' { publishDir = [ @@ -576,33 +665,8 @@ if (!params.skip_variants) { } } } - } - - if (!params.skip_asciigenome) { - process { - withName: 'CUSTOM_GETCHROMSIZES' { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - } - - if ('ivar' in callers) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:ASCIIGENOME' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/asciigenome/${meta.id}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - if ('bcftools' in callers) { + if (!params.skip_asciigenome) { process { withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:ASCIIGENOME' { publishDir = [ @@ -613,68 +677,8 @@ if (!params.skip_variants) { } } } - } - - if (!params.skip_snpeff) { - process { - withName: 'SNPEFF_BUILD' { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - } - - if ('ivar' in callers) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPEFF_ANN' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff" }, - mode: 'copy', - pattern: "*.{csv,txt,html}" - ] - } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { - ext.prefix = { "${meta.id}.snpeff" } - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { - ext.args = '-p vcf -f' - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { - ext.prefix = { "${meta.id}.snpeff" } - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff/bcftools_stats" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - if ('bcftools' in callers) { + if (!params.skip_snpeff) { process { withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPEFF_ANN' { publishDir = [ @@ -721,6 +725,19 @@ if (!params.skip_variants) { } } } + + if (callers.size() > 1) { + process { + withName: 'BCFTOOLS_ISEC' { + ext.args = '--nfiles +2 --output-type z' + publishDir = [ + path: { "${params.outdir}/variants/intersect" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } } if (!params.skip_assembly) { @@ -807,58 +824,78 @@ if (!params.skip_assembly) { enabled: false ] } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:BANDAGE_IMAGE' { - ext.args = '--height 1000' - publishDir = [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}/bandage" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.skip_bandage) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:BANDAGE_IMAGE' { + ext.args = '--height 1000' + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/bandage" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:BLAST_BLASTN' { - ext.args = "-outfmt '6 stitle std slen qlen qcovs'" - publishDir = [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}/blastn" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + if (!params.skip_blast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:BLAST_BLASTN' { + ext.args = "-outfmt '6 stitle std slen qlen qcovs'" + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:FILTER_BLASTN' { - ext.prefix = { "${meta.id}.filter.blastn" } - publishDir = [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}/blastn" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:FILTER_BLASTN' { + ext.prefix = { "${meta.id}.filter.blastn" } + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:QUAST' { - publishDir = [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, - mode: 'copy', - pattern: "quast" - ] + if (!params.skip_assembly_quast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:QUAST' { + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, + mode: 'copy', + pattern: "quast" + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:ABACAS' { - ext.args = '-m -p nucmer' - publishDir = [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}/abacas" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.skip_abacas) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:ABACAS' { + ext.args = '-m -p nucmer' + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/abacas" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:PLASMIDID' { - ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' - publishDir = [ - path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.skip_plasmidid) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:PLASMIDID' { + ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' + publishDir = [ + path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } } } @@ -893,58 +930,78 @@ if (!params.skip_assembly) { enabled: false ] } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:BANDAGE_IMAGE' { - ext.args = '--height 1000' - publishDir = [ - path: { "${params.outdir}/assembly/unicycler/bandage" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.skip_bandage) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:BANDAGE_IMAGE' { + ext.args = '--height 1000' + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/bandage" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:BLAST_BLASTN' { - ext.args = "-outfmt '6 stitle std slen qlen qcovs'" - publishDir = [ - path: { "${params.outdir}/assembly/unicycler/blastn" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + if (!params.skip_blast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:BLAST_BLASTN' { + ext.args = "-outfmt '6 stitle std slen qlen qcovs'" + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:FILTER_BLASTN' { - ext.prefix = { "${meta.id}.filter.blastn" } - publishDir = [ - path: { "${params.outdir}/assembly/unicycler/blastn" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:FILTER_BLASTN' { + ext.prefix = { "${meta.id}.filter.blastn" } + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:QUAST' { - publishDir = [ - path: { "${params.outdir}/assembly/unicycler" }, - mode: 'copy', - pattern: "quast" - ] + if (!params.skip_assembly_quast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:QUAST' { + publishDir = [ + path: { "${params.outdir}/assembly/unicycler" }, + mode: 'copy', + pattern: "quast" + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:ABACAS' { - ext.args = '-m -p nucmer' - publishDir = [ - path: { "${params.outdir}/assembly/unicycler/abacas" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.skip_abacas) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:ABACAS' { + ext.args = '-m -p nucmer' + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/abacas" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:PLASMIDID' { - ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' - publishDir = [ - path: { "${params.outdir}/assembly/unicycler/plasmidid" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.skip_plasmidid) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:PLASMIDID' { + ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' + publishDir = [ + path: { "${params.outdir}/assembly/unicycler/plasmidid" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } } } @@ -959,49 +1016,65 @@ if (!params.skip_assembly) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:BLAST_BLASTN' { - ext.args = "-outfmt '6 stitle std slen qlen qcovs'" - publishDir = [ - path: { "${params.outdir}/assembly/minia/blastn" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + if (!params.skip_blast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:BLAST_BLASTN' { + ext.args = "-outfmt '6 stitle std slen qlen qcovs'" + publishDir = [ + path: { "${params.outdir}/assembly/minia/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:FILTER_BLASTN' { - ext.prefix = { "${meta.id}.filter.blastn" } - publishDir = [ - path: { "${params.outdir}/assembly/minia/blastn" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:FILTER_BLASTN' { + ext.prefix = { "${meta.id}.filter.blastn" } + publishDir = [ + path: { "${params.outdir}/assembly/minia/blastn" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:QUAST' { - publishDir = [ - path: { "${params.outdir}/assembly/minia" }, - mode: 'copy', - pattern: "quast" - ] + if (!params.skip_assembly_quast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:QUAST' { + publishDir = [ + path: { "${params.outdir}/assembly/minia" }, + mode: 'copy', + pattern: "quast" + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:ABACAS' { - ext.args = '-m -p nucmer' - publishDir = [ - path: { "${params.outdir}/assembly/minia/abacas" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.skip_abacas) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:ABACAS' { + ext.args = '-m -p nucmer' + publishDir = [ + path: { "${params.outdir}/assembly/minia/abacas" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:PLASMIDID' { - ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' - publishDir = [ - path: { "${params.outdir}/assembly/minia/plasmidid" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + if (!params.skip_plasmidid) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:PLASMIDID' { + ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' + publishDir = [ + path: { "${params.outdir}/assembly/minia/plasmidid" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } } } From 2446c456a87b919718cab6dba1be12f0de2f1327 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 13 Jan 2022 17:35:26 +0000 Subject: [PATCH 031/238] Add logic for --skip_consensus too --- conf/modules_illumina.config | 240 ++++++++++++++++++----------------- nextflow.config | 6 +- 2 files changed, 127 insertions(+), 119 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index cbccb72f..78d7905f 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -419,26 +419,6 @@ if (!params.skip_variants) { ] } - withName: 'IVAR_CONSENSUS' { - ext.args = '-t 0.75 -q 20 -m 10 -n N' - ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' - ext.prefix = { "${meta.id}.consensus" } - publishDir = [ - path: { "${params.outdir}/variants/ivar/consensus" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PLOT_BASE_DENSITY' { - ext.prefix = { "${meta.id}.consensus" } - publishDir = [ - path: { "${params.outdir}/variants/ivar/consensus/base_qc" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: 'MULTIQC_TSV_IVAR_NEXTCLADE' { publishDir = [ path: { "${params.outdir}/multiqc" }, @@ -447,38 +427,62 @@ if (!params.skip_variants) { } } - if (!params.skip_pangolin) { + if (!params.skip_consensus) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PANGOLIN' { + withName: 'IVAR_CONSENSUS' { + ext.args = '-t 0.75 -q 20 -m 10 -n N' + ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' + ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/ivar/pangolin" }, + path: { "${params.outdir}/variants/ivar/consensus" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - } - } - if (!params.skip_nextclade) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PLOT_BASE_DENSITY' { + ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/ivar/nextclade" }, + path: { "${params.outdir}/variants/ivar/consensus/base_qc" }, mode: 'copy', - pattern: "*.{csv}" + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } - } - if (!params.skip_variants_quast) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:QUAST' { - publishDir = [ - path: { "${params.outdir}/variants/ivar" }, - mode: 'copy', - pattern: "quast" - ] + if (!params.skip_pangolin) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PANGOLIN' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/pangolin" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_nextclade) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/nextclade" }, + mode: 'copy', + pattern: "*.{csv}" + ] + } + } + } + + if (!params.skip_variants_quast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:QUAST' { + publishDir = [ + path: { "${params.outdir}/variants/ivar" }, + mode: 'copy', + pattern: "quast" + ] + } } } } @@ -563,105 +567,109 @@ if (!params.skip_variants) { ] } - withName: 'BEDTOOLS_GENOMECOV' { - ext.args = "-bga | awk '\$4 < 10'" - ext.prefix = { "${meta.id}.coverage" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - enabled: false - ] - } - - withName: 'BEDTOOLS_MERGE' { - ext.prefix = { "${meta.id}.coverage.merged" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - enabled: false - ] - } - - withName: 'MAKE_BED_MASK' { - ext.prefix = { "${meta.id}.coverage.masked" } + withName: 'MULTIQC_TSV_BCFTOOLS_NEXTCLADE' { publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/multiqc" }, enabled: false ] } + } - withName: 'BEDTOOLS_MASKFASTA' { - ext.prefix = { "${meta.id}.masked" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - enabled: false - ] - } + if (!params.skip_consensus) { + process { + withName: 'BEDTOOLS_GENOMECOV' { + ext.args = "-bga | awk '\$4 < 10'" + ext.prefix = { "${meta.id}.coverage" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } - withName: 'BEDTOOLS_MASKFASTA' { - ext.prefix = { "${meta.id}.masked" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - enabled: false - ] - } + withName: 'BEDTOOLS_MERGE' { + ext.prefix = { "${meta.id}.coverage.merged" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } - withName: 'BCFTOOLS_CONSENSUS' { - ext.prefix = { "${meta.id}.consensus" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools/consensus" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + withName: 'MAKE_BED_MASK' { + ext.prefix = { "${meta.id}.coverage.masked" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:PLOT_BASE_DENSITY' { - ext.prefix = { "${meta.id}.consensus" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools/consensus/base_qc" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + withName: 'BEDTOOLS_MASKFASTA' { + ext.prefix = { "${meta.id}.masked" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } - withName: 'MULTIQC_TSV_BCFTOOLS_NEXTCLADE' { - publishDir = [ - path: { "${params.outdir}/multiqc" }, - enabled: false - ] - } - } + withName: 'BEDTOOLS_MASKFASTA' { + ext.prefix = { "${meta.id}.masked" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } - if (!params.skip_pangolin) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:PANGOLIN' { + withName: 'BCFTOOLS_CONSENSUS' { + ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/bcftools/pangolin" }, + path: { "${params.outdir}/variants/bcftools/consensus" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - } - } - if (!params.skip_nextclade) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:PLOT_BASE_DENSITY' { + ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/bcftools/nextclade" }, + path: { "${params.outdir}/variants/bcftools/consensus/base_qc" }, mode: 'copy', - pattern: "*.{csv}" + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } - } - if (!params.skip_variants_quast) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:QUAST' { - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - mode: 'copy', - pattern: "quast" - ] + if (!params.skip_pangolin) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:PANGOLIN' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/pangolin" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_nextclade) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/nextclade" }, + mode: 'copy', + pattern: "*.{csv}" + ] + } + } + } + + if (!params.skip_variants_quast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:QUAST' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: "quast" + ] + } } } } diff --git a/nextflow.config b/nextflow.config index aa3ba0d6..d249c26e 100644 --- a/nextflow.config +++ b/nextflow.config @@ -43,9 +43,10 @@ params { max_multiqc_email_size = '25.MB' skip_mosdepth = false skip_pangolin = false - skip_nextclade = false - skip_asciigenome = false + skip_nextclade = fals skip_variants_quast = false + skip_snpeff = false + skip_asciigenome = false skip_multiqc = false // Illumina QC, read trimming and filtering options @@ -70,7 +71,6 @@ params { skip_ivar_trim = false skip_markduplicates = true skip_picard_metrics = false - skip_snpeff = false skip_consensus = false skip_variants = false From 94fc2859c0912096872ed47735dbe8b5589a3b12 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 13 Jan 2022 17:37:52 +0000 Subject: [PATCH 032/238] Fix tyop --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index d249c26e..1f0470a3 100644 --- a/nextflow.config +++ b/nextflow.config @@ -43,7 +43,7 @@ params { max_multiqc_email_size = '25.MB' skip_mosdepth = false skip_pangolin = false - skip_nextclade = fals + skip_nextclade = false skip_variants_quast = false skip_snpeff = false skip_asciigenome = false From e1cce190fb8b61d124fa5b5d462600bc0b2c05b4 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Thu, 13 Jan 2022 18:45:34 +0000 Subject: [PATCH 033/238] update modules --- modules.json | 8 ++++---- modules/nf-core/modules/bowtie2/align/main.nf | 14 +++++++------- modules/nf-core/modules/ivar/variants/main.nf | 7 ++++--- modules/nf-core/modules/ivar/variants/meta.yml | 3 +++ modules/nf-core/modules/kraken2/kraken2/main.nf | 6 +++--- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/modules.json b/modules.json index 11bb1a81..e08e3c16 100644 --- a/modules.json +++ b/modules.json @@ -10,7 +10,7 @@ "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "artic/minion": { - "git_sha": "bdd8159c535e740ae6b831de6f28cc2ceda14ba9" + "git_sha": "104f896a268df93876c284fdc9e604015bcca243" }, "bandage/image": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" @@ -43,7 +43,7 @@ "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "bowtie2/align": { - "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + "git_sha": "3f9a0285816a2e0ae73a3875fb5ae4b409da5952" }, "bowtie2/build": { "git_sha": "e3285528aca2733ff2d544cb5e5fcc34599226f3" @@ -73,10 +73,10 @@ "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "ivar/variants": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "b4c6b430d0ae4899551bb44dbb78d6270c31e0b2" }, "kraken2/kraken2": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "3f9a0285816a2e0ae73a3875fb5ae4b409da5952" }, "minia": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" diff --git a/modules/nf-core/modules/bowtie2/align/main.nf b/modules/nf-core/modules/bowtie2/align/main.nf index 04f817ec..20b08f72 100644 --- a/modules/nf-core/modules/bowtie2/align/main.nf +++ b/modules/nf-core/modules/bowtie2/align/main.nf @@ -2,10 +2,10 @@ process BOWTIE2_ALIGN { tag "$meta.id" label 'process_high' - conda (params.enable_conda ? 'bioconda::bowtie2=2.4.2 bioconda::samtools=1.11 conda-forge::pigz=2.3.4' : null) + conda (params.enable_conda ? 'bioconda::bowtie2=2.4.4 bioconda::samtools=1.14 conda-forge::pigz=2.6' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0' : - 'quay.io/biocontainers/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:577a697be67b5ae9b16f637fd723b8263a3898b3-0' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:4d235f41348a00533f18e47c9669f1ecb327f629-0' : + 'quay.io/biocontainers/mulled-v2-ac74a7f02cebcfcc07d8e8d1d750af9c83b4d45a:4d235f41348a00533f18e47c9669f1ecb327f629-0' }" input: tuple val(meta), path(reads) @@ -13,10 +13,10 @@ process BOWTIE2_ALIGN { val save_unaligned output: - tuple val(meta), path('*.bam'), emit: bam - tuple val(meta), path('*.log'), emit: log - path "versions.yml" , emit: versions - tuple val(meta), path('*fastq.gz'), optional:true, emit: fastq + tuple val(meta), path('*.bam') , emit: bam + tuple val(meta), path('*.log') , emit: log + tuple val(meta), path('*fastq.gz'), emit: fastq, optional:true + path "versions.yml" , emit: versions script: def args = task.ext.args ?: '' diff --git a/modules/nf-core/modules/ivar/variants/main.nf b/modules/nf-core/modules/ivar/variants/main.nf index ce4abd4d..fda6e0cc 100644 --- a/modules/nf-core/modules/ivar/variants/main.nf +++ b/modules/nf-core/modules/ivar/variants/main.nf @@ -11,6 +11,7 @@ process IVAR_VARIANTS { tuple val(meta), path(bam) path fasta path gff + val save_mpileup output: tuple val(meta), path("*.tsv") , emit: tsv @@ -21,14 +22,14 @@ process IVAR_VARIANTS { def args = task.ext.args ?: '' def args2 = task.ext.args2 ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - def save_mpileup = params.save_mpileup ? "tee ${prefix}.mpileup |" : "" - def features = params.gff ? "-g $gff" : "" + def features = gff ? "-g $gff" : "" + def mpileup = save_mpileup ? "tee ${prefix}.mpileup |" : "" """ samtools mpileup \\ $args2 \\ --reference $fasta \\ $bam | \\ - $save_mpileup \\ + $mpileup \\ ivar variants \\ $args \\ $features \\ diff --git a/modules/nf-core/modules/ivar/variants/meta.yml b/modules/nf-core/modules/ivar/variants/meta.yml index fd3fce9e..7c4297ca 100644 --- a/modules/nf-core/modules/ivar/variants/meta.yml +++ b/modules/nf-core/modules/ivar/variants/meta.yml @@ -29,6 +29,9 @@ input: type: file description: A GFF file in the GFF3 format can be supplied to specify coordinates of open reading frames (ORFs). In absence of GFF file, amino acid translation will not be done. patter: "*.gff" + - save_mpileup: + type: boolean + description: Save mpileup file generated by ivar variants output: - meta: type: map diff --git a/modules/nf-core/modules/kraken2/kraken2/main.nf b/modules/nf-core/modules/kraken2/kraken2/main.nf index 3c4d1caf..eaabb229 100644 --- a/modules/nf-core/modules/kraken2/kraken2/main.nf +++ b/modules/nf-core/modules/kraken2/kraken2/main.nf @@ -2,10 +2,10 @@ process KRAKEN2_KRAKEN2 { tag "$meta.id" label 'process_high' - conda (params.enable_conda ? 'bioconda::kraken2=2.1.1 conda-forge::pigz=2.6' : null) + conda (params.enable_conda ? 'bioconda::kraken2=2.1.2 conda-forge::pigz=2.6' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' : - 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:941789bd7fe00db16531c26de8bf3c5c985242a5-0' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0' : + 'quay.io/biocontainers/mulled-v2-5799ab18b5fc681e75923b2450abaa969907ec98:87fc08d11968d081f3e8a37131c1f1f6715b6542-0' }" input: tuple val(meta), path(reads) From ab6c69217a6e0270c0ae68e3d5b60af9354759d0 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 13 Jan 2022 20:10:21 +0000 Subject: [PATCH 034/238] Update docs for accepting gzip compressed files --- CHANGELOG.md | 1 + docs/usage.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2914ed33..e967f3fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Port pipeline to the updated Nextflow DSL2 syntax adopted on nf-core/modules * Bump minimum Nextflow version from `21.04.0` -> `21.10.3` * Updated pipeline template to [nf-core/tools 2.2](https://github.com/nf-core/tools/releases/tag/2.2) +* [[#218](https://github.com/nf-core/viralrecon/issues/218)] - Support for compressed FastQ files for Nanopore data ### Parameters diff --git a/docs/usage.md b/docs/usage.md index 45d8090e..1f492cd6 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -64,7 +64,7 @@ For Nanopore data the pipeline only supports amplicon-based analysis obtained fr ### Nanopolish -The default variant caller used by artic minion is [Nanopolish](https://github.com/jts/nanopolish) and this requires that you provide `*.fastq`, `*.fast5` and `sequencing_summary.txt` files as input to the pipeline. These files can typically be obtained after demultiplexing and basecalling the sequencing data using [Guppy](https://nanoporetech.com/nanopore-sequencing-data-analysis) (see [ARTIC SOP docs](https://artic.network/ncov-2019/ncov2019-bioinformatics-sop.html)). This pipeline requires that the files are organised in the format outlined below: +The default variant caller used by artic minion is [Nanopolish](https://github.com/jts/nanopolish) and this requires that you provide `*.fastq`, `*.fast5` and `sequencing_summary.txt` files as input to the pipeline. These files can typically be obtained after demultiplexing and basecalling the sequencing data using [Guppy](https://nanoporetech.com/nanopore-sequencing-data-analysis) (see [ARTIC SOP docs](https://artic.network/ncov-2019/ncov2019-bioinformatics-sop.html)). This pipeline requires that the files are organised in the format outlined below and gzip compressed files are also accepted: ```console . From 4fb70b2e50b8eb223b124e5c09e2af273ff4cd71 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 13 Jan 2022 20:13:53 +0000 Subject: [PATCH 035/238] Fix ECLint --- conf/modules_illumina.config | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 78d7905f..427ee87f 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -835,7 +835,7 @@ if (!params.skip_assembly) { } if (!params.skip_bandage) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:BANDAGE_IMAGE' { ext.args = '--height 1000' publishDir = [ @@ -848,7 +848,7 @@ if (!params.skip_assembly) { } if (!params.skip_blast) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:BLAST_BLASTN' { ext.args = "-outfmt '6 stitle std slen qlen qcovs'" publishDir = [ @@ -870,7 +870,7 @@ if (!params.skip_assembly) { } if (!params.skip_assembly_quast) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:QUAST' { publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, @@ -882,7 +882,7 @@ if (!params.skip_assembly) { } if (!params.skip_abacas) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:ABACAS' { ext.args = '-m -p nucmer' publishDir = [ @@ -895,7 +895,7 @@ if (!params.skip_assembly) { } if (!params.skip_plasmidid) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ @@ -903,7 +903,7 @@ if (!params.skip_assembly) { mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - } + } } } } @@ -941,7 +941,7 @@ if (!params.skip_assembly) { } if (!params.skip_bandage) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:BANDAGE_IMAGE' { ext.args = '--height 1000' publishDir = [ @@ -949,12 +949,12 @@ if (!params.skip_assembly) { mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - } + } } } if (!params.skip_blast) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:BLAST_BLASTN' { ext.args = "-outfmt '6 stitle std slen qlen qcovs'" publishDir = [ @@ -976,7 +976,7 @@ if (!params.skip_assembly) { } if (!params.skip_assembly_quast) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:QUAST' { publishDir = [ path: { "${params.outdir}/assembly/unicycler" }, @@ -988,7 +988,7 @@ if (!params.skip_assembly) { } if (!params.skip_abacas) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:ABACAS' { ext.args = '-m -p nucmer' publishDir = [ @@ -1001,7 +1001,7 @@ if (!params.skip_assembly) { } if (!params.skip_plasmidid) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ @@ -1009,7 +1009,7 @@ if (!params.skip_assembly) { mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - } + } } } } @@ -1027,7 +1027,7 @@ if (!params.skip_assembly) { } if (!params.skip_blast) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:BLAST_BLASTN' { ext.args = "-outfmt '6 stitle std slen qlen qcovs'" publishDir = [ @@ -1049,7 +1049,7 @@ if (!params.skip_assembly) { } if (!params.skip_assembly_quast) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:QUAST' { publishDir = [ path: { "${params.outdir}/assembly/minia" }, @@ -1061,7 +1061,7 @@ if (!params.skip_assembly) { } if (!params.skip_abacas) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:ABACAS' { ext.args = '-m -p nucmer' publishDir = [ @@ -1074,7 +1074,7 @@ if (!params.skip_assembly) { } if (!params.skip_plasmidid) { - process { + process { withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ @@ -1082,7 +1082,7 @@ if (!params.skip_assembly) { mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - } + } } } } From 137825eff231db5e3a14ed1398f91c2e4dd2618f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 14 Jan 2022 20:14:46 +0000 Subject: [PATCH 036/238] Install vcflib/vcfuniq from nf-core/modules --- modules.json | 3 ++ .../nf-core/modules/vcflib/vcfuniq/main.nf | 32 +++++++++++++ .../nf-core/modules/vcflib/vcfuniq/meta.yml | 46 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 modules/nf-core/modules/vcflib/vcfuniq/main.nf create mode 100644 modules/nf-core/modules/vcflib/vcfuniq/meta.yml diff --git a/modules.json b/modules.json index 59d7ff84..b1642f07 100644 --- a/modules.json +++ b/modules.json @@ -143,6 +143,9 @@ }, "untar": { "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" + }, + "vcflib/vcfuniq": { + "git_sha": "280712419d6ef5e3fecdc6e9eb98f8746fcbe0b7" } } } diff --git a/modules/nf-core/modules/vcflib/vcfuniq/main.nf b/modules/nf-core/modules/vcflib/vcfuniq/main.nf new file mode 100644 index 00000000..37fc51bc --- /dev/null +++ b/modules/nf-core/modules/vcflib/vcfuniq/main.nf @@ -0,0 +1,32 @@ +def VERSION = '1.0.2' // Version information not provided by tool on CLI + +process VCFLIB_VCFUNIQ { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::vcflib=1.0.2" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/vcflib:1.0.2--h3198e80_5': + 'quay.io/biocontainers/vcflib:1.0.2--h3198e80_5' }" + + input: + tuple val(meta), path(vcf), path(tbi) + + output: + tuple val(meta), path("*.gz"), emit: vcf + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + vcfuniq \\ + $vcf \\ + | bgzip -c $args > ${prefix}.vcf.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + vcflib: $VERSION + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/vcflib/vcfuniq/meta.yml b/modules/nf-core/modules/vcflib/vcfuniq/meta.yml new file mode 100644 index 00000000..3bfc679b --- /dev/null +++ b/modules/nf-core/modules/vcflib/vcfuniq/meta.yml @@ -0,0 +1,46 @@ +name: vcflib_vcfuniq +description: List unique genotypes. Like GNU uniq, but for VCF records. Remove records which have the same position, ref, and alt as the previous record. +keywords: + - vcf + - uniq + - deduplicate +tools: + - vcflib: + description: Command-line tools for manipulating VCF files + homepage: https://github.com/vcflib/vcflib + documentation: https://github.com/vcflib/vcflib#USAGE + doi: "https://doi.org/10.1101/2021.05.21.445151" + licence: ['MIT'] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: Compressed VCF file + pattern: "*.vcf.gz" + - tbi: + type: file + description: Index of VCF file + pattern: "*.vcf.gz.tbi" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - vcf: + type: file + description: Compressed VCF file + pattern: "*.vcf.gz" + +authors: + - "@drpatelh" From 0e15f6b66721aea12b7237ecb3fc1c69f08deffe Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 14 Jan 2022 20:42:08 +0000 Subject: [PATCH 037/238] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e967f3fa..223fa51e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Bump minimum Nextflow version from `21.04.0` -> `21.10.3` * Updated pipeline template to [nf-core/tools 2.2](https://github.com/nf-core/tools/releases/tag/2.2) * [[#218](https://github.com/nf-core/viralrecon/issues/218)] - Support for compressed FastQ files for Nanopore data +* [[#232](https://github.com/nf-core/viralrecon/issues/232)] - Duplicate variants called by ARTIC ONT pipeline ### Parameters From a6ee741f652ae91372abf0e9f7a07d33fa5af5c6 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 14 Jan 2022 20:42:17 +0000 Subject: [PATCH 038/238] Add vcflib citation --- CITATIONS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CITATIONS.md b/CITATIONS.md index 6a3f41ef..ca45fbab 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -91,6 +91,9 @@ * [Unicycler](https://www.ncbi.nlm.nih.gov/pubmed/28594827/) > Wick RR, Judd LM, Gorrie CL, Holt KE. Unicycler: Resolving bacterial genome assemblies from short and long sequencing reads. PLoS Comput Biol. 2017 Jun 8;13(6):e1005595. doi: 10.1371/journal.pcbi.1005595. eCollection 2017 Jun. PubMed PMID: 28594827; PubMed Central PMCID: PMC5481147. +* [Vcflib](https://www.biorxiv.org/content/early/2021/05/23/2021.05.21.445151) + > Garrison E, Kronenberg ZN, Dawson ET, Pedersen BS, P Pjotr. Vcflib and tools for processing the VCF variant call format. bioRxiv 2021 May.doi: 10.1101/2021.05.21.445151. + ## Software packaging/containerisation tools * [Anaconda](https://anaconda.com) From fb42325c7ad2a41a524a89674728ac5206e62e15 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 14 Jan 2022 21:08:35 +0000 Subject: [PATCH 039/238] Implement duplicate variant removal --- conf/modules_nanopore.config | 24 +++++++++++++++++++++--- docs/output.md | 2 ++ workflows/nanopore.nf | 32 +++++++++++++++++++++++++------- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 9e59e252..8add91ef 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -52,6 +52,24 @@ process { ] } + withName: 'VCFLIB_VCFUNIQ' { + ext.prefix = { "${meta.id}.pass.unique" } + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_VIRALRECON:NANOPORE:TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_VIEW' { ext.args = '-b -F 4' ext.prefix = { "${meta.id}.mapped.sorted" } @@ -196,7 +214,7 @@ if (!params.skip_variants_quast) { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}" }, mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + pattern: "quast" ] } } @@ -221,7 +239,7 @@ if (!params.skip_snpeff) { ] } - withName: 'TABIX_BGZIP' { + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:.*:TABIX_BGZIP' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, @@ -230,7 +248,7 @@ if (!params.skip_snpeff) { ] } - withName: 'TABIX_TABIX' { + withName: 'NFCORE_VIRALRECON:NANOPORE:.*:.*:.*:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, diff --git a/docs/output.md b/docs/output.md index 115fbe04..17a90337 100644 --- a/docs/output.md +++ b/docs/output.md @@ -88,6 +88,8 @@ The [artic guppyplex](https://artic.readthedocs.io/en/latest/commands/) tool fro * `/` * `*.consensus.fasta`: Consensus fasta file generated by artic minion. + * `*.pass.unique.vcf.gz`: VCF file containing unique variants passing quality filters. + * `*.pass.unique.vcf.gz.tbi`: VCF index file containing unique variants passing quality filters. * `*.pass.vcf.gz`: VCF file containing variants passing quality filters. * `*.pass.vcf.gz.tbi`: VCF index file containing variants passing quality filters. * `*.primers.vcf`: VCF file containing variants found in primer-binding regions. diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 6f33de6c..6bd9a7bc 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -80,6 +80,8 @@ include { PYCOQC } from '../modules/nf-core/modules/pycoq include { NANOPLOT } from '../modules/nf-core/modules/nanoplot/main' include { ARTIC_GUPPYPLEX } from '../modules/nf-core/modules/artic/guppyplex/main' include { ARTIC_MINION } from '../modules/nf-core/modules/artic/minion/main' +include { VCFLIB_VCFUNIQ } from '../modules/nf-core/modules/vcflib/vcfuniq/main' +include { TABIX_TABIX } from '../modules/nf-core/modules/tabix/tabix/main' include { BCFTOOLS_STATS } from '../modules/nf-core/modules/bcftools/stats/main' include { QUAST } from '../modules/nf-core/modules/quast/main' include { PANGOLIN } from '../modules/nf-core/modules/pangolin/main' @@ -296,21 +298,37 @@ workflow NANOPORE { ch_versions = ch_versions.mix(ARTIC_MINION.out.versions.first().ifEmpty(null)) // - // SUBWORKFLOW: Filter unmapped reads from BAM + // MODULE: Remove duplicate variants // - FILTER_BAM_SAMTOOLS ( - ARTIC_MINION.out.bam + VCFLIB_VCFUNIQ ( + ARTIC_MINION.out.vcf.join(ARTIC_MINION.out.tbi, by: [0]), ) - ch_versions = ch_versions.mix(FILTER_BAM_SAMTOOLS.out.versions) + ch_versions = ch_versions.mix(VCFLIB_VCFUNIQ.out.versions.first().ifEmpty(null)) + + // + // MODULE: Index VCF file + // + TABIX_TABIX ( + VCFLIB_VCFUNIQ.out.vcf + ) + ch_versions = ch_versions.mix(TABIX_TABIX.out.versions.first().ifEmpty(null)) // // MODULE: VCF stats with bcftools stats // BCFTOOLS_STATS ( - ARTIC_MINION.out.vcf + VCFLIB_VCFUNIQ.out.vcf ) ch_versions = ch_versions.mix(BCFTOOLS_STATS.out.versions.first().ifEmpty(null)) + // + // SUBWORKFLOW: Filter unmapped reads from BAM + // + FILTER_BAM_SAMTOOLS ( + ARTIC_MINION.out.bam + ) + ch_versions = ch_versions.mix(FILTER_BAM_SAMTOOLS.out.versions) + // // MODULE: Genome-wide and amplicon-specific coverage QC plots // @@ -409,7 +427,7 @@ workflow NANOPORE { ch_snpeff_multiqc = Channel.empty() if (params.gff && !params.skip_snpeff) { SNPEFF_SNPSIFT ( - ARTIC_MINION.out.vcf, + VCFLIB_VCFUNIQ.out.vcf, PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config, PREPARE_GENOME.out.fasta @@ -425,7 +443,7 @@ workflow NANOPORE { ARTIC_MINION .out .bam_primertrimmed - .join(ARTIC_MINION.out.vcf, by: [0]) + .join(VCFLIB_VCFUNIQ.out.vcf, by: [0]) .join(BCFTOOLS_STATS.out.stats, by: [0]) .map { meta, bam, vcf, stats -> if (WorkflowCommons.getNumVariantsFromBCFToolsStats(stats) > 0) { From 8b272f0a9ec44d4012414b96cd45f5ba108aa385 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 14 Jan 2022 21:13:38 +0000 Subject: [PATCH 040/238] Fix ECLint --- workflows/nanopore.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 6bd9a7bc..237bd81c 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -304,7 +304,7 @@ workflow NANOPORE { ARTIC_MINION.out.vcf.join(ARTIC_MINION.out.tbi, by: [0]), ) ch_versions = ch_versions.mix(VCFLIB_VCFUNIQ.out.versions.first().ifEmpty(null)) - + // // MODULE: Index VCF file // From 279f3a95effdb9d897b24ba299dd78687d1d14a9 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Sat, 15 Jan 2022 09:45:00 +0000 Subject: [PATCH 041/238] Update affiliation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0902fe7..071311d9 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ The nf-core/viralrecon pipeline comes with documentation about the pipeline [usa ## Credits -These scripts were originally written by [Sarai Varona](https://github.com/svarona), [Miguel Juliá](https://github.com/MiguelJulia) and [Sara Monzon](https://github.com/saramonzon) from [BU-ISCIII](https://github.com/BU-ISCIII) and co-ordinated by Isabel Cuesta for the [Institute of Health Carlos III](https://eng.isciii.es/eng.isciii.es/Paginas/Inicio.html), Spain. Through collaboration with the nf-core community the pipeline has now been updated substantially to include additional processing steps, to standardise inputs/outputs and to improve pipeline reporting; implemented primarily by [Harshil Patel](https://github.com/drpatelh) from [The Bioinformatics & Biostatistics Group](https://www.crick.ac.uk/research/science-technology-platforms/bioinformatics-and-biostatistics/) at [The Francis Crick Institute](https://www.crick.ac.uk/), London. +These scripts were originally written by [Sarai Varona](https://github.com/svarona), [Miguel Juliá](https://github.com/MiguelJulia) and [Sara Monzon](https://github.com/saramonzon) from [BU-ISCIII](https://github.com/BU-ISCIII) and co-ordinated by Isabel Cuesta for the [Institute of Health Carlos III](https://eng.isciii.es/eng.isciii.es/Paginas/Inicio.html), Spain. Through collaboration with the nf-core community the pipeline has now been updated substantially to include additional processing steps, to standardise inputs/outputs and to improve pipeline reporting; implemented and maintained primarily by Harshil Patel ([@drpatelh](https://github.com/drpatelh)) from [Seqera Labs, Spain](https://seqera.io/). The key steps in the Nanopore implementation of the pipeline are carried out using the [ARTIC Network's field bioinformatics pipeline](https://github.com/artic-network/fieldbioinformatics) and were inspired by the amazing work carried out by contributors to the [connor-lab/ncov2019-artic-nf pipeline](https://github.com/connor-lab/ncov2019-artic-nf) originally written by [Matt Bull](https://github.com/m-bull) for use by the [COG-UK](https://github.com/COG-UK) project. Thank you for all of your incredible efforts during this pandemic! From 01a7e95f1a0804f6d75be466644d5d0962d9e84d Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 10:47:43 +0000 Subject: [PATCH 042/238] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 223fa51e..e15340aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Bump minimum Nextflow version from `21.04.0` -> `21.10.3` * Updated pipeline template to [nf-core/tools 2.2](https://github.com/nf-core/tools/releases/tag/2.2) * [[#218](https://github.com/nf-core/viralrecon/issues/218)] - Support for compressed FastQ files for Nanopore data -* [[#232](https://github.com/nf-core/viralrecon/issues/232)] - Duplicate variants called by ARTIC ONT pipeline +* [[#232](https://github.com/nf-core/viralrecon/issues/232)] - Remove duplicate variants called by ARTIC ONT pipeline ### Parameters From 2082932c76479c1631cef2dfd563479d8221e66f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 12:10:55 +0000 Subject: [PATCH 043/238] Re-install Nextclade modules and add relevant params --- CHANGELOG.md | 11 +++++ modules.json | 7 +++- .../modules/nextclade/datasetget/main.nf | 39 +++++++++++++++++ .../modules/nextclade/datasetget/meta.yml | 42 +++++++++++++++++++ modules/nf-core/modules/nextclade/main.nf | 40 ------------------ modules/nf-core/modules/nextclade/run/main.nf | 42 +++++++++++++++++++ .../modules/nextclade/{ => run}/meta.yml | 21 +++++----- nextflow.config | 3 ++ nextflow_schema.json | 15 +++++++ 9 files changed, 168 insertions(+), 52 deletions(-) create mode 100644 modules/nf-core/modules/nextclade/datasetget/main.nf create mode 100644 modules/nf-core/modules/nextclade/datasetget/meta.yml delete mode 100644 modules/nf-core/modules/nextclade/main.nf create mode 100644 modules/nf-core/modules/nextclade/run/main.nf rename modules/nf-core/modules/nextclade/{ => run}/meta.yml (75%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e15340aa..2a075d77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Updated pipeline template to [nf-core/tools 2.2](https://github.com/nf-core/tools/releases/tag/2.2) * [[#218](https://github.com/nf-core/viralrecon/issues/218)] - Support for compressed FastQ files for Nanopore data * [[#232](https://github.com/nf-core/viralrecon/issues/232)] - Remove duplicate variants called by ARTIC ONT pipeline +* [[#235](https://github.com/nf-core/viralrecon/issues/235)] - Nextclade version bump ### Parameters +| Old parameter | New parameter | +|-------------------------------|---------------------------------------| +| | `--nextclade_dataset_name` | +| | `--nextclade_dataset_reference` | +| | `--nextclade_dataset_tag` | + +> **NB:** Parameter has been __updated__ if both old and new parameter information is present. +> **NB:** Parameter has been __added__ if just the new parameter information is present. +> **NB:** Parameter has been __removed__ if new parameter information isn't present. + ## [[2.2](https://github.com/nf-core/rnaseq/releases/tag/2.2)] - 2021-07-29 ### Enhancements & fixes diff --git a/modules.json b/modules.json index b1642f07..716cf7a1 100644 --- a/modules.json +++ b/modules.json @@ -87,8 +87,11 @@ "nanoplot": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, - "nextclade": { - "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" + "nextclade/datasetget": { + "git_sha": "796dbb573e52403bb644a65f0639af0a121c90f1" + }, + "nextclade/run": { + "git_sha": "796dbb573e52403bb644a65f0639af0a121c90f1" }, "pangolin": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" diff --git a/modules/nf-core/modules/nextclade/datasetget/main.nf b/modules/nf-core/modules/nextclade/datasetget/main.nf new file mode 100644 index 00000000..55371168 --- /dev/null +++ b/modules/nf-core/modules/nextclade/datasetget/main.nf @@ -0,0 +1,39 @@ +process NEXTCLADE_DATASETGET { + tag "$dataset" + label 'process_low' + + conda (params.enable_conda ? "bioconda::nextclade=1.9.0" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/nextclade:1.9.0--h9ee0642_0' : + 'quay.io/biocontainers/nextclade:1.9.0--h9ee0642_0' }" + + input: + val dataset + val reference + val tag + + output: + path "$prefix" , emit: dataset + path "versions.yml", emit: versions + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${dataset}" + def fasta = reference ? "--reference ${reference}" : '' + def version = tag ? "--tag ${tag}" : '' + """ + nextclade \\ + dataset \\ + get \\ + $args \\ + --name $dataset \\ + $fasta \\ + $version \\ + --output-dir $prefix + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + nextclade: \$(nextclade --version 2>&1) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/nextclade/datasetget/meta.yml b/modules/nf-core/modules/nextclade/datasetget/meta.yml new file mode 100644 index 00000000..1246d918 --- /dev/null +++ b/modules/nf-core/modules/nextclade/datasetget/meta.yml @@ -0,0 +1,42 @@ +name: nextclade_datasetget +description: Get dataset for SARS-CoV-2 genome clade assignment, mutation calling, and sequence quality checks (C++ implementation) +keywords: + - nextclade + - variant + - consensus +tools: + - nextclade: + description: SARS-CoV-2 genome clade assignment, mutation calling, and sequence quality checks + homepage: https://github.com/nextstrain/nextclade + documentation: https://github.com/nextstrain/nextclade + tool_dev_url: https://github.com/nextstrain/nextclade + doi: "" + licence: ['MIT'] + +input: + - dataset: + type: string + description: Name of dataset to retrieve. A list of available datasets can be obtained using the nextclade dataset list command. + pattern: ".+" + - reference: + type: string + description: Accession id to download dataset based on a particular reference sequence. A list of available datasets can be obtained using the nextclade dataset list command. + pattern: ".+" + - tag: + type: string + description: Version tag of the dataset to download. A list of available datasets can be obtained using the nextclade dataset list command. + pattern: ".+" + +output: + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - prefix: + type: path + description: A directory containing the dataset files needed for nextclade run + pattern: "prefix" + +authors: + - "@antunderwood" + - "@drpatelh" diff --git a/modules/nf-core/modules/nextclade/main.nf b/modules/nf-core/modules/nextclade/main.nf deleted file mode 100644 index f60af57b..00000000 --- a/modules/nf-core/modules/nextclade/main.nf +++ /dev/null @@ -1,40 +0,0 @@ -process NEXTCLADE { - tag "$meta.id" - label 'process_low' - - conda (params.enable_conda ? "bioconda::nextclade_js=0.14.4" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/nextclade_js:0.14.4--h9ee0642_0' : - 'quay.io/biocontainers/nextclade_js:0.14.4--h9ee0642_0' }" - - input: - tuple val(meta), path(fasta) - - output: - tuple val(meta), path("${prefix}.csv") , emit: csv - tuple val(meta), path("${prefix}.json") , emit: json - tuple val(meta), path("${prefix}.tree.json") , emit: json_tree - tuple val(meta), path("${prefix}.tsv") , emit: tsv - tuple val(meta), path("${prefix}.clades.tsv"), optional:true, emit: tsv_clades - path "versions.yml" , emit: versions - - script: - def args = task.ext.args ?: '' - prefix = task.ext.prefix ?: "${meta.id}" - """ - nextclade \\ - $args \\ - --jobs $task.cpus \\ - --input-fasta $fasta \\ - --output-json ${prefix}.json \\ - --output-csv ${prefix}.csv \\ - --output-tsv ${prefix}.tsv \\ - --output-tsv-clades-only ${prefix}.clades.tsv \\ - --output-tree ${prefix}.tree.json - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - nextclade: \$(nextclade --version 2>&1) - END_VERSIONS - """ -} diff --git a/modules/nf-core/modules/nextclade/run/main.nf b/modules/nf-core/modules/nextclade/run/main.nf new file mode 100644 index 00000000..e29dd8ce --- /dev/null +++ b/modules/nf-core/modules/nextclade/run/main.nf @@ -0,0 +1,42 @@ +process NEXTCLADE_RUN { + tag "$meta.id" + label 'process_low' + + conda (params.enable_conda ? "bioconda::nextclade=1.9.0" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/nextclade:1.9.0--h9ee0642_0' : + 'quay.io/biocontainers/nextclade:1.9.0--h9ee0642_0' }" + + input: + tuple val(meta), path(fasta) + path dataset + + output: + tuple val(meta), path("${prefix}.csv") , emit: csv + tuple val(meta), path("${prefix}.tsv") , emit: tsv + tuple val(meta), path("${prefix}.json") , emit: json + tuple val(meta), path("${prefix}.tree.json"), emit: json_tree + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + """ + nextclade \\ + run \\ + $args \\ + --jobs $task.cpus \\ + --input-fasta $fasta \\ + --input-dataset $dataset \\ + --output-csv ${prefix}.csv \\ + --output-tsv ${prefix}.tsv \\ + --output-json ${prefix}.json \\ + --output-tree ${prefix}.tree.json \\ + --output-basename ${prefix} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + nextclade: \$(nextclade --version 2>&1) + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/nextclade/meta.yml b/modules/nf-core/modules/nextclade/run/meta.yml similarity index 75% rename from modules/nf-core/modules/nextclade/meta.yml rename to modules/nf-core/modules/nextclade/run/meta.yml index 1b4a435a..40a863e6 100644 --- a/modules/nf-core/modules/nextclade/meta.yml +++ b/modules/nf-core/modules/nextclade/run/meta.yml @@ -1,17 +1,17 @@ -name: nextclade -description: SARS-CoV-2 genome clade assignment, mutation calling, and sequence quality checks (Javascript implementation) +name: nextclade_run +description: SARS-CoV-2 genome clade assignment, mutation calling, and sequence quality checks (C++ implementation) keywords: - nextclade - variant - consensus tools: - nextclade: - description: SARS-CoV-2 genome clade assignment, mutation calling, and sequence quality checks (Javascript implementation) - homepage: https://clades.nextstrain.org - documentation: None + description: SARS-CoV-2 genome clade assignment, mutation calling, and sequence quality checks + homepage: https://github.com/nextstrain/nextclade + documentation: https://github.com/nextstrain/nextclade tool_dev_url: https://github.com/nextstrain/nextclade doi: "" - licence: ["MIT"] + licence: ['MIT'] input: - meta: @@ -19,6 +19,10 @@ input: description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] + - dataset: + type: path + description: Path containing the dataset files obtained by running nextclade dataset get + pattern: "*" - fasta: type: file description: FASTA file containing one or more consensus sequences @@ -50,10 +54,7 @@ output: type: file description: TSV file containing nextclade results pattern: "*.{tsv}" - - tsv_clades: - type: file - description: TSV file containing nextclade results for clades only - pattern: "*.{clades.tsv}" authors: + - "@antunderwood" - "@drpatelh" diff --git a/nextflow.config b/nextflow.config index 1f0470a3..ed33c732 100644 --- a/nextflow.config +++ b/nextflow.config @@ -38,6 +38,9 @@ params { // Nanopore/Illumina options asciigenome_read_depth = 50 asciigenome_window_size = 50 + nextclade_dataset_name = null + nextclade_dataset_reference = null + nextclade_dataset_tag = null multiqc_title = null multiqc_config = null max_multiqc_email_size = '25.MB' diff --git a/nextflow_schema.json b/nextflow_schema.json index 11c894cf..93464053 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -265,6 +265,21 @@ "hidden": true, "help_text": "If file generated by pipeline exceeds the threshold, it will not be attached." }, + "nextclade_dataset_name": { + "type": "string", + "description": "Name of Nextclade dataset to retrieve. A list of available datasets can be obtained using the 'nextclade dataset list' command.", + "fa_icon": "fas fa-project-diagram" + }, + "nextclade_dataset_reference": { + "type": "string", + "description": "Accession id to download dataset based on a particular reference sequence. A list of available datasets can be obtained using the 'nextclade dataset list' command.", + "fa_icon": "fas fa-project-diagram" + }, + "nextclade_dataset_tag": { + "type": "string", + "description": "Version tag of the dataset to download. A list of available datasets can be obtained using the 'nextclade dataset list' command.", + "fa_icon": "fas fa-project-diagram" + }, "skip_mosdepth": { "type": "boolean", "fa_icon": "fas fa-fast-forward", From 4e404b4d2c5018a0a4846cd21a6ff91fb46ac458 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 12:29:02 +0000 Subject: [PATCH 044/238] Add params for Nextclade to main.nf --- main.nf | 5 +++++ nextflow.config | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/main.nf b/main.nf index 9d9eb79d..09abe972 100644 --- a/main.nf +++ b/main.nf @@ -33,6 +33,10 @@ params.gff = WorkflowMain.getGenomeAttribute(params, 'gff' , log params.bowtie2_index = WorkflowMain.getGenomeAttribute(params, 'bowtie2' , log, primer_set, primer_set_version) params.primer_bed = WorkflowMain.getGenomeAttribute(params, 'primer_bed', log, primer_set, primer_set_version) +params.nextclade_dataset_name = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_name', log, primer_set, primer_set_version) +params.nextclade_dataset_reference = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_reference', log, primer_set, primer_set_version) +params.nextclade_dataset_tag = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_tag', log, primer_set, primer_set_version) + /* ======================================================================================== VALIDATE & PRINT PARAMETER SUMMARY @@ -79,6 +83,7 @@ workflow NFCORE_VIRALRECON { // WORKFLOW: Execute a single named workflow for the pipeline // See: https://github.com/nf-core/rnaseq/issues/619 // + workflow { NFCORE_VIRALRECON () } diff --git a/nextflow.config b/nextflow.config index ed33c732..1f0470a3 100644 --- a/nextflow.config +++ b/nextflow.config @@ -38,9 +38,6 @@ params { // Nanopore/Illumina options asciigenome_read_depth = 50 asciigenome_window_size = 50 - nextclade_dataset_name = null - nextclade_dataset_reference = null - nextclade_dataset_tag = null multiqc_title = null multiqc_config = null max_multiqc_email_size = '25.MB' From 78488f89e28d4fa407419ddb1d029b0d5bd0061b Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 12:29:48 +0000 Subject: [PATCH 045/238] Change alignment --- main.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.nf b/main.nf index 09abe972..c0e8157b 100644 --- a/main.nf +++ b/main.nf @@ -33,9 +33,9 @@ params.gff = WorkflowMain.getGenomeAttribute(params, 'gff' , log params.bowtie2_index = WorkflowMain.getGenomeAttribute(params, 'bowtie2' , log, primer_set, primer_set_version) params.primer_bed = WorkflowMain.getGenomeAttribute(params, 'primer_bed', log, primer_set, primer_set_version) -params.nextclade_dataset_name = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_name', log, primer_set, primer_set_version) +params.nextclade_dataset_name = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_name' , log, primer_set, primer_set_version) params.nextclade_dataset_reference = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_reference', log, primer_set, primer_set_version) -params.nextclade_dataset_tag = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_tag', log, primer_set, primer_set_version) +params.nextclade_dataset_tag = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_tag' , log, primer_set, primer_set_version) /* ======================================================================================== From 836ca460e82db012705c9b7ad55547ec4cb7f65a Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 12:55:09 +0000 Subject: [PATCH 046/238] Tweak code to get correct Nextclade keys from top-level in config --- lib/WorkflowMain.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index cc722ed0..c998e756 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -134,6 +134,8 @@ class WorkflowMain { } if (genome_map.containsKey(attribute)) { val = genome_map[ attribute ] + } else if (params.genomes[ params.genome ].containsKey(attribute)) { + val = params.genomes[ params.genome ][ attribute ] } } return val From 61c2439d96ec6099e8fa079f17c3635331fc6ffe Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 16:53:57 +0100 Subject: [PATCH 047/238] Added --ignore-overlaps to ivar mpileup --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 427ee87f..170a64c2 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -378,7 +378,7 @@ if (!params.skip_variants) { process { withName: 'IVAR_VARIANTS' { ext.args = '-t 0.25 -q 20 -m 10' - ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0' + ext.args2 = '--ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 0' publishDir = [ path: { "${params.outdir}/variants/ivar" }, mode: 'copy', From 19d6d990e8d6b95de698d270532054289027b394 Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 16:55:55 +0100 Subject: [PATCH 048/238] Added --ignore-overlaps to bcftools mpileup --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 170a64c2..1d178878 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -550,7 +550,7 @@ if (!params.skip_variants) { if ('bcftools' in callers) { process { withName: 'BCFTOOLS_MPILEUP' { - ext.args = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' + ext.args = '--ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' ext.args3 = "--include 'INFO/DP>=10'" publishDir = [ From 15d9174ddd005756911849f1cace87d6e0d4c834 Mon Sep 17 00:00:00 2001 From: "erika.kvalem" Date: Mon, 17 Jan 2022 17:01:44 +0100 Subject: [PATCH 049/238] Included ivar_variants_to_vcf.py script with strand-bias filter and codon fixed included --- bin/ivar_variants_to_vcf.py | 246 +++++++++++++++++++++++++++++++----- 1 file changed, 213 insertions(+), 33 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 4abd3453..0c72103b 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -4,6 +4,8 @@ import re import errno import argparse +import numpy as np +from scipy.stats import fisher_exact def parse_args(args=None): @@ -28,9 +30,82 @@ def parse_args(args=None): default=0, help="Only output variants where allele frequency greater than this number (default: 0).", ) + parser.add_argument( + "-nsb", + "--not_strand_bias", + dest="NOT_STRAND_BIAS", + default=False, + help="Does not take into account strand bias, use this option when not using amplicons for sequencing", + action="store_true" + ) + parser.add_argument( + "-nmc", + "--not_merge_codons", + dest="NOT_MERGE_CODONS", + help="Only output variants without taking into accout if the positions are consecutive and belong to the same codon.", + action="store_true" + ) return parser.parse_args(args) +def checkConsecutive(mylist): + ''' + Input dict_lines['POS'] position list. + If len(list) = 3 and consecutive positions, returns "triple" + If len(list) = 2 and consecutive positions, returns "double" + If not consecutive returns False + ''' + my_list = list(map(int, mylist)) + if sorted(my_list) == list(range(min(my_list), max(my_list)+1)): + return len(my_list) + else: + if len(my_list) > 1: + my_list.pop() + if sorted(my_list) == list(range(min(my_list), max(my_list)+1)): + return len(my_list) + else: + return False + return False + +def is_same_codon(seq1,seq2): + ''' + Returns position where seq1 != seq2 + ''' + if seq1 =="NA": + return False + + ind_diff = [i for i in range(len(seq1)) if seq1[i] != seq2[i]] + if len(ind_diff) > 1: + print("There has been an issue, more than one difference between the seqs.") + return False + else: + return ind_diff[0] + +def renameVars(dict_lines,dummy): + CHROM = dict_lines["CHROM"][0] + POS = dict_lines["POS"][0] + ID = dict_lines["ID"][0] + if dummy =="double": + REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) + ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) + elif dummy =="triple": + REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) + str(dict_lines["REF"][2]) + ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) + str(dict_lines["ALT"][2]) + ## TODO Check how much differences we found among DPs in the three positions of a codon. + REF_DP = dict_lines["REF_DP"][0] + REF_RV = dict_lines["REF_RV"][0] + ALT_DP = dict_lines["ALT_DP"][0] + ALT_RV = dict_lines["ALT_RV"][0] + QUAL = dict_lines["QUAL"][0] + REF_CODON = REF + ALT_CODON = ALT + FILTER =dict_lines["FILTER"][0] + # INFO DP depends on the decision in the todo above. SB is left with the first one (en principio) + INFO = dict_lines["INFO"][0] + FORMAT = dict_lines["FORMAT"][0] + # sample depends on the decision in the todo above. + SAMPLE = dict_lines["SAMPLE"][0] + return CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE def make_dir(path): if not len(path) == 0: @@ -41,7 +116,7 @@ def make_dir(path): raise -def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0): +def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias=False,NotMergeCodon=False): filename = os.path.splitext(FileIn)[0] header = ( "##fileformat=VCFv4.2\n" @@ -59,24 +134,40 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0): '##FORMAT=\n' ) header += ( - "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t" + filename + "\n" + "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tINFO[SB]\tFORMAT\t" + filename + "\n" ) - + varList = [] varCountDict = {"SNP": 0, "INS": 0, "DEL": 0} + dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[],'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER': [],'INFO':[],'FORMAT':[],'SAMPLE':[]} + writeLine=False + write2Line = False OutDir = os.path.dirname(FileOut) make_dir(OutDir) fout = open(FileOut, "w") fout.write(header) + with open(FileIn) as f: + for line in f: + if not re.match("REGION", line): + line = re.split("\t", line) CHROM = line[0] POS = line[1] ID = "." REF = line[2] ALT = line[3] + REF_DP = int(line[4]) + REF_RV = int(line[5]) + REF_FW = REF_DP - REF_RV + ALT_RV = int(line[8]) + ALT_DP = int(line[7]) + ALT_FW = ALT_DP - ALT_RV + table = np.array([[REF_FW, REF_RV], [ALT_FW, ALT_RV]]) + oddsr, p = fisher_exact(table, alternative='greater') + var_type = "SNP" if ALT[0] == "+": ALT = REF + ALT[1:] @@ -85,13 +176,36 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0): REF += ALT[1:] ALT = line[2] var_type = "DEL" + QUAL = "." pass_test = line[13] - if pass_test == "TRUE": - FILTER = "PASS" + REF_CODON = line[15] + ALT_CODON = line[17] + + + + + + if NotStrandBias: + + if pass_test =="TRUE": + FILTER = "PASS" + else: + FILTER = "FAIL" + INFO = "DP=" + line[11] else: - FILTER = "FAIL" - INFO = "DP=" + line[11] + + if p<0.05 and pass_test =="TRUE": + FILTER = "SB" + elif p>0.05 and pass_test =="TRUE": + FILTER = "PASS" + elif p<=0.05 and pass_test == "FALSE": + FILTER = "SB,other" + else: + FILTER = "FAIL" + INFO = "DP=" + line[11]+":SB_pvalue="+str(round(p,5)) + + FORMAT = "GT:REF_DP:REF_RV:REF_QUAL:ALT_DP:ALT_RV:ALT_QUAL:ALT_FREQ" SAMPLE = ( "1:" @@ -109,29 +223,74 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0): + ":" + line[10] ) - oline = ( - CHROM - + "\t" - + POS - + "\t" - + ID - + "\t" - + REF - + "\t" - + ALT - + "\t" - + QUAL - + "\t" - + FILTER - + "\t" - + INFO - + "\t" - + FORMAT - + "\t" - + SAMPLE - + "\n" - ) - writeLine = True + param_list = [CHROM,POS,ID,REF,ALT,REF_DP,REF_RV,ALT_DP,ALT_RV,QUAL,REF_CODON,ALT_CODON,FILTER,INFO,FORMAT,SAMPLE] + + if NotMergeCodon: + writeLine = True + oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + + else: + #For the first two lines of the files, write without checking if the 3 positions are consecutive + if len(dict_lines["POS"]) ==0 or len(dict_lines["POS"]) ==1 : + for i,j in enumerate(dict_lines): + dict_lines.setdefault(j, []).append(param_list[i]) + writeLine=False + + + elif len(dict_lines["POS"]) == 2: + + for i,j in enumerate(dict_lines): + dict_lines.setdefault(j, []).append(param_list[i]) + + #¿Consecutive? + if checkConsecutive(dict_lines["POS"]) == 2: + if is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) != 2: + writeLine = True + dummy = "double" + CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) + oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+"\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + for i,j in enumerate(dict_lines): + dict_lines[list(dict_lines.keys())[i]].pop(0) + dict_lines[list(dict_lines.keys())[i]].pop(0) + + elif checkConsecutive(dict_lines["POS"]) == 3: + + if is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 0: + writeLine = True + dummy = "triple" + CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) + oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + for i,j in enumerate(dict_lines): + dict_lines[list(dict_lines.keys())[i]].pop(0) + dict_lines[list(dict_lines.keys())[i]].pop(0) + dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[], 'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER':[],'INFO':[],'FORMAT':[],'SAMPLE':[]} + + elif is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 1: + writeLine = True + dummy = "double" + CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) + oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + for i,j in enumerate(dict_lines): + dict_lines[list(dict_lines.keys())[i]].pop(0) + dict_lines[list(dict_lines.keys())[i]].pop(0) + elif is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 2: + writeLine = True + oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t" + dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") + + for i,j in enumerate(dict_lines): + dict_lines[list(dict_lines.keys())[i]].pop(0) + elif checkConsecutive(dict_lines["POS"]) == False: + writeLine = True + + oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") + + for i,j in enumerate(dict_lines): + dict_lines[list(dict_lines.keys())[i]].pop(0) + else: + print("Something went terribly wrong!!" + str(len(dict_lines["POS"]))) + + + if passOnly and FILTER != "PASS": writeLine = False if float(line[10]) < minAF: @@ -143,7 +302,10 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0): if writeLine: varCountDict[var_type] += 1 fout.write(oline) - fout.close() + + + + ## Print variant counts to pass to MultiQC varCountList = [(k, str(v)) for k, v in sorted(varCountDict.items())] @@ -151,12 +313,30 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0): print("\t".join([filename] + [x[1] for x in varCountList])) + if len(dict_lines["POS"]) == 2: + if checkConsecutive(dict_lines["POS"]) == 2: + if is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) != 2: + writeLine = True + dummy = "double" + CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) + oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+"\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + fout.write(oline) + else: + oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") + oline1 =(dict_lines["CHROM"][1] + "\t"+ dict_lines["POS"][1]+ "\t"+ dict_lines["ID"][1]+ "\t"+ dict_lines ["REF"][1]+ "\t"+ dict_lines["ALT"][1]+ "\t"+ dict_lines["QUAL"][1]+ "\t"+ dict_lines["FILTER"][1]+ "\t"+ dict_lines["INFO"][1]+ "\t"+ dict_lines["FORMAT"][1]+ "\t"+ dict_lines["SAMPLE"][1]+ "\n") + fout.write(oline) + fout.write(oline1) + elif len(dict_lines["POS"]) == 1: + oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") + fout.write(oline) + + def main(args=None): args = parse_args(args) ivar_variants_to_vcf( - args.FILE_IN, args.FILE_OUT, args.PASS_ONLY, args.ALLELE_FREQ_THRESH + args.FILE_IN, args.FILE_OUT, args.PASS_ONLY, args.ALLELE_FREQ_THRESH, args.NOT_STRAND_BIAS, args.NOT_MERGE_CODONS ) if __name__ == "__main__": - sys.exit(main()) + sys.exit(main()) \ No newline at end of file From 831c1a8d44bb2ed17cf46fa0ac514821394e7989 Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 17:17:08 +0100 Subject: [PATCH 050/238] Remove rename of fasta reference --- modules/local/make_bed_mask.nf | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index 58829660..828134f3 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -23,10 +23,6 @@ process MAKE_BED_MASK { $bed \\ ${prefix}.bed - ## Rename fasta entry by sample name and not reference genome - FASTA_NAME=\$(head -n1 $fasta | sed 's/>//g') - sed "s/\${FASTA_NAME}/${meta.id}/g" $fasta > ${prefix}.fasta - cat <<-END_VERSIONS > versions.yml "${task.process}": python: \$(python --version | sed 's/Python //g') From 395aabb5fd910142cb0b02b03821f916f5f06451 Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 17:25:19 +0100 Subject: [PATCH 051/238] Removed fasta channel, dont need it --- modules/local/make_bed_mask.nf | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index 828134f3..6acb1e8c 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -8,11 +8,9 @@ process MAKE_BED_MASK { input: tuple val(meta), path(vcf), path(bed) - path fasta output: tuple val(meta), path("*.bed") , emit: bed - tuple val(meta), path("*.fasta"), emit: fasta path "versions.yml" , emit: versions script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ From 76478a57288483a15817aa0a83b19a10fb37412a Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 17:27:52 +0100 Subject: [PATCH 052/238] Removed fasta input, dont need it --- subworkflows/local/make_consensus.nf | 1 - 1 file changed, 1 deletion(-) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index d4a195bb..526d39df 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -32,7 +32,6 @@ workflow MAKE_CONSENSUS { MAKE_BED_MASK ( bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf ] }.join( BEDTOOLS_MERGE.out.bed, by: [0] ), - fasta ) ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) From 7197e1a9c5f9c7d2b23627ed39b54afb864d189c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 17 Jan 2022 17:28:06 +0100 Subject: [PATCH 053/238] removed trailing spaces --- bin/ivar_variants_to_vcf.py | 51 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 0c72103b..1b99318c 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -1,4 +1,5 @@ #!/usr/bin/env python + import os import sys import re @@ -63,13 +64,13 @@ def checkConsecutive(mylist): my_list.pop() if sorted(my_list) == list(range(min(my_list), max(my_list)+1)): return len(my_list) - else: + else: return False return False def is_same_codon(seq1,seq2): ''' - Returns position where seq1 != seq2 + Returns position where seq1 != seq2 ''' if seq1 =="NA": return False @@ -78,16 +79,16 @@ def is_same_codon(seq1,seq2): if len(ind_diff) > 1: print("There has been an issue, more than one difference between the seqs.") return False - else: + else: return ind_diff[0] - + def renameVars(dict_lines,dummy): CHROM = dict_lines["CHROM"][0] POS = dict_lines["POS"][0] ID = dict_lines["ID"][0] if dummy =="double": - REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) - ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) + REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) + ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) elif dummy =="triple": REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) + str(dict_lines["REF"][2]) ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) + str(dict_lines["ALT"][2]) @@ -99,9 +100,9 @@ def renameVars(dict_lines,dummy): QUAL = dict_lines["QUAL"][0] REF_CODON = REF ALT_CODON = ALT - FILTER =dict_lines["FILTER"][0] + FILTER =dict_lines["FILTER"][0] # INFO DP depends on the decision in the todo above. SB is left with the first one (en principio) - INFO = dict_lines["INFO"][0] + INFO = dict_lines["INFO"][0] FORMAT = dict_lines["FORMAT"][0] # sample depends on the decision in the todo above. SAMPLE = dict_lines["SAMPLE"][0] @@ -136,7 +137,7 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= header += ( "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tINFO[SB]\tFORMAT\t" + filename + "\n" ) - + varList = [] varCountDict = {"SNP": 0, "INS": 0, "DEL": 0} dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[],'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER': [],'INFO':[],'FORMAT':[],'SAMPLE':[]} @@ -148,11 +149,11 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= fout.write(header) with open(FileIn) as f: - + for line in f: if not re.match("REGION", line): - + line = re.split("\t", line) CHROM = line[0] POS = line[1] @@ -181,10 +182,10 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= pass_test = line[13] REF_CODON = line[15] ALT_CODON = line[17] - - - - + + + + if NotStrandBias: @@ -205,7 +206,7 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= FILTER = "FAIL" INFO = "DP=" + line[11]+":SB_pvalue="+str(round(p,5)) - + FORMAT = "GT:REF_DP:REF_RV:REF_QUAL:ALT_DP:ALT_RV:ALT_QUAL:ALT_FREQ" SAMPLE = ( "1:" @@ -224,7 +225,7 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= + line[10] ) param_list = [CHROM,POS,ID,REF,ALT,REF_DP,REF_RV,ALT_DP,ALT_RV,QUAL,REF_CODON,ALT_CODON,FILTER,INFO,FORMAT,SAMPLE] - + if NotMergeCodon: writeLine = True oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) @@ -264,12 +265,12 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[], 'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER':[],'INFO':[],'FORMAT':[],'SAMPLE':[]} - + elif is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 1: writeLine = True dummy = "double" CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) - oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines[list(dict_lines.keys())[i]].pop(0) @@ -287,10 +288,10 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) else: - print("Something went terribly wrong!!" + str(len(dict_lines["POS"]))) + print("Something went terribly wrong!!" + str(len(dict_lines["POS"]))) + + - - if passOnly and FILTER != "PASS": writeLine = False if float(line[10]) < minAF: @@ -303,9 +304,9 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= varCountDict[var_type] += 1 fout.write(oline) - - + + ## Print variant counts to pass to MultiQC varCountList = [(k, str(v)) for k, v in sorted(varCountDict.items())] @@ -339,4 +340,4 @@ def main(args=None): if __name__ == "__main__": - sys.exit(main()) \ No newline at end of file + sys.exit(main()) From a6e863176fbb7004aae49e961072cefd4f130a33 Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 17:30:04 +0100 Subject: [PATCH 054/238] Input reference fasta to bedtools maskfasta --- subworkflows/local/make_consensus.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index 526d39df..3f1ce5fa 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -37,7 +37,7 @@ workflow MAKE_CONSENSUS { BEDTOOLS_MASKFASTA ( MAKE_BED_MASK.out.bed, - MAKE_BED_MASK.out.fasta.map{it[1]} + fasta ) ch_versions = ch_versions.mix(BEDTOOLS_MASKFASTA.out.versions.first()) From 3057e7cc2b4229e174bd6d8aec4df492de04bfef Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 18:16:12 +0100 Subject: [PATCH 055/238] Removed bedtools genomecoverage, no needed anymore --- subworkflows/local/make_consensus.nf | 7 ------- 1 file changed, 7 deletions(-) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index 3f1ce5fa..36aa19d8 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -18,13 +18,6 @@ workflow MAKE_CONSENSUS { ch_versions = Channel.empty() - BEDTOOLS_GENOMECOV ( - bam_vcf.map { meta, bam, vcf, tbi -> [ meta, bam, 1 ] }, - [], - 'bed', - ) - ch_versions = ch_versions.mix(BEDTOOLS_GENOMECOV.out.versions.first()) - BEDTOOLS_MERGE ( BEDTOOLS_GENOMECOV.out.genomecov ) From ffe29f416ae430f22c3b03538047dddb2af975b7 Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 18:16:24 +0100 Subject: [PATCH 056/238] Removed bedtools genomecoverage, no needed anymore --- subworkflows/local/make_consensus.nf | 1 - 1 file changed, 1 deletion(-) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index 36aa19d8..b6bb0459 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -2,7 +2,6 @@ // Run various tools to generate a masked genome consensus sequence // -include { BEDTOOLS_GENOMECOV } from '../../modules/nf-core/modules/bedtools/genomecov/main' include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' From 49220d30a83fef67cb62078ee1c9a9e105b139ca Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 18:24:40 +0100 Subject: [PATCH 057/238] Change bcftools merge and make_ned_mask order to fix new masking --- subworkflows/local/make_consensus.nf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index b6bb0459..51292239 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -17,15 +17,15 @@ workflow MAKE_CONSENSUS { ch_versions = Channel.empty() - BEDTOOLS_MERGE ( - BEDTOOLS_GENOMECOV.out.genomecov + MAKE_BED_MASK ( + bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf ] }.join( BEDTOOLS_MERGE.out.bed, by: [0] $ ) - ch_versions = ch_versions.mix(BEDTOOLS_MERGE.out.versions.first()) + ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) - MAKE_BED_MASK ( - bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf ] }.join( BEDTOOLS_MERGE.out.bed, by: [0] ), + BEDTOOLS_MERGE ( + MAKE_BED_MASK.out.genomecov ) - ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) + ch_versions = ch_versions.mix(BEDTOOLS_MERGE.out.versions.first()) BEDTOOLS_MASKFASTA ( MAKE_BED_MASK.out.bed, From 2738529565e3249ccd7324de2f6b3368368ea2ba Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 18:26:28 +0100 Subject: [PATCH 058/238] Fix left padding spaces --- subworkflows/local/make_consensus.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index 51292239..2888cc50 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -17,7 +17,7 @@ workflow MAKE_CONSENSUS { ch_versions = Channel.empty() - MAKE_BED_MASK ( + MAKE_BED_MASK ( bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf ] }.join( BEDTOOLS_MERGE.out.bed, by: [0] $ ) ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) From 027d78aaebf04b2e25140d009f49f38231f12eac Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 18:27:22 +0100 Subject: [PATCH 059/238] Remove joining, we need bam and vcf for this process now --- subworkflows/local/make_consensus.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index 2888cc50..8ca06e85 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -18,7 +18,7 @@ workflow MAKE_CONSENSUS { ch_versions = Channel.empty() MAKE_BED_MASK ( - bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf ] }.join( BEDTOOLS_MERGE.out.bed, by: [0] $ + bam_vcf ) ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) From d73fd70bd2e5ebd224f3d43e252f4f6f2cc3b602 Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 18:31:06 +0100 Subject: [PATCH 060/238] Fixed input files --- modules/local/make_bed_mask.nf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index 6acb1e8c..cd608660 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -7,7 +7,8 @@ process MAKE_BED_MASK { 'quay.io/biocontainers/python:3.9--1' }" input: - tuple val(meta), path(vcf), path(bed) + tuple val(meta), path(bam), path(vcf) + path fasta output: tuple val(meta), path("*.bed") , emit: bed @@ -16,6 +17,7 @@ process MAKE_BED_MASK { script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ def prefix = task.ext.prefix ?: "${meta.id}" """ + make_bed_mask.py \\ $vcf \\ $bed \\ From 14af298a7fa06efe0b89624f5119bfdbd39df359 Mon Sep 17 00:00:00 2001 From: svarona Date: Mon, 17 Jan 2022 18:31:58 +0100 Subject: [PATCH 061/238] Added fasta as input file --- subworkflows/local/make_consensus.nf | 1 + 1 file changed, 1 insertion(+) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index 8ca06e85..3d0f39c9 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -19,6 +19,7 @@ workflow MAKE_CONSENSUS { MAKE_BED_MASK ( bam_vcf + fasta ) ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) From 2db0c9e17cad7ff1051e0bf661f7b6321b4cd343 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 17:50:01 +0000 Subject: [PATCH 062/238] Add --nextclade_dataset param --- lib/WorkflowMain.groovy | 8 ++++++++ main.nf | 1 + nextflow_schema.json | 35 ++++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index c998e756..40fccb68 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -80,6 +80,14 @@ class WorkflowMain { log.error "Invalid platform option: '${params.platform}'. Valid options: ${platformList.join(', ')}." System.exit(1) } + + // Check Nextclade dataset parameters + if (!params.skip_nextclade) { + if (!params.nextclade_dataset && !params.nextclade_dataset_name) { + log.error "Nextclade dataset not specified with '--nextclade_dataset' or '--nextclade_dataset_name'. A list of available datasets can be obtained using the Nextclade 'nextclade dataset list' command." + System.exit(1) + } + } } // diff --git a/main.nf b/main.nf index c0e8157b..b0337a10 100644 --- a/main.nf +++ b/main.nf @@ -33,6 +33,7 @@ params.gff = WorkflowMain.getGenomeAttribute(params, 'gff' , log params.bowtie2_index = WorkflowMain.getGenomeAttribute(params, 'bowtie2' , log, primer_set, primer_set_version) params.primer_bed = WorkflowMain.getGenomeAttribute(params, 'primer_bed', log, primer_set, primer_set_version) +params.nextclade_dataset = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset' , log, primer_set, primer_set_version) params.nextclade_dataset_name = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_name' , log, primer_set, primer_set_version) params.nextclade_dataset_reference = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_reference', log, primer_set, primer_set_version) params.nextclade_dataset_tag = WorkflowMain.getGenomeAttribute(params, 'nextclade_dataset_tag' , log, primer_set, primer_set_version) diff --git a/nextflow_schema.json b/nextflow_schema.json index 93464053..b6ce9a60 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -231,6 +231,26 @@ "description": "Options common to both the Nanopore and Illumina workflows in the pipeline.", "default": "", "properties": { + "nextclade_dataset": { + "type": "string", + "description": "Full path to Nextclade dataset.", + "fa_icon": "fas fa-project-diagram" + }, + "nextclade_dataset_name": { + "type": "string", + "description": "Name of Nextclade dataset to retrieve. A list of available datasets can be obtained using the 'nextclade dataset list' command.", + "fa_icon": "fas fa-project-diagram" + }, + "nextclade_dataset_reference": { + "type": "string", + "description": "Accession id to download dataset based on a particular reference sequence. A list of available datasets can be obtained using the 'nextclade dataset list' command.", + "fa_icon": "fas fa-project-diagram" + }, + "nextclade_dataset_tag": { + "type": "string", + "description": "Version tag of the dataset to download. A list of available datasets can be obtained using the 'nextclade dataset list' command.", + "fa_icon": "fas fa-project-diagram" + }, "asciigenome_read_depth": { "type": "integer", "default": 50, @@ -265,21 +285,6 @@ "hidden": true, "help_text": "If file generated by pipeline exceeds the threshold, it will not be attached." }, - "nextclade_dataset_name": { - "type": "string", - "description": "Name of Nextclade dataset to retrieve. A list of available datasets can be obtained using the 'nextclade dataset list' command.", - "fa_icon": "fas fa-project-diagram" - }, - "nextclade_dataset_reference": { - "type": "string", - "description": "Accession id to download dataset based on a particular reference sequence. A list of available datasets can be obtained using the 'nextclade dataset list' command.", - "fa_icon": "fas fa-project-diagram" - }, - "nextclade_dataset_tag": { - "type": "string", - "description": "Version tag of the dataset to download. A list of available datasets can be obtained using the 'nextclade dataset list' command.", - "fa_icon": "fas fa-project-diagram" - }, "skip_mosdepth": { "type": "boolean", "fa_icon": "fas fa-fast-forward", From 9ebdbddc24047a06b43e6e01531086065eeaf28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 17 Jan 2022 18:51:45 +0100 Subject: [PATCH 063/238] fixed spaces, homogeneize var names --- bin/ivar_variants_to_vcf.py | 152 ++++++++++++++++++++++-------------- 1 file changed, 94 insertions(+), 58 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 1b99318c..0340a370 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -49,17 +49,22 @@ def parse_args(args=None): return parser.parse_args(args) -def checkConsecutive(mylist): +def check_consecutive(mylist): ''' - Input dict_lines['POS'] position list. - If len(list) = 3 and consecutive positions, returns "triple" - If len(list) = 2 and consecutive positions, returns "double" - If not consecutive returns False + Description: + This function checks if a list of three or two numbers are consecutive and returns how many items are consecutive. + input: + my_list - An integer list + return: + Number of items consecutive in the list - [False, 1, 2] ''' my_list = list(map(int, mylist)) + + ## Check if the list contains consecutive numbers if sorted(my_list) == list(range(min(my_list), max(my_list)+1)): return len(my_list) else: + ## If not, and the list is > 1, remove the last item and reevaluate. if len(my_list) > 1: my_list.pop() if sorted(my_list) == list(range(min(my_list), max(my_list)+1)): @@ -68,9 +73,15 @@ def checkConsecutive(mylist): return False return False -def is_same_codon(seq1,seq2): +def codon_position(seq1,seq2): ''' - Returns position where seq1 != seq2 + Description: + Function compares two codon nucleotide sequences (size 3) and retuns the position where it differs. + Input: + seq1 - list size 3 [A,T,C,G] + seq2 - list size 3 [A,T,C,G] + Returns: + Returns position where seq1 != seq2 ''' if seq1 =="NA": return False @@ -82,14 +93,25 @@ def is_same_codon(seq1,seq2): else: return ind_diff[0] -def renameVars(dict_lines,dummy): +def rename_vars(dict_lines,num_collapse): + ''' + Description: + The function set the vars acordingly to the lines to collapse do to consecutive variants. + Input: + dict_lines - Dict with var lines. + num_collapse - number of lines to collapse [1,2] + Returns:: + Vars fixed. + ''' CHROM = dict_lines["CHROM"][0] POS = dict_lines["POS"][0] ID = dict_lines["ID"][0] - if dummy =="double": + # If two consecutive collapse 2 lines into one. + if num_collapse == 2: REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) - elif dummy =="triple": + # If three consecutive collapse 3 lines into one. + elif num_collapse == 3: REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) + str(dict_lines["REF"][2]) ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) + str(dict_lines["ALT"][2]) ## TODO Check how much differences we found among DPs in the three positions of a codon. @@ -101,7 +123,7 @@ def renameVars(dict_lines,dummy): REF_CODON = REF ALT_CODON = ALT FILTER =dict_lines["FILTER"][0] - # INFO DP depends on the decision in the todo above. SB is left with the first one (en principio) + # INFO DP depends on the decision in the todo above. SB is left with the first one. INFO = dict_lines["INFO"][0] FORMAT = dict_lines["FORMAT"][0] # sample depends on the decision in the todo above. @@ -109,6 +131,14 @@ def renameVars(dict_lines,dummy): return CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE def make_dir(path): + ''' + Description: + Create directory. + Input: + path - path where the directory will be created. + Returns: + None + ''' if not len(path) == 0: try: os.makedirs(path) @@ -116,8 +146,21 @@ def make_dir(path): if exception.errno != errno.EEXIST: raise - def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias=False,NotMergeCodon=False): + ''' + Description: + Main function to to the parsing from tsv to vcf. + Input: + FileIn - tsv file + FileOut - vcf filename + passOnly - whether to keep only pass filter variants [True, False] + minAF - min Alternate frequency to keep a variant. + NotStrandBias - whether to perform strand-bias filter [True, False] + NotMergeCodon - whether to perform codon merging in consecutive positions [True, False] + + Returns: + None + ''' filename = os.path.splitext(FileIn)[0] header = ( "##fileformat=VCFv4.2\n" @@ -142,18 +185,14 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= varCountDict = {"SNP": 0, "INS": 0, "DEL": 0} dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[],'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER': [],'INFO':[],'FORMAT':[],'SAMPLE':[]} writeLine=False - write2Line = False OutDir = os.path.dirname(FileOut) make_dir(OutDir) fout = open(FileOut, "w") fout.write(header) with open(FileIn) as f: - for line in f: - if not re.match("REGION", line): - line = re.split("\t", line) CHROM = line[0] POS = line[1] @@ -165,6 +204,8 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= REF_FW = REF_DP - REF_RV ALT_RV = int(line[8]) ALT_DP = int(line[7]) + + ## Perform a fisher_exact test for strand bias detection ALT_FW = ALT_DP - ALT_RV table = np.array([[REF_FW, REF_RV], [ALT_FW, ALT_RV]]) oddsr, p = fisher_exact(table, alternative='greater') @@ -183,19 +224,14 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= REF_CODON = line[15] ALT_CODON = line[17] - - - - if NotStrandBias: - if pass_test =="TRUE": FILTER = "PASS" else: FILTER = "FAIL" INFO = "DP=" + line[11] else: - + # If strand-bias test significative add SB in the FILTER field, else PASS if p<0.05 and pass_test =="TRUE": FILTER = "SB" elif p>0.05 and pass_test =="TRUE": @@ -206,7 +242,6 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= FILTER = "FAIL" INFO = "DP=" + line[11]+":SB_pvalue="+str(round(p,5)) - FORMAT = "GT:REF_DP:REF_RV:REF_QUAL:ALT_DP:ALT_RV:ALT_QUAL:ALT_FREQ" SAMPLE = ( "1:" @@ -231,56 +266,65 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) else: - #For the first two lines of the files, write without checking if the 3 positions are consecutive + ## dict_lines contains all the informative fields for 3 positions in the vcf. + # dict_lines has a maximum size of three. + + ## Always fill dict_lines until size 2. if len(dict_lines["POS"]) ==0 or len(dict_lines["POS"]) ==1 : for i,j in enumerate(dict_lines): dict_lines.setdefault(j, []).append(param_list[i]) writeLine=False - + # If queue has size 2, we include the third line elif len(dict_lines["POS"]) == 2: - for i,j in enumerate(dict_lines): dict_lines.setdefault(j, []).append(param_list[i]) - #¿Consecutive? - if checkConsecutive(dict_lines["POS"]) == 2: - if is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) != 2: + # Are two positions in the dict consecutive? + if check_consecutive(dict_lines["POS"]) == 2: + ## If the first position is not on the third position of the codon they are in the same codon. + if codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) != 2: writeLine = True - dummy = "double" - CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) + num_collapse = "2" + CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+"\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + ## We removed the first two items in dict_lines with have been just processed. for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines[list(dict_lines.keys())[i]].pop(0) - elif checkConsecutive(dict_lines["POS"]) == 3: - - if is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 0: + # Are the three positions in the dict consecutive? + elif check_consecutive(dict_lines["POS"]) == 3: + ## we check the first position in which codon position is to process it acordingly. + # If first position is in the first codon position all three positions belong to the same codon. + if codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 0: writeLine = True - dummy = "triple" - CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) + num_collapse = 3 + CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) - for i,j in enumerate(dict_lines): - dict_lines[list(dict_lines.keys())[i]].pop(0) - dict_lines[list(dict_lines.keys())[i]].pop(0) - dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[], 'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER':[],'INFO':[],'FORMAT':[],'SAMPLE':[]} + #for i,j in enumerate(dict_lines): + # dict_lines[list(dict_lines.keys())[i]].pop(0) + # dict_lines[list(dict_lines.keys())[i]].pop(0) - elif is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 1: + # we empty the dict_lines + dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[], 'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER':[],'INFO':[],'FORMAT':[],'SAMPLE':[]} + # If first position is in the second codon position, we have the two first positions belonging to the same codon and the last one independent. + elif codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 1: writeLine = True - dummy = "double" - CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) + num_collapse = 2 + CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines[list(dict_lines.keys())[i]].pop(0) - elif is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 2: + ## Finally if we have the first position in the last codon position, we write first position and left the remaining two to be evaluated in the next iteration. + elif codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 2: writeLine = True oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t" + dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) - elif checkConsecutive(dict_lines["POS"]) == False: + elif check_consecutive(dict_lines["POS"]) == False: writeLine = True oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") @@ -290,8 +334,6 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= else: print("Something went terribly wrong!!" + str(len(dict_lines["POS"]))) - - if passOnly and FILTER != "PASS": writeLine = False if float(line[10]) < minAF: @@ -304,22 +346,18 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= varCountDict[var_type] += 1 fout.write(oline) - - - - ## Print variant counts to pass to MultiQC varCountList = [(k, str(v)) for k, v in sorted(varCountDict.items())] print("\t".join(["sample"] + [x[0] for x in varCountList])) print("\t".join([filename] + [x[1] for x in varCountList])) - + ## Handle last 3 lines. if len(dict_lines["POS"]) == 2: - if checkConsecutive(dict_lines["POS"]) == 2: - if is_same_codon(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) != 2: + if check_consecutive(dict_lines["POS"]) == 2: + if codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) != 2: writeLine = True - dummy = "double" - CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = renameVars(dict_lines,dummy) + num_collapse = 2 + CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+"\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) fout.write(oline) else: @@ -331,13 +369,11 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") fout.write(oline) - def main(args=None): args = parse_args(args) ivar_variants_to_vcf( args.FILE_IN, args.FILE_OUT, args.PASS_ONLY, args.ALLELE_FREQ_THRESH, args.NOT_STRAND_BIAS, args.NOT_MERGE_CODONS ) - if __name__ == "__main__": sys.exit(main()) From c5cfa18c1452aa4b2b37422af807ce688c545bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 17 Jan 2022 18:54:41 +0100 Subject: [PATCH 064/238] fixed spaces --- bin/ivar_variants_to_vcf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 0340a370..0c656009 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -101,7 +101,7 @@ def rename_vars(dict_lines,num_collapse): dict_lines - Dict with var lines. num_collapse - number of lines to collapse [1,2] Returns:: - Vars fixed. + Vars fixed. ''' CHROM = dict_lines["CHROM"][0] POS = dict_lines["POS"][0] From 1b7a6531fdb51a006caadce9f5c52a1e9268507f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 18:10:14 +0000 Subject: [PATCH 065/238] Add logic for nextclade/datasetget module --- subworkflows/local/prepare_genome_illumina.nf | 29 +++++++++++++++++++ subworkflows/local/prepare_genome_nanopore.nf | 29 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/subworkflows/local/prepare_genome_illumina.nf b/subworkflows/local/prepare_genome_illumina.nf index 09363f4c..860d49a4 100644 --- a/subworkflows/local/prepare_genome_illumina.nf +++ b/subworkflows/local/prepare_genome_illumina.nf @@ -7,12 +7,14 @@ include { GUNZIP as GUNZIP_GFF } from '../../modules/nf-core/modules/gu include { GUNZIP as GUNZIP_PRIMER_BED } from '../../modules/nf-core/modules/gunzip/main' include { GUNZIP as GUNZIP_PRIMER_FASTA } from '../../modules/nf-core/modules/gunzip/main' include { UNTAR as UNTAR_BOWTIE2_INDEX } from '../../modules/nf-core/modules/untar/main' +include { UNTAR as UNTAR_NEXTCLADE_DB } from '../../modules/nf-core/modules/untar/main' include { UNTAR as UNTAR_KRAKEN2_DB } from '../../modules/nf-core/modules/untar/main' include { UNTAR as UNTAR_BLAST_DB } from '../../modules/nf-core/modules/untar/main' include { BOWTIE2_BUILD } from '../../modules/nf-core/modules/bowtie2/build/main' include { BLAST_MAKEBLASTDB } from '../../modules/nf-core/modules/blast/makeblastdb/main' include { BEDTOOLS_GETFASTA } from '../../modules/nf-core/modules/bedtools/getfasta/main' include { CUSTOM_GETCHROMSIZES } from '../../modules/nf-core/modules/custom/getchromsizes/main' +include { NEXTCLADE_DATASETGET } from '../../modules/nf-core/modules/nextclade/datasetget/main' include { COLLAPSE_PRIMERS } from '../../modules/local/collapse_primers' include { KRAKEN2_BUILD } from '../../modules/local/kraken2_build' include { SNPEFF_BUILD } from '../../modules/local/snpeff_build' @@ -163,6 +165,32 @@ workflow PREPARE_GENOME { } } + // + // Prepare Nextclade dataset + // + ch_nextclade_db = Channel.empty() + if (!params.skip_consensus && !params.skip_nextclade) { + if (params.nextclade_dataset) { + if (params.nextclade_dataset.endsWith('.tar.gz')) { + UNTAR_NEXTCLADE_DB ( + params.nextclade_dataset + ) + ch_nextclade_db = UNTAR_NEXTCLADE_DB.out.untar + ch_versions = ch_versions.mix(UNTAR_NEXTCLADE_DB.out.versions) + } else { + ch_nextclade_db = file(params.nextclade_dataset) + } + } else if (params.nextclade_dataset_name) { + NEXTCLADE_DATASETGET ( + params.nextclade_dataset_name, + params.nextclade_dataset_reference, + params.nextclade_dataset_tag + ) + ch_nextclade_db = NEXTCLADE_DATASETGET.out.dataset + ch_versions = ch_versions.mix(NEXTCLADE_DATASETGET.out.versions) + } + } + // // Prepare reference files required for de novo assembly // @@ -212,6 +240,7 @@ workflow PREPARE_GENOME { primer_bed = ch_primer_bed // path: primer.bed primer_collapsed_bed = ch_primer_collapsed_bed // path: primer.collapsed.bed primer_fasta = ch_primer_fasta // path: primer.fasta + nextclade_db = ch_nextclade_db // path: nextclade_db blast_db = ch_blast_db // path: blast_db/ kraken2_db = ch_kraken2_db // path: kraken2_db/ snpeff_db = ch_snpeff_db // path: snpeff_db diff --git a/subworkflows/local/prepare_genome_nanopore.nf b/subworkflows/local/prepare_genome_nanopore.nf index 6d18c487..124904fb 100644 --- a/subworkflows/local/prepare_genome_nanopore.nf +++ b/subworkflows/local/prepare_genome_nanopore.nf @@ -5,7 +5,9 @@ include { GUNZIP as GUNZIP_FASTA } from '../../modules/nf-core/modules/gunzip/main' include { GUNZIP as GUNZIP_GFF } from '../../modules/nf-core/modules/gunzip/main' include { GUNZIP as GUNZIP_PRIMER_BED } from '../../modules/nf-core/modules/gunzip/main' +include { UNTAR } from '../../modules/nf-core/modules/untar/main' include { CUSTOM_GETCHROMSIZES } from '../../modules/nf-core/modules/custom/getchromsizes/main' +include { NEXTCLADE_DATASETGET } from '../../modules/nf-core/modules/nextclade/datasetget/main' include { COLLAPSE_PRIMERS } from '../../modules/local/collapse_primers' include { SNPEFF_BUILD } from '../../modules/local/snpeff_build' @@ -86,6 +88,32 @@ workflow PREPARE_GENOME { ch_versions = ch_versions.mix(COLLAPSE_PRIMERS.out.versions) } + // + // Prepare Nextclade dataset + // + ch_nextclade_db = Channel.empty() + if (!params.skip_consensus && !params.skip_nextclade) { + if (params.nextclade_dataset) { + if (params.nextclade_dataset.endsWith('.tar.gz')) { + UNTAR ( + params.nextclade_dataset + ) + ch_nextclade_db = UNTAR.out.untar + ch_versions = ch_versions.mix(UNTAR.out.versions) + } else { + ch_nextclade_db = file(params.nextclade_dataset) + } + } else if (params.nextclade_dataset_name) { + NEXTCLADE_DATASETGET ( + params.nextclade_dataset_name, + params.nextclade_dataset_reference, + params.nextclade_dataset_tag + ) + ch_nextclade_db = NEXTCLADE_DATASETGET.out.dataset + ch_versions = ch_versions.mix(NEXTCLADE_DATASETGET.out.versions) + } + } + // // Make snpEff database // @@ -107,6 +135,7 @@ workflow PREPARE_GENOME { chrom_sizes = ch_chrom_sizes // path: genome.sizes primer_bed = ch_primer_bed // path: primer.bed primer_collapsed_bed = ch_primer_collapsed_bed // path: primer.collapsed.bed + nextclade_db = ch_nextclade_db // path: nextclade_db snpeff_db = ch_snpeff_db // path: snpeff_db snpeff_config = ch_snpeff_config // path: snpeff.config From 2c9ad31ce452949d490a96aa6cfa840d18d8b94d Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 18:10:42 +0000 Subject: [PATCH 066/238] Check if using --skip_consensus too --- lib/WorkflowMain.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index 40fccb68..6f9a12db 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -82,7 +82,7 @@ class WorkflowMain { } // Check Nextclade dataset parameters - if (!params.skip_nextclade) { + if (!params.skip_consensus && !params.skip_nextclade) { if (!params.nextclade_dataset && !params.nextclade_dataset_name) { log.error "Nextclade dataset not specified with '--nextclade_dataset' or '--nextclade_dataset_name'. A list of available datasets can be obtained using the Nextclade 'nextclade dataset list' command." System.exit(1) From 62f2c32a46ba1c79cafb7212ea12f4232a2ccb88 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 18:23:34 +0000 Subject: [PATCH 067/238] Fix Nextclade functionality in ONT pipeline --- CHANGELOG.md | 1 + conf/modules_nanopore.config | 20 +++++++++++++++++++- workflows/nanopore.nf | 11 ++++++----- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a075d77..14a97409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | Old parameter | New parameter | |-------------------------------|---------------------------------------| +| | `--nextclade_dataset` | | | `--nextclade_dataset_name` | | | `--nextclade_dataset_reference` | | | `--nextclade_dataset_tag` | diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 8add91ef..11bf7343 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -198,7 +198,25 @@ if (!params.skip_pangolin) { if (!params.skip_nextclade) { process { - withName: 'NEXTCLADE' { + withName: 'UNTAR' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: 'NEXTCLADE_DATASETGET' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + + withName: 'NEXTCLADE_RUN' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/nextclade" }, mode: 'copy', diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 237bd81c..06eb74c7 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -85,7 +85,7 @@ include { TABIX_TABIX } from '../modules/nf-core/modules/tabix include { BCFTOOLS_STATS } from '../modules/nf-core/modules/bcftools/stats/main' include { QUAST } from '../modules/nf-core/modules/quast/main' include { PANGOLIN } from '../modules/nf-core/modules/pangolin/main' -include { NEXTCLADE } from '../modules/nf-core/modules/nextclade/main' +include { NEXTCLADE_RUN } from '../modules/nf-core/modules/nextclade/run/main' include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/modules/custom/dumpsoftwareversions/main' include { MOSDEPTH as MOSDEPTH_GENOME } from '../modules/nf-core/modules/mosdepth/main' include { MOSDEPTH as MOSDEPTH_AMPLICON } from '../modules/nf-core/modules/mosdepth/main' @@ -380,15 +380,16 @@ workflow NANOPORE { // ch_nextclade_multiqc = Channel.empty() if (!params.skip_nextclade) { - NEXTCLADE ( - ARTIC_MINION.out.fasta + NEXTCLADE_RUN ( + ARTIC_MINION.out.fasta, + PREPARE_GENOME.out.nextclade_db ) - ch_versions = ch_versions.mix(NEXTCLADE.out.versions.first().ifEmpty(null)) + ch_versions = ch_versions.mix(NEXTCLADE_RUN.out.versions.first().ifEmpty(null)) // // MODULE: Get Nextclade clade information for MultiQC report // - NEXTCLADE + NEXTCLADE_RUN .out .csv .map { meta, csv -> From 1136cf23db576f62f9b9c44c4bbe47cee5b8e6a6 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 18:33:06 +0000 Subject: [PATCH 068/238] Fix Nextclade functionality in Illumina pipeline --- conf/modules_illumina.config | 4 ++-- subworkflows/local/variants_bcftools.nf | 12 +++++++----- subworkflows/local/variants_ivar.nf | 12 +++++++----- workflows/illumina.nf | 2 ++ 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 427ee87f..53a3b727 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -464,7 +464,7 @@ if (!params.skip_variants) { if (!params.skip_nextclade) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE_RUN' { publishDir = [ path: { "${params.outdir}/variants/ivar/nextclade" }, mode: 'copy', @@ -651,7 +651,7 @@ if (!params.skip_variants) { if (!params.skip_nextclade) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE_RUN' { publishDir = [ path: { "${params.outdir}/variants/bcftools/nextclade" }, mode: 'copy', diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index 08e29e2f..a2a09308 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -5,7 +5,7 @@ include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' include { QUAST } from '../../modules/nf-core/modules/quast/main' include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' -include { NEXTCLADE } from '../../modules/nf-core/modules/nextclade/main' +include { NEXTCLADE_RUN } from '../../modules/nf-core/modules/nextclade/run/main' include { ASCIIGENOME } from '../../modules/local/asciigenome' include { MAKE_CONSENSUS } from './make_consensus' @@ -18,6 +18,7 @@ workflow VARIANTS_BCFTOOLS { sizes // channel: /path/to/genome.sizes gff // channel: /path/to/genome.gff bed // channel: /path/to/primers.bed + nextclade_db // channel: /path/to/nextclade_db/ snpeff_db // channel: /path/to/snpeff_db/ snpeff_config // channel: /path/to/snpeff.config @@ -76,11 +77,12 @@ workflow VARIANTS_BCFTOOLS { } if (!params.skip_nextclade) { - NEXTCLADE ( - ch_consensus + NEXTCLADE_RUN ( + ch_consensus, + nextclade_db ) - ch_nextclade_report = NEXTCLADE.out.csv - ch_versions = ch_versions.mix(NEXTCLADE.out.versions.first()) + ch_nextclade_report = NEXTCLADE_RUN.out.csv + ch_versions = ch_versions.mix(NEXTCLADE_RUN.out.versions.first()) } } diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index 480f1ff3..152b7f3f 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -8,7 +8,7 @@ include { IVAR_VARIANTS } from '../../modules/nf-core/modules/ivar/varia include { IVAR_CONSENSUS } from '../../modules/nf-core/modules/ivar/consensus/main' include { QUAST } from '../../modules/nf-core/modules/quast/main' include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' -include { NEXTCLADE } from '../../modules/nf-core/modules/nextclade/main' +include { NEXTCLADE_RUN } from '../../modules/nf-core/modules/nextclade/run/main' include { ASCIIGENOME } from '../../modules/local/asciigenome' include { VCF_BGZIP_TABIX_STATS } from '../nf-core/vcf_bgzip_tabix_stats' @@ -21,6 +21,7 @@ workflow VARIANTS_IVAR { sizes // channel: /path/to/genome.sizes gff // channel: /path/to/genome.gff bed // channel: /path/to/primers.bed + nextclade_db // channel: /path/to/nextclade_db/ snpeff_db // channel: /path/to/snpeff_db/ snpeff_config // channel: /path/to/snpeff.config ivar_multiqc_header // channel: /path/to/multiqc_header for ivar variants @@ -103,11 +104,12 @@ workflow VARIANTS_IVAR { } if (!params.skip_nextclade) { - NEXTCLADE ( - ch_consensus + NEXTCLADE_RUN ( + ch_consensus, + nextclade_db ) - ch_nextclade_report = NEXTCLADE.out.csv - ch_versions = ch_versions.mix(NEXTCLADE.out.versions.first()) + ch_nextclade_report = NEXTCLADE_RUN.out.csv + ch_versions = ch_versions.mix(NEXTCLADE_RUN.out.versions.first()) } } diff --git a/workflows/illumina.nf b/workflows/illumina.nf index 20334155..c2807c3b 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -399,6 +399,7 @@ workflow ILLUMINA { PREPARE_GENOME.out.chrom_sizes, PREPARE_GENOME.out.gff, (params.protocol == 'amplicon' && params.primer_bed) ? PREPARE_GENOME.out.primer_bed : [], + PREPARE_GENOME.out.nextclade_db, PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config, ch_ivar_variants_header_mqc @@ -448,6 +449,7 @@ workflow ILLUMINA { PREPARE_GENOME.out.chrom_sizes, PREPARE_GENOME.out.gff, (params.protocol == 'amplicon' && params.primer_bed) ? PREPARE_GENOME.out.primer_bed : [], + PREPARE_GENOME.out.nextclade_db, PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config ) From f54f20228bb51472515dc6631114e42f843660b9 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 20:39:06 +0000 Subject: [PATCH 069/238] Update usage docs for Nextclade and Pangolin --- README.md | 2 ++ docs/usage.md | 49 +++++++++++++++++++++++++++++++++++++++++--- nextflow_schema.json | 2 +- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 071311d9..3775ca01 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,8 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and The nf-core/viralrecon pipeline comes with documentation about the pipeline [usage](https://nf-co.re/viralrecon/usage), [parameters](https://nf-co.re/viralrecon/parameters) and [output](https://nf-co.re/viralrecon/output). +For instructions to use more recent versions of lineage analysis tools like Pangolin and Nextclade please refer to the [updating containers](https://nf-co.re/viralrecon/usage#updating-containers) section in the usage docs. + ## Credits These scripts were originally written by [Sarai Varona](https://github.com/svarona), [Miguel Juliá](https://github.com/MiguelJulia) and [Sara Monzon](https://github.com/saramonzon) from [BU-ISCIII](https://github.com/BU-ISCIII) and co-ordinated by Isabel Cuesta for the [Institute of Health Carlos III](https://eng.isciii.es/eng.isciii.es/Paginas/Inicio.html), Spain. Through collaboration with the nf-core community the pipeline has now been updated substantially to include additional processing steps, to standardise inputs/outputs and to improve pipeline reporting; implemented and maintained primarily by Harshil Patel ([@drpatelh](https://github.com/drpatelh)) from [Seqera Labs, Spain](https://seqera.io/). diff --git a/docs/usage.md b/docs/usage.md index 1f492cd6..14576f65 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -310,7 +310,7 @@ The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementatio ```nextflow process { withName: PANGOLIN { - container = 'quay.io/biocontainers/pangolin:3.0.5--pyhdfd78af_0' + container = 'quay.io/biocontainers/pangolin:3.1.17--pyhdfd78af_1' } } ``` @@ -320,7 +320,7 @@ The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementatio ```nextflow process { withName: PANGOLIN { - container = 'https://depot.galaxyproject.org/singularity/pangolin:3.0.5--pyhdfd78af_0' + container = 'https://depot.galaxyproject.org/singularity/pangolin:3.1.17--pyhdfd78af_1' } } ``` @@ -330,11 +330,54 @@ The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementatio ```nextflow process { withName: PANGOLIN { - conda = 'bioconda::pangolin=3.0.5' + conda = 'bioconda::pangolin=3.1.17' } } ``` +You can use a similar approach to update the version of Nextclade used by the pipeline: +1. Check the default version used by the pipeline in the module file for [Nextclade](https://github.com/nf-core/viralrecon/blob/bc9ed7163796eed433580e3fa6d3421f65b939fe/modules/nf-core/modules/nextclade/main.nf#L5-L8) +2. Find the latest version of the Biocontainer available on [Quay.io](https://quay.io/repository/biocontainers/nextclade?tag=latest&tab=tags) +3. Create the custom config accordingly: + + * For Docker: + + ```nextflow + process { + withName: 'NEXTCLADE_DATASETGET|NEXTCLADE_RUN' { + container = 'quay.io/biocontainers/nextclade:1.9.0--h9ee0642_0' + } + } + ``` + + * For Singularity: + + ```nextflow + process { + withName: 'NEXTCLADE_DATASETGET|NEXTCLADE_RUN' { + container = 'https://depot.galaxyproject.org/singularity/nextclade:1.9.0--h9ee0642_0' + } + } + ``` + + * For Conda: + + ```nextflow + process { + withName: 'NEXTCLADE_DATASETGET|NEXTCLADE_RUN' { + conda = 'bioconda::nextclade=1.9.0' + } + } + ``` + +A [`nextclade dataset`](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html#nextclade-datasets) feature was introduced in [Nextclade CLI v1.3.0](https://github.com/nextstrain/nextclade/releases/tag/1.3.0) that fetches input genome files such as reference sequences and trees from a central dataset repository. We have uploaded Nextclade dataset [v2022-01-05](https://github.com/nextstrain/nextclade_data/releases/tag/2022-01-06--15-17-13--UTC) to [nf-core/test-datasets](https://github.com/nf-core/test-datasets/blob/viralrecon/genome/MN908947.3/nextclade_sars-cov-2_MN908947_2022-01-05T19_54_31Z.tar.gz), and for reproducibility, this will be used by default if you specifiy `--genome 'MN908947.3'` when running the pipeline. However, there are a number of ways you can use a more recent version of the dataset: + +* Supply your own via the `--nextclade_dataset` parameter +* Let the pipeline create and use the latest version by setting `--nextclade_dataset false --nextclade_dataset_tag false` +* Let the pipeline create and use a more recent tagged version by setting `--nextclade_dataset false --nextclade_dataset_tag ` + +If the `--save_reference` parameter is provided then the Nextclade dataset downloaded by the pipeline will be saved in the `results/genome/` directory. + > **NB:** If you wish to periodically update individual tool-specific results (e.g. Pangolin) generated by the pipeline then you must ensure to keep the `work/` directory otherwise the `-resume` ability of the pipeline will be compromised and it will restart from scratch. ### nf-core/configs diff --git a/nextflow_schema.json b/nextflow_schema.json index b6ce9a60..18214bdf 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -233,7 +233,7 @@ "properties": { "nextclade_dataset": { "type": "string", - "description": "Full path to Nextclade dataset.", + "description": "Full path to Nextclade dataset required for 'nextclade run' command.", "fa_icon": "fas fa-project-diagram" }, "nextclade_dataset_name": { From 692f4c9935bcac7a7f81b932af07552047adb9f5 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 20:40:54 +0000 Subject: [PATCH 070/238] Fix markdownlitn --- docs/usage.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/usage.md b/docs/usage.md index 14576f65..1e21f1a5 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -336,6 +336,7 @@ The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementatio ``` You can use a similar approach to update the version of Nextclade used by the pipeline: + 1. Check the default version used by the pipeline in the module file for [Nextclade](https://github.com/nf-core/viralrecon/blob/bc9ed7163796eed433580e3fa6d3421f65b939fe/modules/nf-core/modules/nextclade/main.nf#L5-L8) 2. Find the latest version of the Biocontainer available on [Quay.io](https://quay.io/repository/biocontainers/nextclade?tag=latest&tab=tags) 3. Create the custom config accordingly: From 821dc337fdcaf1978e74bec2d4404a0faf5d4523 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 20:49:19 +0000 Subject: [PATCH 071/238] Update docs --- docs/usage.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 1e21f1a5..f3c801cf 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -373,11 +373,11 @@ You can use a similar approach to update the version of Nextclade used by the pi A [`nextclade dataset`](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html#nextclade-datasets) feature was introduced in [Nextclade CLI v1.3.0](https://github.com/nextstrain/nextclade/releases/tag/1.3.0) that fetches input genome files such as reference sequences and trees from a central dataset repository. We have uploaded Nextclade dataset [v2022-01-05](https://github.com/nextstrain/nextclade_data/releases/tag/2022-01-06--15-17-13--UTC) to [nf-core/test-datasets](https://github.com/nf-core/test-datasets/blob/viralrecon/genome/MN908947.3/nextclade_sars-cov-2_MN908947_2022-01-05T19_54_31Z.tar.gz), and for reproducibility, this will be used by default if you specifiy `--genome 'MN908947.3'` when running the pipeline. However, there are a number of ways you can use a more recent version of the dataset: -* Supply your own via the `--nextclade_dataset` parameter -* Let the pipeline create and use the latest version by setting `--nextclade_dataset false --nextclade_dataset_tag false` -* Let the pipeline create and use a more recent tagged version by setting `--nextclade_dataset false --nextclade_dataset_tag ` +* Supply your own by setting: `--nextclade_dataset ` +* Let the pipeline create and use the latest version by setting: `--nextclade_dataset false --nextclade_dataset_tag false` +* Let the pipeline create and use a specific, tagged version by setting: `--nextclade_dataset false --nextclade_dataset_tag ` -If the `--save_reference` parameter is provided then the Nextclade dataset downloaded by the pipeline will be saved in the `results/genome/` directory. +If the `--save_reference` parameter is provided then the Nextclade dataset generated by the pipeline will also be saved in the `results/genome/` directory. > **NB:** If you wish to periodically update individual tool-specific results (e.g. Pangolin) generated by the pipeline then you must ensure to keep the `work/` directory otherwise the `-resume` ability of the pipeline will be compromised and it will restart from scratch. From 049ba4fbc3c37beb9d343f22d4d6dbe0c1446319 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 21:29:58 +0000 Subject: [PATCH 072/238] Fix missing process blocks in modules_illumina.config --- conf/modules_illumina.config | 64 +++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 53a3b727..213888e6 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -91,13 +91,15 @@ if (!params.skip_fastp) { } if (!params.skip_fastqc) { - withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_TRIM' { - ext.args = '--quiet' - publishDir = [ - path: { "${params.outdir}/fastqc/trim" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_TRIM' { + ext.args = '--quiet' + publishDir = [ + path: { "${params.outdir}/fastqc/trim" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } } } @@ -345,31 +347,33 @@ if (!params.skip_variants) { } if (params.protocol == 'amplicon') { - withName: 'COLLAPSE_PRIMERS' { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } + process { + withName: 'COLLAPSE_PRIMERS' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } - withName: 'MOSDEPTH_AMPLICON' { - ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' - publishDir = [ - path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, - mode: 'copy', - pattern: "*.{summary.txt}" - ] - } + withName: 'MOSDEPTH_AMPLICON' { + ext.args = '--fast-mode --use-median --thresholds 0,1,10,50,100,500' + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, + mode: 'copy', + pattern: "*.{summary.txt}" + ] + } - withName: 'PLOT_MOSDEPTH_REGIONS_AMPLICON' { - ext.args = '--input_suffix .regions.bed.gz' - publishDir = [ - path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, - mode: 'copy', - pattern: "*.{tsv,pdf}" - ] + withName: 'PLOT_MOSDEPTH_REGIONS_AMPLICON' { + ext.args = '--input_suffix .regions.bed.gz' + publishDir = [ + path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, + mode: 'copy', + pattern: "*.{tsv,pdf}" + ] + } } } } From 1b63667edb685de79342f8e2bce81e7742b196f8 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 21:42:15 +0000 Subject: [PATCH 073/238] Fix missing process blocks in modules_nanopore.config --- conf/modules_nanopore.config | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 11bf7343..9ce51ebe 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -24,7 +24,7 @@ process { ] } - withName: 'MULTIQC_TSV_.*' { + withName: 'MULTIQC_TSV_BARCODE_COUNT|MULTIQC_TSV_GUPPYPLEX_COUNT' { publishDir = [ path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, enabled: false @@ -111,6 +111,15 @@ process { // Optional configuration options // +if (params.input) { + withName: 'MULTIQC_TSV_NO_.*' { + publishDir = [ + path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, + enabled: false + ] + } +} + if (params.sequencing_summary && !params.skip_pycoqc) { process { withName: 'PYCOQC' { @@ -223,6 +232,13 @@ if (!params.skip_nextclade) { pattern: "*.{csv}" ] } + + withName: 'MULTIQC_TSV_NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, + enabled: false + ] + } } } From 6fed011664916e2dd7914100bb6ca5724c025420 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 17 Jan 2022 21:45:51 +0000 Subject: [PATCH 074/238] Add missing process block --- conf/modules_nanopore.config | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 9ce51ebe..39030207 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -112,11 +112,13 @@ process { // if (params.input) { - withName: 'MULTIQC_TSV_NO_.*' { - publishDir = [ - path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, - enabled: false - ] + process { + withName: 'MULTIQC_TSV_NO_.*' { + publishDir = [ + path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, + enabled: false + ] + } } } From 56c4a4b3db1fda61e1453b4f8624d39f46d47d02 Mon Sep 17 00:00:00 2001 From: svarona Date: Tue, 18 Jan 2022 10:33:02 +0100 Subject: [PATCH 075/238] Fixed script for new masking with mpileup --- bin/make_bed_mask.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/make_bed_mask.py b/bin/make_bed_mask.py index efd99057..6f59033c 100755 --- a/bin/make_bed_mask.py +++ b/bin/make_bed_mask.py @@ -66,7 +66,7 @@ def make_bed_mask(bed_in, bed_out, indels_pos_len): for position in indels_positions: indel_init_pos = position indel_whole_length = indels_pos_len[position] - indel_end_pos = int(indel_init_pos) + int(indel_whole_length) + indel_end_pos = int(indel_init_pos) + int(indel_whole_length)-1 if int(init_pos) in range( int(indel_init_pos), int(indel_end_pos) ) or int(end_pos) in range(int(indel_init_pos), int(indel_end_pos)): @@ -75,7 +75,7 @@ def make_bed_mask(bed_in, bed_out, indels_pos_len): else: oline = ref_genome + "\t" + init_pos + "\t" + end_pos if test: - fout.write(oline) + fout.write(oline + "\n") def main(args=None): From b62d8ed5ce99fab1773807780dd37f756deefaed Mon Sep 17 00:00:00 2001 From: svarona Date: Tue, 18 Jan 2022 10:35:43 +0100 Subject: [PATCH 076/238] Added arg to new make_bed_mask process with mpileup --- conf/modules_illumina.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 1d178878..d1d68983 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -595,6 +595,8 @@ if (!params.skip_variants) { } withName: 'MAKE_BED_MASK' { + ext.args = "-a --ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 0" + ext.args2 = "10" ext.prefix = { "${meta.id}.coverage.masked" } publishDir = [ path: { "${params.outdir}/variants/bcftools" }, From 2afbaa07e59bcbe61153ccb94e4801a38913b9dd Mon Sep 17 00:00:00 2001 From: svarona Date: Tue, 18 Jan 2022 10:59:57 +0100 Subject: [PATCH 077/238] Added samtools mpileup to obtain masking bed --- modules/local/make_bed_mask.nf | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index cd608660..a36bc141 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -1,10 +1,10 @@ process MAKE_BED_MASK { tag "$meta.id" - conda (params.enable_conda ? "conda-forge::python=3.9.5" : null) + conda (params.enable_conda ? "conda-forge::python=3.9.5 bioconda::samtools=1.12" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/python:3.9--1' : - 'quay.io/biocontainers/python:3.9--1' }" + 'https://depot.galaxyproject.org/singularity/plasmidid:1.6.5--hdfd78af_0' : + 'quay.io/biocontainers/plasmidid:1.6.5--hdfd78af_0' }" input: tuple val(meta), path(bam), path(vcf) @@ -15,12 +15,22 @@ process MAKE_BED_MASK { path "versions.yml" , emit: versions script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' def prefix = task.ext.prefix ?: "${meta.id}" + def save_mpileup = params.save_mpileup ? "tee ${prefix}.mpileup |" : "" + """ - + samtools mpileup \\ + $args \\ + --reference $fasta \\ + $bam | \\ + $save_mpileup \\ + awk -v OFS='\\t' '{print \$1, \$2-1, \$2, \$4}' | awk '\$4 < $args2' > lowcov.bes + make_bed_mask.py \\ $vcf \\ - $bed \\ + lowcov.bes \\ ${prefix}.bed cat <<-END_VERSIONS > versions.yml From 9dfc86d38542de575b1147adf32adbe63b7dca22 Mon Sep 17 00:00:00 2001 From: svarona Date: Tue, 18 Jan 2022 11:01:54 +0100 Subject: [PATCH 078/238] Fixed bedtools maskfasta input as the output of bedtools merge --- subworkflows/local/make_consensus.nf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index 3d0f39c9..c90f28fc 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -18,18 +18,18 @@ workflow MAKE_CONSENSUS { ch_versions = Channel.empty() MAKE_BED_MASK ( - bam_vcf + bam_vcf, fasta ) ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) BEDTOOLS_MERGE ( - MAKE_BED_MASK.out.genomecov + MAKE_BED_MASK.out.bed ) ch_versions = ch_versions.mix(BEDTOOLS_MERGE.out.versions.first()) BEDTOOLS_MASKFASTA ( - MAKE_BED_MASK.out.bed, + BEDTOOLS_MERGE.out.bed, fasta ) ch_versions = ch_versions.mix(BEDTOOLS_MASKFASTA.out.versions.first()) From f327393b367b1092e725c3936b5cba124518154a Mon Sep 17 00:00:00 2001 From: svarona Date: Tue, 18 Jan 2022 11:19:08 +0100 Subject: [PATCH 079/238] Fixed file name with mpileup positions to mask --- modules/local/make_bed_mask.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index a36bc141..00dfceba 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -26,11 +26,11 @@ process MAKE_BED_MASK { --reference $fasta \\ $bam | \\ $save_mpileup \\ - awk -v OFS='\\t' '{print \$1, \$2-1, \$2, \$4}' | awk '\$4 < $args2' > lowcov.bes + awk -v OFS='\\t' '{print \$1, \$2-1, \$2, \$4}' | awk '\$4 < $args2' > lowcov_positions make_bed_mask.py \\ $vcf \\ - lowcov.bes \\ + lowcov_positions \\ ${prefix}.bed cat <<-END_VERSIONS > versions.yml From f91936642010fa675bcb7ffb26732847deb6b50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 11:24:04 +0100 Subject: [PATCH 080/238] updated container and conda packages for ivar_variants_to_vcf --- modules/local/ivar_variants_to_vcf.nf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/local/ivar_variants_to_vcf.nf b/modules/local/ivar_variants_to_vcf.nf index 4b3f696a..ad4dcbc3 100644 --- a/modules/local/ivar_variants_to_vcf.nf +++ b/modules/local/ivar_variants_to_vcf.nf @@ -1,10 +1,10 @@ process IVAR_VARIANTS_TO_VCF { tag "$meta.id" - conda (params.enable_conda ? "conda-forge::python=3.9.5" : null) + conda (params.enable_conda ? "conda-forge::python=3.9.5 conda-forge::matplotlib=3.5.1 conda-forge::pandas=1.3.5 conda-forge::r-sys=3.4 conda-forge::regex=2021.11.10 conda-forge::scipy=1.7.3" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/python:3.9--1' : - 'quay.io/biocontainers/python:3.9--1' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-77320db00eefbbf8c599692102c3d387a37ef02a:08144a66f00dc7684fad061f1466033c0176e7ad-0' : + 'quay.io/biocontainers/mulled-v2-77320db00eefbbf8c599692102c3d387a37ef02a:08144a66f00dc7684fad061f1466033c0176e7ad-0' }" input: tuple val(meta), path(tsv) From 07defc38ddefda190de64375bd0c5ddacae1e8e3 Mon Sep 17 00:00:00 2001 From: svarona Date: Tue, 18 Jan 2022 11:25:55 +0100 Subject: [PATCH 081/238] Added gawk to conda package --- modules/local/make_bed_mask.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index 00dfceba..e3126e0d 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -1,7 +1,7 @@ process MAKE_BED_MASK { tag "$meta.id" - conda (params.enable_conda ? "conda-forge::python=3.9.5 bioconda::samtools=1.12" : null) + conda (params.enable_conda ? "conda-forge::python=3.9.5 conda-forge::gawk=5.1.0 bioconda::samtools=1.12" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/plasmidid:1.6.5--hdfd78af_0' : 'quay.io/biocontainers/plasmidid:1.6.5--hdfd78af_0' }" From dbd3dc522323f11ec0a74ea51e75d8c930ea4124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 11:27:40 +0100 Subject: [PATCH 082/238] Add arg not_strand_bias if protocol amplicon in config --- conf/modules_illumina.config | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 427ee87f..f764dcdf 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -385,14 +385,24 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - - withName: 'IVAR_VARIANTS_TO_VCF' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/log" }, - mode: 'copy', - pattern: '*.log' - ] - } + if (params.protocol == "amplicon"){ + withName: 'IVAR_VARIANTS_TO_VCF' { + ext.args = '--not_strand_bias' + publishDir = [ + path: { "${params.outdir}/variants/ivar/log" }, + mode: 'copy', + pattern: '*.log' + ] + } + else{ + withName: 'IVAR_VARIANTS_TO_VCF' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/log" }, + mode: 'copy', + pattern: '*.log' + ] + } + } withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:TABIX_BGZIP' { publishDir = [ From 8f4ac9fc1c19e342722ea710be1c5708ee5404a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 12:22:47 +0100 Subject: [PATCH 083/238] fix tabs to spaces --- conf/modules_illumina.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index f764dcdf..f63a5bfe 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -385,8 +385,8 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - if (params.protocol == "amplicon"){ - withName: 'IVAR_VARIANTS_TO_VCF' { + if (params.protocol == "amplicon"){ + withName: 'IVAR_VARIANTS_TO_VCF' { ext.args = '--not_strand_bias' publishDir = [ path: { "${params.outdir}/variants/ivar/log" }, From 73f71c45e9a53c4c6f4e7b1074d5e6ecfebfa1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 12:26:32 +0100 Subject: [PATCH 084/238] fix tabs to spaces --- conf/modules_illumina.config | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index f63a5bfe..3c22f2bf 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -387,22 +387,22 @@ if (!params.skip_variants) { } if (params.protocol == "amplicon"){ withName: 'IVAR_VARIANTS_TO_VCF' { - ext.args = '--not_strand_bias' - publishDir = [ - path: { "${params.outdir}/variants/ivar/log" }, - mode: 'copy', - pattern: '*.log' - ] - } - else{ - withName: 'IVAR_VARIANTS_TO_VCF' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/log" }, - mode: 'copy', - pattern: '*.log' - ] - } - } + ext.args = '--not_strand_bias' + publishDir = [ + path: { "${params.outdir}/variants/ivar/log" }, + mode: 'copy', + pattern: '*.log' + ] + } + else{ + withName: 'IVAR_VARIANTS_TO_VCF' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/log" }, + mode: 'copy', + pattern: '*.log' + ] + } + } withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:TABIX_BGZIP' { publishDir = [ From 31814276faed8aca65a5b65545af3ffdcca9e5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 12:38:41 +0100 Subject: [PATCH 085/238] fix missing curly --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 3c22f2bf..32e07d7d 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -394,7 +394,7 @@ if (!params.skip_variants) { pattern: '*.log' ] } - else{ + }else{ withName: 'IVAR_VARIANTS_TO_VCF' { publishDir = [ path: { "${params.outdir}/variants/ivar/log" }, From 74b1c3940c59d480bea27a2462acb502e67a9b50 Mon Sep 17 00:00:00 2001 From: "erika.kvalem" Date: Tue, 18 Jan 2022 12:51:32 +0100 Subject: [PATCH 086/238] added parser_ivar_bcftools with relative paths --- bin/parser_ivar_bcftools.py | 200 ++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 bin/parser_ivar_bcftools.py diff --git a/bin/parser_ivar_bcftools.py b/bin/parser_ivar_bcftools.py new file mode 100644 index 00000000..f971c7e5 --- /dev/null +++ b/bin/parser_ivar_bcftools.py @@ -0,0 +1,200 @@ +from matplotlib import table +import pandas as pd +import sys +import numpy as np +import re +import argparse +import glob, os +import io +pd.set_option('display.max_columns', None) +pd.set_option('display.max_rows', None) + +#Current working directory +cwd = os.getcwd() +os.chdir(cwd) + +#create SampleTables folder in the folder where the script is running +sample_table_path = os.path.join(cwd, "SampleTables") +os.mkdir(sample_table_path) + + + +def parser_args(args=None): + Description = 'Create long/wide tables fo ivar/bcftools' + Epilog = """Example usage: python parser_ivar_bcftools.py """ + parser = argparse.ArgumentParser(description=Description, epilog=Epilog) + parser.add_argument('-sample', help="Input sample table files path." ) + parser.add_argument('-snpsift', help="Input snpsift txt files path.",required=True) + parser.add_argument('-pangolin', help="Input pangolin csv files path.",required=True) + parser.add_argument('-software', help="Input bcftools of ivar.",required=True) + + return parser.parse_args(args) + + +def create_long(in_table,snp_table,pango_table,counter,software): + + if not in_table: + intable_pathname = cwd + else: + intable_pathname = os.path.join(cwd, in_table) + os.chdir(intable_pathname) + table_list = [] + for file in glob.glob("*_norm.table"): + table_list.append(file) + table_list.sort() + + + snp_pathname = os.path.join(cwd, snp_table) + os.chdir(snp_pathname) + snp_list = [] + for file in glob.glob("*_norm.snpsift.txt"): + snp_list.append(file) + snp_list.sort() + + pango_pathname = os.path.join(cwd, pango_table) + os.chdir(pango_pathname) + pangolin_list = [] + for file in glob.glob("*.csv"): + pangolin_list.append(file) + pangolin_list.sort() + + ### format of sample table + sample_table=pd.read_table(str(intable_pathname) + "/" + table_list[counter], header='infer') + sample_table = sample_table.dropna(how = 'all', axis =1) + + if software=='bcftools': + sample_table.rename(columns={sample_table.columns[5]: "DP",sample_table.columns[6]: "AD"}, inplace=True) + + new_column = sample_table + new_column[['REF_DP','ALT_DP']] = sample_table['AD'].str.split(',', expand=True) + sample_table = pd.merge(sample_table,new_column,how = 'left') + sample_table[["ALT_DP", "DP"]] = sample_table[["ALT_DP", "DP"]].apply(pd.to_numeric) + sample_table['AF']=sample_table['ALT_DP']/sample_table['DP'] + sample_table['AF'] = sample_table['AF'].round(2) + sample_table = sample_table.loc[:, ~sample_table.columns.str.contains('AD')] + + elif software=='ivar': + sample_table.rename(columns={sample_table.columns[0]: "CHROM",sample_table.columns[1]: "POS",sample_table.columns[2]: "REF",sample_table.columns[3]: "ALT",sample_table.columns[4]: "FILTER",sample_table.columns[5]: "DP",sample_table.columns[6]: "REF_DP", sample_table.columns[7]: "ALT_DP"}, inplace=True) + sample_table[["ALT_DP", "DP"]] = sample_table[["ALT_DP", "DP"]].apply(pd.to_numeric) + sample_table['AF']=sample_table['ALT_DP']/sample_table['DP'] + sample_table['AF'] = sample_table['AF'].round(2) + + ### format of snpsift table + snpsift_table = pd.read_csv(str(snp_pathname) + '/' + snp_list[counter], sep="\t", header = "infer") + snpsift_table = snpsift_table.loc[:, ~snpsift_table.columns.str.contains('^Unnamed')] + colnames_snpsift = list(snpsift_table.columns) + colnames_snpsift = [i.replace('ANN[*].', '') for i in colnames_snpsift] + for i in range(len(colnames_snpsift)): + snpsift_table.rename(columns = {snpsift_table.columns[i]:colnames_snpsift[i]}, inplace = True) + snpsift_table = snpsift_table.loc[:, ['CHROM','POS','REF','ALT','GENE','EFFECT','HGVS_C','HGVS_P']] + + snpsift_table_copy = snpsift_table.copy() + + for i in range(len(snpsift_table_copy)): + for j in range(3,8): + snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] + + + + #format of lineages + pangolin_table = pd.read_csv(str(pango_pathname) + '/' + pangolin_list[counter], sep=",", header = "infer") + lineages = pangolin_table.loc[:,['taxon','lineage']] + + #table long one sample + tl_onesample = pd.DataFrame(data =sample_table) + if software=='bcftools': + tl_onesample["Sample"] = lineages.iloc[0,0] + elif software=='ivar': + tl_onesample["Sample"] = lineages.iloc[0,0].split('_')[1].split('.')[0] + tl_onesample["software"] = software + tl_onesample["Lineage"] = lineages.iloc[0,1] + + return tl_onesample,snpsift_table_copy,counter + + + +def mergetables(sample_intable,snp_intable,path,counter): + + if not path: + merge_pathname = cwd + else: + merge_pathname = os.path.join(cwd, path) + + os.chdir(merge_pathname) + table_list = [] + for file in glob.glob("*.table"): + table_list.append(file[0:6]) + table_list.sort() + + left = snp_intable + right = sample_intable + merged_table_long = pd.merge(left,right,how = 'outer') + + merged_table_long.to_csv(str(merge_pathname) + '/SampleTables/'+ table_list[counter] + '.csv', header='infer', index=None, sep=' ', mode='a') + return(merged_table_long) + +def loop(path_in): + + if not path_in: + in_pathname = cwd + else: + in_pathname = os.path.join(cwd, path_in) + + + os.chdir(in_pathname) + table_list = [] + for file in glob.glob("*.table"): + table_list.append(file) + table_list.sort() + + length = len(table_list) + + return length + + +def concatenatetable(path_in_concat): + + if not path_in_concat: + concat_pathname = os.path.join(cwd, 'SampleTables') + else: + concat_pathname = os.path.join(cwd,path_in_concat,'SampleTables') + + + os.chdir(concat_pathname) + + extension = 'csv' + all_filenames = [i for i in glob.glob('*.{}'.format(extension))] + + + #combine all files in the list + #combined_csv = pd.concat([pd.read_csv(f) for f in all_filenames ]) + list_of_dataframes = [] + for i in all_filenames: + list_of_dataframes.append(pd.read_csv(i,sep=" ")) + + merged_df = pd.concat(list_of_dataframes) + merged_df.to_csv("final_long_table.csv", index=False, encoding='utf-8-sig') + return merged_df + + + + +def main(args=None): + + + + args = parser_args(args) + + + length = loop(args.sample) + + for i in range(0,length): + + tl_onesample_out,snpsift_table_final,counter_it = create_long(args.sample,args.snpsift, args.pangolin,i,args.software) + merged = mergetables(tl_onesample_out,snpsift_table_final,args.sample,counter_it) + + merged_df= concatenatetable(args.sample) + + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file From 4c688cf5844058d50b9d0e4686b08e2ffe426c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 13:10:37 +0100 Subject: [PATCH 087/238] Fix bug when two consecutive and not same codon --- bin/ivar_variants_to_vcf.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 0c656009..3e21b7c5 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -99,7 +99,7 @@ def rename_vars(dict_lines,num_collapse): The function set the vars acordingly to the lines to collapse do to consecutive variants. Input: dict_lines - Dict with var lines. - num_collapse - number of lines to collapse [1,2] + num_collapse - number of lines to collapse [2,3] Returns:: Vars fixed. ''' @@ -107,11 +107,11 @@ def rename_vars(dict_lines,num_collapse): POS = dict_lines["POS"][0] ID = dict_lines["ID"][0] # If two consecutive collapse 2 lines into one. - if num_collapse == 2: + if int(num_collapse) == 2: REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) # If three consecutive collapse 3 lines into one. - elif num_collapse == 3: + elif int(num_collapse) == 3: REF = str(dict_lines["REF"][0]) + str(dict_lines["REF"][1]) + str(dict_lines["REF"][2]) ALT = str(dict_lines["ALT"][0]) + str(dict_lines["ALT"][1]) + str(dict_lines["ALT"][2]) ## TODO Check how much differences we found among DPs in the three positions of a codon. @@ -279,7 +279,6 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= elif len(dict_lines["POS"]) == 2: for i,j in enumerate(dict_lines): dict_lines.setdefault(j, []).append(param_list[i]) - # Are two positions in the dict consecutive? if check_consecutive(dict_lines["POS"]) == 2: ## If the first position is not on the third position of the codon they are in the same codon. @@ -292,6 +291,12 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines[list(dict_lines.keys())[i]].pop(0) + else: + writeLine = True + oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") + + for i,j in enumerate(dict_lines): + dict_lines[list(dict_lines.keys())[i]].pop(0) # Are the three positions in the dict consecutive? elif check_consecutive(dict_lines["POS"]) == 3: @@ -302,9 +307,9 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= num_collapse = 3 CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) - #for i,j in enumerate(dict_lines): - # dict_lines[list(dict_lines.keys())[i]].pop(0) - # dict_lines[list(dict_lines.keys())[i]].pop(0) + for i,j in enumerate(dict_lines): + dict_lines[list(dict_lines.keys())[i]].pop(0) + dict_lines[list(dict_lines.keys())[i]].pop(0) # we empty the dict_lines dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[], 'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER':[],'INFO':[],'FORMAT':[],'SAMPLE':[]} @@ -324,9 +329,9 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) + elif check_consecutive(dict_lines["POS"]) == False: writeLine = True - oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") for i,j in enumerate(dict_lines): From f7c4a801936883cfba5d84c1ea7008ff598db92a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 13:28:08 +0100 Subject: [PATCH 088/238] Fix header vcf --- bin/ivar_variants_to_vcf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 3e21b7c5..1b111e48 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -168,6 +168,7 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= '##INFO=\n' '##FILTER=\n' '##FILTER= 0.05">\n' + '##FILTER=\n' '##FORMAT=\n' '##FORMAT=\n' '##FORMAT=\n' @@ -177,8 +178,9 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= '##FORMAT=\n' '##FORMAT=\n' ) + if header += ( - "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tINFO[SB]\tFORMAT\t" + filename + "\n" + "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t" + filename + "\n" ) varList = [] From 1646c66c433d169ae62d9570a5f01501a71b614a Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Tue, 18 Jan 2022 12:28:11 +0000 Subject: [PATCH 089/238] Small changes to documentation --- README.md | 7 ++++++- docs/images/nextclade_tag_example.png | Bin 0 -> 38057 bytes docs/usage.md | 12 +++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 docs/images/nextclade_tag_example.png diff --git a/README.md b/README.md index 3775ca01..30b5f94d 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,12 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and The nf-core/viralrecon pipeline comes with documentation about the pipeline [usage](https://nf-co.re/viralrecon/usage), [parameters](https://nf-co.re/viralrecon/parameters) and [output](https://nf-co.re/viralrecon/output). -For instructions to use more recent versions of lineage analysis tools like Pangolin and Nextclade please refer to the [updating containers](https://nf-co.re/viralrecon/usage#updating-containers) section in the usage docs. +--- +**Keeping the pipeline up to date** + +___ + +The Pangolin and Nextclade lineage and clade definitions change regularly as new SARS-CoV-2 lineages are discovered. For instructions to use more recent versions of lineage analysis tools like Pangolin and Nextclade please refer to the [updating containers](https://nf-co.re/viralrecon/usage#updating-containers) section in the usage docs. ## Credits diff --git a/docs/images/nextclade_tag_example.png b/docs/images/nextclade_tag_example.png new file mode 100644 index 0000000000000000000000000000000000000000..8c409fc9f13c3f30cb3217e6c33d3079b7944899 GIT binary patch literal 38057 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 * Let the pipeline create and use the latest version by setting: `--nextclade_dataset false --nextclade_dataset_tag false` * Let the pipeline create and use a specific, tagged version by setting: `--nextclade_dataset false --nextclade_dataset_tag ` +The Nextclade data releases can be found on their [Github page](https://github.com/nextstrain/nextclade_data/releases). Use the tag specified for each release e.g + +![Nextclade tag example](images/nextclade_tag_example.png) + If the `--save_reference` parameter is provided then the Nextclade dataset generated by the pipeline will also be saved in the `results/genome/` directory. > **NB:** If you wish to periodically update individual tool-specific results (e.g. Pangolin) generated by the pipeline then you must ensure to keep the `work/` directory otherwise the `-resume` ability of the pipeline will be compromised and it will restart from scratch. From 919703eec90db50374c0fe5388a2761a590febf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 13:32:11 +0100 Subject: [PATCH 090/238] fixed typo --- bin/ivar_variants_to_vcf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 1b111e48..5a9e6b59 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -178,7 +178,6 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= '##FORMAT=\n' '##FORMAT=\n' ) - if header += ( "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t" + filename + "\n" ) From 4942a8b0a96de3fb0d4b50ed36576de197569849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 18 Jan 2022 14:13:36 +0100 Subject: [PATCH 091/238] fix quotes --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 32e07d7d..366156f0 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -385,7 +385,7 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - if (params.protocol == "amplicon"){ + if (params.protocol == 'amplicon') { withName: 'IVAR_VARIANTS_TO_VCF' { ext.args = '--not_strand_bias' publishDir = [ From 20f72bcccded73e851ff62a391946a05ffcadd33 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 18 Jan 2022 22:20:52 +0000 Subject: [PATCH 092/238] Update changes from bcftools consensus mask fix PR #255 --- conf/modules_illumina.config | 35 ++++-------- modules.json | 3 - modules/local/make_bed_mask.nf | 25 +++++---- .../modules/bedtools/genomecov/main.nf | 56 ------------------- .../modules/bedtools/genomecov/meta.yml | 51 ----------------- subworkflows/local/make_consensus.nf | 5 +- 6 files changed, 27 insertions(+), 148 deletions(-) delete mode 100644 modules/nf-core/modules/bedtools/genomecov/main.nf delete mode 100644 modules/nf-core/modules/bedtools/genomecov/meta.yml diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index fa0440d9..0915890e 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -332,7 +332,7 @@ if (!params.skip_variants) { publishDir = [ path: { "${params.outdir}/variants/bowtie2/mosdepth/genome" }, mode: 'copy', - pattern: "*.{summary.txt}" + pattern: "*.summary.txt" ] } @@ -362,7 +362,7 @@ if (!params.skip_variants) { publishDir = [ path: { "${params.outdir}/variants/bowtie2/mosdepth/amplicon" }, mode: 'copy', - pattern: "*.{summary.txt}" + pattern: "*.summary.txt" ] } @@ -472,7 +472,7 @@ if (!params.skip_variants) { publishDir = [ path: { "${params.outdir}/variants/ivar/nextclade" }, mode: 'copy', - pattern: "*.{csv}" + pattern: "*.csv" ] } } @@ -581,35 +581,20 @@ if (!params.skip_variants) { if (!params.skip_consensus) { process { - withName: 'BEDTOOLS_GENOMECOV' { - ext.args = "-bga | awk '\$4 < 10'" - ext.prefix = { "${meta.id}.coverage" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - enabled: false - ] - } - - withName: 'BEDTOOLS_MERGE' { - ext.prefix = { "${meta.id}.coverage.merged" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - enabled: false - ] - } - withName: 'MAKE_BED_MASK' { ext.args = "-a --ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 0" - ext.args2 = "10" + ext.args2 = 10 ext.prefix = { "${meta.id}.coverage.masked" } publishDir = [ path: { "${params.outdir}/variants/bcftools" }, - enabled: false + mode: 'copy', + pattern: "*.mpileup", + enabled: params.save_mpileup ] } - withName: 'BEDTOOLS_MASKFASTA' { - ext.prefix = { "${meta.id}.masked" } + withName: 'BEDTOOLS_MERGE' { + ext.prefix = { "${meta.id}.coverage.merged" } publishDir = [ path: { "${params.outdir}/variants/bcftools" }, enabled: false @@ -661,7 +646,7 @@ if (!params.skip_variants) { publishDir = [ path: { "${params.outdir}/variants/bcftools/nextclade" }, mode: 'copy', - pattern: "*.{csv}" + pattern: "*.csv" ] } } diff --git a/modules.json b/modules.json index 716cf7a1..4922ac49 100644 --- a/modules.json +++ b/modules.json @@ -24,9 +24,6 @@ "bcftools/stats": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, - "bedtools/genomecov": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" - }, "bedtools/getfasta": { "git_sha": "d473a247d2e0c619b0df877ea19d9a5a98c8e3c8" }, diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index e3126e0d..18d060cc 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -8,33 +8,36 @@ process MAKE_BED_MASK { input: tuple val(meta), path(bam), path(vcf) - path fasta + path fasta + val save_mpileup output: - tuple val(meta), path("*.bed") , emit: bed - path "versions.yml" , emit: versions + tuple val(meta), path("*.bed") , emit: bed + tuple val(meta), path("*.mpileup"), optional:true, emit: mpileup + path "versions.yml" , emit: versions script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ def args = task.ext.args ?: '' - def args2 = task.ext.args2 ?: '' + def args2 = task.ext.args2 ?: 10 def prefix = task.ext.prefix ?: "${meta.id}" - def save_mpileup = params.save_mpileup ? "tee ${prefix}.mpileup |" : "" - + def mpileup = save_mpileup ? "| tee ${prefix}.mpileup" : "" """ - samtools mpileup \\ + samtools \\ + mpileup \\ $args \\ --reference $fasta \\ - $bam | \\ - $save_mpileup \\ - awk -v OFS='\\t' '{print \$1, \$2-1, \$2, \$4}' | awk '\$4 < $args2' > lowcov_positions + $bam \\ + $mpileup \\ + | awk -v OFS='\\t' '{print \$1, \$2-1, \$2, \$4}' | awk '\$4 < $args2' > lowcov_positions.txt make_bed_mask.py \\ $vcf \\ - lowcov_positions \\ + lowcov_positions.txt \\ ${prefix}.bed cat <<-END_VERSIONS > versions.yml "${task.process}": + samtools: \$(echo \$(samtools --version 2>&1) | sed 's/^.*samtools //; s/Using.*\$//') python: \$(python --version | sed 's/Python //g') END_VERSIONS """ diff --git a/modules/nf-core/modules/bedtools/genomecov/main.nf b/modules/nf-core/modules/bedtools/genomecov/main.nf deleted file mode 100644 index ca491e75..00000000 --- a/modules/nf-core/modules/bedtools/genomecov/main.nf +++ /dev/null @@ -1,56 +0,0 @@ -process BEDTOOLS_GENOMECOV { - tag "$meta.id" - label 'process_medium' - - conda (params.enable_conda ? "bioconda::bedtools=2.30.0" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/bedtools:2.30.0--hc088bd4_0' : - 'quay.io/biocontainers/bedtools:2.30.0--hc088bd4_0' }" - - input: - tuple val(meta), path(intervals), val(scale) - path sizes - val extension - - output: - tuple val(meta), path("*.${extension}"), emit: genomecov - path "versions.yml" , emit: versions - - script: - def args = task.ext.args ?: '' - def args_list = args.tokenize() - args += (scale > 0 && scale != 1) ? " -scale $scale" : "" - if (!args_list.contains('-bg') && (scale > 0 && scale != 1)) { - args += " -bg" - } - - def prefix = task.ext.prefix ?: "${meta.id}" - if (intervals.name =~ /\.bam/) { - """ - bedtools \\ - genomecov \\ - -ibam $intervals \\ - $args \\ - > ${prefix}.${extension} - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") - END_VERSIONS - """ - } else { - """ - bedtools \\ - genomecov \\ - -i $intervals \\ - -g $sizes \\ - $args \\ - > ${prefix}.${extension} - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - bedtools: \$(bedtools --version | sed -e "s/bedtools v//g") - END_VERSIONS - """ - } -} diff --git a/modules/nf-core/modules/bedtools/genomecov/meta.yml b/modules/nf-core/modules/bedtools/genomecov/meta.yml deleted file mode 100644 index 0713e95b..00000000 --- a/modules/nf-core/modules/bedtools/genomecov/meta.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: bedtools_genomecov -description: Computes histograms (default), per-base reports (-d) and BEDGRAPH (-bg) summaries of feature coverage (e.g., aligned sequences) for a given genome. -keywords: - - bed - - bam - - genomecov -tools: - - bedtools: - description: | - A set of tools for genomic analysis tasks, specifically enabling genome arithmetic (merge, count, complement) on various file types. - documentation: https://bedtools.readthedocs.io/en/latest/content/tools/genomecov.html - licence: ['MIT'] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - intervals: - type: file - description: BAM/BED/GFF/VCF - pattern: "*.{bam|bed|gff|vcf}" - - scale: - type: value - description: Number containing the scale factor for the output. Set to 1 to disable. Setting to a value other than 1 will also get the -bg bedgraph output format as this is required for this command switch - - sizes: - type: file - description: Tab-delimited table of chromosome names in the first column and chromosome sizes in the second column - - extension: - type: string - description: Extension of the output file (e. g., ".bg", ".bedgraph", ".txt", ".tab", etc.) It is set arbitrarily by the user and corresponds to the file format which depends on arguments. -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - genomecov: - type: file - description: Computed genome coverage file - pattern: "*.${extension}" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@Emiller88" - - "@sruthipsuresh" - - "@drpatelh" - - "@sidorov-si" - - "@chris-cheshire" diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf index c90f28fc..95481bd1 100644 --- a/subworkflows/local/make_consensus.nf +++ b/subworkflows/local/make_consensus.nf @@ -18,8 +18,9 @@ workflow MAKE_CONSENSUS { ch_versions = Channel.empty() MAKE_BED_MASK ( - bam_vcf, - fasta + bam_vcf.map { meta, bam, vcf, tbi -> [ meta, bam, vcf ] }, + fasta, + params.save_mpileup ) ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) From 369e2a25c9769753064d255a5a7e5afac63cb9a8 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 18 Jan 2022 22:21:10 +0000 Subject: [PATCH 093/238] Small updates --- conf/modules_nanopore.config | 6 +++--- docs/usage.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 39030207..2e31cb98 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -162,7 +162,7 @@ if (!params.skip_mosdepth) { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/genome" }, mode: 'copy', - pattern: "*.{summary.txt}" + pattern: "*.summary.txt" ] } @@ -180,7 +180,7 @@ if (!params.skip_mosdepth) { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/mosdepth/amplicon" }, mode: 'copy', - pattern: "*.{summary.txt}" + pattern: "*.summary.txt" ] } @@ -231,7 +231,7 @@ if (!params.skip_nextclade) { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/nextclade" }, mode: 'copy', - pattern: "*.{csv}" + pattern: "*.csv" ] } diff --git a/docs/usage.md b/docs/usage.md index f3c801cf..b7a1bba4 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -337,7 +337,7 @@ The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementatio You can use a similar approach to update the version of Nextclade used by the pipeline: -1. Check the default version used by the pipeline in the module file for [Nextclade](https://github.com/nf-core/viralrecon/blob/bc9ed7163796eed433580e3fa6d3421f65b939fe/modules/nf-core/modules/nextclade/main.nf#L5-L8) +1. Check the default version used by the pipeline in the module file for [Nextclade](https://github.com/nf-core/viralrecon/blob/e582db9c70721aae530703ec9a2ab8b219c96a99/modules/nf-core/modules/nextclade/run/main.nf#L5-L8) 2. Find the latest version of the Biocontainer available on [Quay.io](https://quay.io/repository/biocontainers/nextclade?tag=latest&tab=tags) 3. Create the custom config accordingly: From e192daa5e6c9093faeea07682c8890dad7ee9836 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 18 Jan 2022 22:40:16 +0000 Subject: [PATCH 094/238] Update nf-core modules and get tests passing --- CHANGELOG.md | 1 + conf/modules_illumina.config | 20 ++++++++++++++++--- modules.json | 8 ++++---- .../modules/bcftools/consensus/main.nf | 9 ++++++--- .../nf-core/modules/bcftools/mpileup/main.nf | 9 +++++++-- .../nf-core/modules/bcftools/mpileup/meta.yml | 4 ++++ .../nf-core/modules/ivar/consensus/main.nf | 15 ++++++++------ .../nf-core/modules/ivar/consensus/meta.yml | 4 ++++ modules/nf-core/modules/ivar/variants/main.nf | 12 ++++++----- .../nf-core/modules/ivar/variants/meta.yml | 1 + subworkflows/local/variants_bcftools.nf | 3 ++- subworkflows/local/variants_ivar.nf | 3 ++- 12 files changed, 64 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14a97409..f2c93952 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [[#218](https://github.com/nf-core/viralrecon/issues/218)] - Support for compressed FastQ files for Nanopore data * [[#232](https://github.com/nf-core/viralrecon/issues/232)] - Remove duplicate variants called by ARTIC ONT pipeline * [[#235](https://github.com/nf-core/viralrecon/issues/235)] - Nextclade version bump +* [[#244](https://github.com/nf-core/viralrecon/issues/244)] - Fix BCFtools consensus generation and masking ### Parameters diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 0915890e..de95711a 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -438,9 +438,17 @@ if (!params.skip_variants) { ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/ivar/consensus" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + [ + path: { "${params.outdir}/variants/ivar/consensus" }, + mode: 'copy', + pattern: "*.{fa,txt}", + ], + [ + path: { "${params.outdir}/variants/ivar/consensus" }, + mode: 'copy', + pattern: "*.mpileup", + enabled: params.save_mpileup + ] ] } @@ -563,6 +571,12 @@ if (!params.skip_variants) { mode: 'copy', pattern: '*.{gz,tbi}' ], + [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: '*.mpileup', + enabled: params.save_mpileup + ], [ path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, mode: 'copy', diff --git a/modules.json b/modules.json index 4922ac49..6aac9011 100644 --- a/modules.json +++ b/modules.json @@ -16,10 +16,10 @@ "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "bcftools/consensus": { - "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, "bcftools/mpileup": { - "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, "bcftools/stats": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" @@ -64,13 +64,13 @@ "git_sha": "20d8250d9f39ddb05dfb437603aaf99b5c0b2b41" }, "ivar/consensus": { - "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" + "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, "ivar/trim": { "git_sha": "9d0cad583b9a71a6509b754fdf589cbfbed08961" }, "ivar/variants": { - "git_sha": "b4c6b430d0ae4899551bb44dbb78d6270c31e0b2" + "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, "kraken2/kraken2": { "git_sha": "3f9a0285816a2e0ae73a3875fb5ae4b409da5952" diff --git a/modules/nf-core/modules/bcftools/consensus/main.nf b/modules/nf-core/modules/bcftools/consensus/main.nf index 5d7cd74f..040e6534 100644 --- a/modules/nf-core/modules/bcftools/consensus/main.nf +++ b/modules/nf-core/modules/bcftools/consensus/main.nf @@ -18,9 +18,12 @@ process BCFTOOLS_CONSENSUS { def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" """ - cat $fasta | bcftools consensus $vcf $args > ${prefix}.fa - header=\$(head -n 1 ${prefix}.fa | sed 's/>//g') - sed -i 's/\${header}/${meta.id}/g' ${prefix}.fa + cat $fasta \\ + | bcftools \\ + consensus \\ + $vcf \\ + $args \\ + > ${prefix}.fa cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/bcftools/mpileup/main.nf b/modules/nf-core/modules/bcftools/mpileup/main.nf index 3583aac4..cdd38eec 100644 --- a/modules/nf-core/modules/bcftools/mpileup/main.nf +++ b/modules/nf-core/modules/bcftools/mpileup/main.nf @@ -9,12 +9,14 @@ process BCFTOOLS_MPILEUP { input: tuple val(meta), path(bam) - path fasta + path fasta + val save_mpileup output: tuple val(meta), path("*.gz") , emit: vcf tuple val(meta), path("*.tbi") , emit: tbi tuple val(meta), path("*stats.txt"), emit: stats + tuple val(meta), path("*.mpileup") , emit: mpileup, optional: true path "versions.yml" , emit: versions script: @@ -22,13 +24,16 @@ process BCFTOOLS_MPILEUP { def args2 = task.ext.args2 ?: '' def args3 = task.ext.args3 ?: '' def prefix = task.ext.prefix ?: "${meta.id}" + def mpileup = save_mpileup ? "| tee ${prefix}.mpileup" : "" """ echo "${meta.id}" > sample_name.list - bcftools mpileup \\ + bcftools \\ + mpileup \\ --fasta-ref $fasta \\ $args \\ $bam \\ + $mpileup \\ | bcftools call --output-type v $args2 \\ | bcftools reheader --samples sample_name.list \\ | bcftools view --output-file ${prefix}.vcf.gz --output-type z $args3 diff --git a/modules/nf-core/modules/bcftools/mpileup/meta.yml b/modules/nf-core/modules/bcftools/mpileup/meta.yml index c31180ee..483d0e71 100644 --- a/modules/nf-core/modules/bcftools/mpileup/meta.yml +++ b/modules/nf-core/modules/bcftools/mpileup/meta.yml @@ -26,6 +26,10 @@ input: type: file description: FASTA reference file pattern: "*.{fasta,fa}" + - save_mpileup: + type: boolean + description: Save mpileup file generated by bcftools mpileup + patter: "*.mpileup" output: - meta: type: map diff --git a/modules/nf-core/modules/ivar/consensus/main.nf b/modules/nf-core/modules/ivar/consensus/main.nf index 58d97c8c..96d00ce2 100644 --- a/modules/nf-core/modules/ivar/consensus/main.nf +++ b/modules/nf-core/modules/ivar/consensus/main.nf @@ -9,7 +9,8 @@ process IVAR_CONSENSUS { input: tuple val(meta), path(bam) - path fasta + path fasta + val save_mpileup output: tuple val(meta), path("*.fa") , emit: fasta @@ -21,14 +22,16 @@ process IVAR_CONSENSUS { def args = task.ext.args ?: '' def args2 = task.ext.args2 ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - def save_mpileup = params.save_mpileup ? "tee ${prefix}.mpileup |" : "" + def mpileup = save_mpileup ? "| tee ${prefix}.mpileup" : "" """ - samtools mpileup \\ + samtools \\ + mpileup \\ --reference $fasta \\ $args2 \\ - $bam | \\ - $save_mpileup \\ - ivar consensus \\ + $bam \\ + $mpileup \\ + | ivar \\ + consensus \\ $args \\ -p $prefix diff --git a/modules/nf-core/modules/ivar/consensus/meta.yml b/modules/nf-core/modules/ivar/consensus/meta.yml index 2ee5f2c6..aa08ad98 100644 --- a/modules/nf-core/modules/ivar/consensus/meta.yml +++ b/modules/nf-core/modules/ivar/consensus/meta.yml @@ -25,6 +25,10 @@ input: type: file description: The reference sequence used for mapping and generating the BAM file pattern: "*.fa" + - save_mpileup: + type: boolean + description: Save mpileup file generated by ivar consensus + patter: "*.mpileup" output: - meta: type: map diff --git a/modules/nf-core/modules/ivar/variants/main.nf b/modules/nf-core/modules/ivar/variants/main.nf index fda6e0cc..bb6e402b 100644 --- a/modules/nf-core/modules/ivar/variants/main.nf +++ b/modules/nf-core/modules/ivar/variants/main.nf @@ -23,14 +23,16 @@ process IVAR_VARIANTS { def args2 = task.ext.args2 ?: '' def prefix = task.ext.prefix ?: "${meta.id}" def features = gff ? "-g $gff" : "" - def mpileup = save_mpileup ? "tee ${prefix}.mpileup |" : "" + def mpileup = save_mpileup ? "| tee ${prefix}.mpileup" : "" """ - samtools mpileup \\ + samtools \\ + mpileup \\ $args2 \\ --reference $fasta \\ - $bam | \\ - $mpileup \\ - ivar variants \\ + $bam \\ + $mpileup \\ + | ivar \\ + variants \\ $args \\ $features \\ -r $fasta \\ diff --git a/modules/nf-core/modules/ivar/variants/meta.yml b/modules/nf-core/modules/ivar/variants/meta.yml index 7c4297ca..29cbd958 100644 --- a/modules/nf-core/modules/ivar/variants/meta.yml +++ b/modules/nf-core/modules/ivar/variants/meta.yml @@ -32,6 +32,7 @@ input: - save_mpileup: type: boolean description: Save mpileup file generated by ivar variants + patter: "*.mpileup" output: - meta: type: map diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index a2a09308..501ecd53 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -31,7 +31,8 @@ workflow VARIANTS_BCFTOOLS { // BCFTOOLS_MPILEUP ( bam, - fasta + fasta, + params.save_mpileup ) ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index 152b7f3f..a21a2825 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -69,7 +69,8 @@ workflow VARIANTS_IVAR { if (!params.skip_consensus) { IVAR_CONSENSUS ( bam, - fasta + fasta, + params.save_mpileup ) ch_consensus = IVAR_CONSENSUS.out.fasta ch_consensus_qual = IVAR_CONSENSUS.out.qual From e1b4f11aaaeae9fb16756e16a5dfc85052335c23 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 09:48:46 +0000 Subject: [PATCH 095/238] Add changes to say what --genome includes --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 30b5f94d..4a532c67 100644 --- a/README.md +++ b/README.md @@ -141,16 +141,22 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and ./fastq_dir_to_samplesheet.py samplesheet.csv ``` - * You can find the default keys used to specify `--genome` in the [genomes config file](https://github.com/nf-core/configs/blob/master/conf/pipeline/viralrecon/genomes.config). Where possible we are trying to collate links and settings for standard primer sets to make it easier to run the pipeline with standard keys; see [usage docs](https://nf-co.re/viralrecon/usage#illumina-primer-sets). + * You can find the default keys used to specify `--genome` in the [genomes config file](https://github.com/nf-core/configs/blob/master/conf/pipeline/viralrecon/genomes.config). This provides default params for + * SARS-CoV-2 reference genomes + * primersets + * Nextclade datasets + + Where possible we are trying to collate links and settings for standard primer sets to make it easier to run the pipeline with standard keys; see [usage docs](https://nf-co.re/viralrecon/usage#illumina-primer-sets). ## Documentation The nf-core/viralrecon pipeline comes with documentation about the pipeline [usage](https://nf-co.re/viralrecon/usage), [parameters](https://nf-co.re/viralrecon/parameters) and [output](https://nf-co.re/viralrecon/output). --- + **Keeping the pipeline up to date** -___ +--- The Pangolin and Nextclade lineage and clade definitions change regularly as new SARS-CoV-2 lineages are discovered. For instructions to use more recent versions of lineage analysis tools like Pangolin and Nextclade please refer to the [updating containers](https://nf-co.re/viralrecon/usage#updating-containers) section in the usage docs. From ee026164a1e7fba9049bccdcbbd81119e1551b9f Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 09:51:31 +0000 Subject: [PATCH 096/238] Add tag explicitly --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 2e61b18b..b2b4d08e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -383,7 +383,7 @@ A [`nextclade dataset`](https://docs.nextstrain.org/projects/nextclade/en/latest * Let the pipeline create and use the latest version by setting: `--nextclade_dataset false --nextclade_dataset_tag false` * Let the pipeline create and use a specific, tagged version by setting: `--nextclade_dataset false --nextclade_dataset_tag ` -The Nextclade data releases can be found on their [Github page](https://github.com/nextstrain/nextclade_data/releases). Use the tag specified for each release e.g +The Nextclade data releases can be found on their [Github page](https://github.com/nextstrain/nextclade_data/releases). Use the tag specified for each release e.g `2022-01-05T19:54:31Z` in the example below. ![Nextclade tag example](images/nextclade_tag_example.png) From 8dd4afc2035fe250322cd9e46be04f2c01a1ebd9 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 09:54:25 +0000 Subject: [PATCH 097/238] Correct syntax to pass linting --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4a532c67..3ef84821 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and * SARS-CoV-2 reference genomes * primersets * Nextclade datasets - + Where possible we are trying to collate links and settings for standard primer sets to make it easier to run the pipeline with standard keys; see [usage docs](https://nf-co.re/viralrecon/usage#illumina-primer-sets). ## Documentation @@ -154,7 +154,7 @@ The nf-core/viralrecon pipeline comes with documentation about the pipeline [usa --- -**Keeping the pipeline up to date** +### Keeping the pipeline up to date --- From 7b6e0cf0a757e090f7325f8436b36e6d527db325 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 09:57:56 +0000 Subject: [PATCH 098/238] Correct linting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ef84821..528fd45a 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and * SARS-CoV-2 reference genomes * primersets * Nextclade datasets - + Where possible we are trying to collate links and settings for standard primer sets to make it easier to run the pipeline with standard keys; see [usage docs](https://nf-co.re/viralrecon/usage#illumina-primer-sets). ## Documentation From 36f981564bbb955b5c42ea81430ef053de66047e Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 09:59:30 +0000 Subject: [PATCH 099/238] Correct linting --- docs/usage.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index b2b4d08e..e7e5d430 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -302,6 +302,7 @@ process { The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. If for some reason you need to use a different version of a particular tool with the pipeline then you just need to identify the `process` name and override the Nextflow `container` definition for that process using the `withName` declaration. #### Pangolin + For example, in the [nf-core/viralrecon](https://nf-co.re/viralrecon) pipeline a tool called [Pangolin](https://github.com/cov-lineages/pangolin) has been used during the COVID-19 pandemic to assign lineages to SARS-CoV-2 genome sequenced samples. Given that the lineage assignments change quite frequently it doesn't make sense to re-release the nf-core/viralrecon everytime a new version of Pangolin has been released. However, you can override the default container used by the pipeline by creating a custom config file and passing it as a command-line argument via `-c custom.config`. 1. Check the default version used by the pipeline in the module file for [Pangolin](https://github.com/nf-core/viralrecon/blob/a85d5969f9025409e3618d6c280ef15ce417df65/modules/nf-core/software/pangolin/main.nf#L14-L19) @@ -339,6 +340,7 @@ For example, in the [nf-core/viralrecon](https://nf-co.re/viralrecon) pipeline a ``` #### Nextclade + You can use a similar approach to update the version of Nextclade used by the pipeline: 1. Check the default version used by the pipeline in the module file for [Nextclade](https://github.com/nf-core/viralrecon/blob/bc9ed7163796eed433580e3fa6d3421f65b939fe/modules/nf-core/modules/nextclade/main.nf#L5-L8) @@ -375,7 +377,7 @@ You can use a similar approach to update the version of Nextclade used by the pi } ``` -**Nextclade datasets** +##### Nextclade datasets A [`nextclade dataset`](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html#nextclade-datasets) feature was introduced in [Nextclade CLI v1.3.0](https://github.com/nextstrain/nextclade/releases/tag/1.3.0) that fetches input genome files such as reference sequences and trees from a central dataset repository. We have uploaded Nextclade dataset [v2022-01-05](https://github.com/nextstrain/nextclade_data/releases/tag/2022-01-06--15-17-13--UTC) to [nf-core/test-datasets](https://github.com/nf-core/test-datasets/blob/viralrecon/genome/MN908947.3/nextclade_sars-cov-2_MN908947_2022-01-05T19_54_31Z.tar.gz), and for reproducibility, this will be used by default if you specifiy `--genome 'MN908947.3'` when running the pipeline. However, there are a number of ways you can use a more recent version of the dataset: From 67f3e2b82906d787f4dc208f0b8c1ba280d016c9 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 12:26:23 +0000 Subject: [PATCH 100/238] Update README.md Co-authored-by: Harshil Patel --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 528fd45a..789e7c9e 100644 --- a/README.md +++ b/README.md @@ -142,9 +142,9 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and ``` * You can find the default keys used to specify `--genome` in the [genomes config file](https://github.com/nf-core/configs/blob/master/conf/pipeline/viralrecon/genomes.config). This provides default params for - * SARS-CoV-2 reference genomes - * primersets - * Nextclade datasets + * Reference genomes (including SARS-CoV-2) + * Genome associates primer sets + * [Nextclade datasets](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html) Where possible we are trying to collate links and settings for standard primer sets to make it easier to run the pipeline with standard keys; see [usage docs](https://nf-co.re/viralrecon/usage#illumina-primer-sets). From e741c992964a04d47741d768e8098be8ef68ad44 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 12:26:33 +0000 Subject: [PATCH 101/238] Update README.md Co-authored-by: Harshil Patel --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 789e7c9e..b6a7629b 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and ./fastq_dir_to_samplesheet.py samplesheet.csv ``` - * You can find the default keys used to specify `--genome` in the [genomes config file](https://github.com/nf-core/configs/blob/master/conf/pipeline/viralrecon/genomes.config). This provides default params for + * You can find the default keys used to specify `--genome` in the [genomes config file](https://github.com/nf-core/configs/blob/master/conf/pipeline/viralrecon/genomes.config). This provides default options for * Reference genomes (including SARS-CoV-2) * Genome associates primer sets * [Nextclade datasets](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html) From 490ba1da3dfc3f6b3f17fdaa77c3722827537547 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 12:26:50 +0000 Subject: [PATCH 102/238] Update README.md Co-authored-by: Harshil Patel --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b6a7629b..5252ddca 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,8 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and * Genome associates primer sets * [Nextclade datasets](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html) + The Pangolin and Nextclade lineage and clade definitions change regularly as new SARS-CoV-2 lineages are discovered. For instructions to use more recent versions of lineage analysis tools like Pangolin and Nextclade please refer to the [updating containers](https://nf-co.re/viralrecon/usage#updating-containers) section in the usage docs. + Where possible we are trying to collate links and settings for standard primer sets to make it easier to run the pipeline with standard keys; see [usage docs](https://nf-co.re/viralrecon/usage#illumina-primer-sets). ## Documentation From ab900e08e938105fe25fbc29f10674092e8160ed Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 12:27:05 +0000 Subject: [PATCH 103/238] Update README.md Co-authored-by: Harshil Patel --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index 5252ddca..b4a9c1a9 100644 --- a/README.md +++ b/README.md @@ -154,13 +154,6 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and The nf-core/viralrecon pipeline comes with documentation about the pipeline [usage](https://nf-co.re/viralrecon/usage), [parameters](https://nf-co.re/viralrecon/parameters) and [output](https://nf-co.re/viralrecon/output). ---- - -### Keeping the pipeline up to date - ---- - -The Pangolin and Nextclade lineage and clade definitions change regularly as new SARS-CoV-2 lineages are discovered. For instructions to use more recent versions of lineage analysis tools like Pangolin and Nextclade please refer to the [updating containers](https://nf-co.re/viralrecon/usage#updating-containers) section in the usage docs. ## Credits From 781287952620261a7bf57067375c9395f307b7b1 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 12:27:31 +0000 Subject: [PATCH 104/238] Update docs/usage.md Co-authored-by: Harshil Patel --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index e7e5d430..6473db84 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -379,7 +379,7 @@ You can use a similar approach to update the version of Nextclade used by the pi ##### Nextclade datasets -A [`nextclade dataset`](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html#nextclade-datasets) feature was introduced in [Nextclade CLI v1.3.0](https://github.com/nextstrain/nextclade/releases/tag/1.3.0) that fetches input genome files such as reference sequences and trees from a central dataset repository. We have uploaded Nextclade dataset [v2022-01-05](https://github.com/nextstrain/nextclade_data/releases/tag/2022-01-06--15-17-13--UTC) to [nf-core/test-datasets](https://github.com/nf-core/test-datasets/blob/viralrecon/genome/MN908947.3/nextclade_sars-cov-2_MN908947_2022-01-05T19_54_31Z.tar.gz), and for reproducibility, this will be used by default if you specifiy `--genome 'MN908947.3'` when running the pipeline. However, there are a number of ways you can use a more recent version of the dataset: +A [`nextclade dataset`](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html#nextclade-datasets) feature was introduced in [Nextclade CLI v1.3.0](https://github.com/nextstrain/nextclade/releases/tag/1.3.0) that fetches input genome files such as reference sequences and trees from a central dataset repository. We have uploaded Nextclade dataset [v2022-01-05](https://github.com/nextstrain/nextclade_data/releases/tag/2022-01-06--15-17-13--UTC) to [nf-core/test-datasets](https://github.com/nf-core/test-datasets/blob/viralrecon/genome/MN908947.3/nextclade_sars-cov-2_MN908947_2022-01-05T19_54_31Z.tar.gz), and for reproducibility, this will be used by default if you specify `--genome 'MN908947.3'` when running the pipeline. However, there are a number of ways you can use a more recent version of the dataset: * Supply your own by setting: `--nextclade_dataset ` * Let the pipeline create and use the latest version by setting: `--nextclade_dataset false --nextclade_dataset_tag false` From 4993d145958dc074c6899e3537065f60799b0601 Mon Sep 17 00:00:00 2001 From: Anthony Underwood Date: Wed, 19 Jan 2022 12:27:39 +0000 Subject: [PATCH 105/238] Update docs/usage.md Co-authored-by: Harshil Patel --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 6473db84..f6fd62e4 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -385,7 +385,7 @@ A [`nextclade dataset`](https://docs.nextstrain.org/projects/nextclade/en/latest * Let the pipeline create and use the latest version by setting: `--nextclade_dataset false --nextclade_dataset_tag false` * Let the pipeline create and use a specific, tagged version by setting: `--nextclade_dataset false --nextclade_dataset_tag ` -The Nextclade data releases can be found on their [Github page](https://github.com/nextstrain/nextclade_data/releases). Use the tag specified for each release e.g `2022-01-05T19:54:31Z` in the example below. +The Nextclade dataset releases can be found on their [Github page](https://github.com/nextstrain/nextclade_data/releases). Use the tag specified for each release e.g `2022-01-05T19:54:31Z` in the example below: ![Nextclade tag example](images/nextclade_tag_example.png) From 93f147a984dff40aab7003f1aad8a4cc2d9972f0 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 19 Jan 2022 13:03:01 +0000 Subject: [PATCH 106/238] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index b4a9c1a9..c4fa3a7e 100644 --- a/README.md +++ b/README.md @@ -147,14 +147,13 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and * [Nextclade datasets](https://docs.nextstrain.org/projects/nextclade/en/latest/user/datasets.html) The Pangolin and Nextclade lineage and clade definitions change regularly as new SARS-CoV-2 lineages are discovered. For instructions to use more recent versions of lineage analysis tools like Pangolin and Nextclade please refer to the [updating containers](https://nf-co.re/viralrecon/usage#updating-containers) section in the usage docs. - + Where possible we are trying to collate links and settings for standard primer sets to make it easier to run the pipeline with standard keys; see [usage docs](https://nf-co.re/viralrecon/usage#illumina-primer-sets). ## Documentation The nf-core/viralrecon pipeline comes with documentation about the pipeline [usage](https://nf-co.re/viralrecon/usage), [parameters](https://nf-co.re/viralrecon/parameters) and [output](https://nf-co.re/viralrecon/output). - ## Credits These scripts were originally written by [Sarai Varona](https://github.com/svarona), [Miguel Juliá](https://github.com/MiguelJulia) and [Sara Monzon](https://github.com/saramonzon) from [BU-ISCIII](https://github.com/BU-ISCIII) and co-ordinated by Isabel Cuesta for the [Institute of Health Carlos III](https://eng.isciii.es/eng.isciii.es/Paginas/Inicio.html), Spain. Through collaboration with the nf-core community the pipeline has now been updated substantially to include additional processing steps, to standardise inputs/outputs and to improve pipeline reporting; implemented and maintained primarily by Harshil Patel ([@drpatelh](https://github.com/drpatelh)) from [Seqera Labs, Spain](https://seqera.io/). From 7943dda8dc79db4931f7262a9d392eedbd7c4923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Thu, 20 Jan 2022 14:56:13 +0100 Subject: [PATCH 107/238] change param in ivar_variants_to_vcf to ignore_strand_bias --- bin/ivar_variants_to_vcf.py | 4 ++-- conf/modules_illumina.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 5a9e6b59..93ab6d52 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -33,7 +33,7 @@ def parse_args(args=None): ) parser.add_argument( "-nsb", - "--not_strand_bias", + "--ignore_strand_bias", dest="NOT_STRAND_BIAS", default=False, help="Does not take into account strand bias, use this option when not using amplicons for sequencing", @@ -41,7 +41,7 @@ def parse_args(args=None): ) parser.add_argument( "-nmc", - "--not_merge_codons", + "--ignore_merge_codons", dest="NOT_MERGE_CODONS", help="Only output variants without taking into accout if the positions are consecutive and belong to the same codon.", action="store_true" diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 7ef167a7..db244a7c 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -391,7 +391,7 @@ if (!params.skip_variants) { } if (params.protocol == 'amplicon') { withName: 'IVAR_VARIANTS_TO_VCF' { - ext.args = '--not_strand_bias' + ext.args = '--ignore_strand_bias' publishDir = [ path: { "${params.outdir}/variants/ivar/log" }, mode: 'copy', From 93e57e65e3013c4032e5774513b6d77ff8733d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Thu, 20 Jan 2022 16:13:43 +0100 Subject: [PATCH 108/238] Added some docs for ivar tsv to vcf script output --- docs/output.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index 17a90337..4ae2e65f 100644 --- a/docs/output.md +++ b/docs/output.md @@ -511,7 +511,7 @@ Unless you are using [UMIs](https://emea.illumina.com/science/sequencing-method- * `variants/ivar/` * `*.tsv`: Original iVar variants in TSV format. - * `*.vcf.gz`: iVar variants in VCF format. + * `*.vcf.gz`: iVar variants in VCF format. Converted using custom `ivar_variants_to_vcf.py` python script. * `*.vcf.gz.tbi`: iVar variants in VCF index file. * `variants/ivar/consensus/` * `*.consensus.fa`: Consensus Fasta file generated by iVar. @@ -531,6 +531,8 @@ Unless you are using [UMIs](https://emea.illumina.com/science/sequencing-method- [iVar](https://github.com/andersen-lab/ivar/blob/master/docs/MANUAL.md) is a computational package that contains functions broadly useful for viral amplicon-based sequencing. We use iVar in this pipeline to [trim primer sequences](#ivar-trim) for amplicon input data as well as to call variants and for consensus sequence generation. +iVar outputs a tsv format, which is not compatible with downstream analysis such as annotation using snpeff. Moreover some issues need to be addressed such as [strand-bias filtering](https://github.com/andersen-lab/ivar/issues/5) and [consecutive variants belonging to the same codon](https://github.com/andersen-lab/ivar/issues/92). Viralrecon uses a custom python script [ivar_variants_to_vcf.py](https://github.com/nf-core/viralrecon/blob/master/bin/ivar_variants_to_vcf.py) to convert ivar default output to vcf addressing both issues. + ![MultiQC - iVar variants called plot](images/mqc_ivar_variants_plot.png) ### BCFTools and BEDTools From f82671b5abe33e7233ce04baab2152d4a9078212 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 20 Jan 2022 17:59:31 +0100 Subject: [PATCH 109/238] Fixed insertion error when masking consensus --- bin/make_bed_mask.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/bin/make_bed_mask.py b/bin/make_bed_mask.py index 6f59033c..b042fba6 100755 --- a/bin/make_bed_mask.py +++ b/bin/make_bed_mask.py @@ -18,23 +18,6 @@ def parse_args(args=None): def find_indels_vcf(vcf_in): - encoding = "utf-8" - indels_pos_len = {} - with gzip.open(vcf_in, "r") as f: - for line in f: - if "#" not in str(line, encoding): - line = re.split("\t", str(line, encoding)) - var_pos = line[1] - ref = line[3] - alt = line[4] - if len(alt) > len(ref): - indels_pos_len[var_pos] = len(alt) - elif len(ref) > len(alt): - indels_pos_len[var_pos] = len(ref) - return indels_pos_len - - -def find_dels_vcf(vcf_in): encoding = "utf-8" dels_pos_len = {} with gzip.open(vcf_in, "r") as f: @@ -44,7 +27,7 @@ def find_dels_vcf(vcf_in): var_pos = line[1] ref = line[3] alt = line[4] - if len(ref) > len(alt): + if len(ref) != len(alt): dels_pos_len[var_pos] = len(ref) return dels_pos_len @@ -80,7 +63,7 @@ def make_bed_mask(bed_in, bed_out, indels_pos_len): def main(args=None): args = parse_args(args) - indels_pos_len = find_dels_vcf(args.VCF_IN) + indels_pos_len = find_indels_vcf(args.VCF_IN) make_bed_mask(args.BED_IN, args.BED_OUT, indels_pos_len) From 0472306ef084bf5139bf8fe95997e90b7e43004f Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 20 Jan 2022 18:18:13 +0100 Subject: [PATCH 110/238] Fixed variable names --- bin/make_bed_mask.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/make_bed_mask.py b/bin/make_bed_mask.py index b042fba6..46e06bed 100755 --- a/bin/make_bed_mask.py +++ b/bin/make_bed_mask.py @@ -19,7 +19,7 @@ def parse_args(args=None): def find_indels_vcf(vcf_in): encoding = "utf-8" - dels_pos_len = {} + indels_pos_len = {} with gzip.open(vcf_in, "r") as f: for line in f: if "#" not in str(line, encoding): @@ -28,8 +28,8 @@ def find_indels_vcf(vcf_in): ref = line[3] alt = line[4] if len(ref) != len(alt): - dels_pos_len[var_pos] = len(ref) - return dels_pos_len + indels_pos_len[var_pos] = len(ref) + return indels_pos_len def make_bed_mask(bed_in, bed_out, indels_pos_len): From e0476094ac6241381f43cbcc53ed650db8b38c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 21 Jan 2022 17:12:27 +0100 Subject: [PATCH 111/238] exec perm to parser_ivar_bcftools.py --- bin/parser_ivar_bcftools.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bin/parser_ivar_bcftools.py diff --git a/bin/parser_ivar_bcftools.py b/bin/parser_ivar_bcftools.py old mode 100644 new mode 100755 From 4edbb56b125dd2be5ba5f44751332f75a354ee3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 24 Jan 2022 11:09:10 +0100 Subject: [PATCH 112/238] fix spaces --- bin/parser_ivar_bcftools.py | 70 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/bin/parser_ivar_bcftools.py b/bin/parser_ivar_bcftools.py index f971c7e5..e0410a62 100755 --- a/bin/parser_ivar_bcftools.py +++ b/bin/parser_ivar_bcftools.py @@ -9,7 +9,7 @@ pd.set_option('display.max_columns', None) pd.set_option('display.max_rows', None) -#Current working directory +#Current working directory cwd = os.getcwd() os.chdir(cwd) @@ -17,8 +17,6 @@ sample_table_path = os.path.join(cwd, "SampleTables") os.mkdir(sample_table_path) - - def parser_args(args=None): Description = 'Create long/wide tables fo ivar/bcftools' Epilog = """Example usage: python parser_ivar_bcftools.py """ @@ -27,14 +25,14 @@ def parser_args(args=None): parser.add_argument('-snpsift', help="Input snpsift txt files path.",required=True) parser.add_argument('-pangolin', help="Input pangolin csv files path.",required=True) parser.add_argument('-software', help="Input bcftools of ivar.",required=True) - + return parser.parse_args(args) def create_long(in_table,snp_table,pango_table,counter,software): if not in_table: - intable_pathname = cwd + intable_pathname = cwd else: intable_pathname = os.path.join(cwd, in_table) os.chdir(intable_pathname) @@ -50,7 +48,7 @@ def create_long(in_table,snp_table,pango_table,counter,software): for file in glob.glob("*_norm.snpsift.txt"): snp_list.append(file) snp_list.sort() - + pango_pathname = os.path.join(cwd, pango_table) os.chdir(pango_pathname) pangolin_list = [] @@ -59,12 +57,12 @@ def create_long(in_table,snp_table,pango_table,counter,software): pangolin_list.sort() ### format of sample table - sample_table=pd.read_table(str(intable_pathname) + "/" + table_list[counter], header='infer') + sample_table=pd.read_table(str(intable_pathname) + "/" + table_list[counter], header='infer') sample_table = sample_table.dropna(how = 'all', axis =1) if software=='bcftools': sample_table.rename(columns={sample_table.columns[5]: "DP",sample_table.columns[6]: "AD"}, inplace=True) - + new_column = sample_table new_column[['REF_DP','ALT_DP']] = sample_table['AD'].str.split(',', expand=True) sample_table = pd.merge(sample_table,new_column,how = 'left') @@ -72,13 +70,13 @@ def create_long(in_table,snp_table,pango_table,counter,software): sample_table['AF']=sample_table['ALT_DP']/sample_table['DP'] sample_table['AF'] = sample_table['AF'].round(2) sample_table = sample_table.loc[:, ~sample_table.columns.str.contains('AD')] - + elif software=='ivar': sample_table.rename(columns={sample_table.columns[0]: "CHROM",sample_table.columns[1]: "POS",sample_table.columns[2]: "REF",sample_table.columns[3]: "ALT",sample_table.columns[4]: "FILTER",sample_table.columns[5]: "DP",sample_table.columns[6]: "REF_DP", sample_table.columns[7]: "ALT_DP"}, inplace=True) sample_table[["ALT_DP", "DP"]] = sample_table[["ALT_DP", "DP"]].apply(pd.to_numeric) sample_table['AF']=sample_table['ALT_DP']/sample_table['DP'] sample_table['AF'] = sample_table['AF'].round(2) - + ### format of snpsift table snpsift_table = pd.read_csv(str(snp_pathname) + '/' + snp_list[counter], sep="\t", header = "infer") snpsift_table = snpsift_table.loc[:, ~snpsift_table.columns.str.contains('^Unnamed')] @@ -87,20 +85,20 @@ def create_long(in_table,snp_table,pango_table,counter,software): for i in range(len(colnames_snpsift)): snpsift_table.rename(columns = {snpsift_table.columns[i]:colnames_snpsift[i]}, inplace = True) snpsift_table = snpsift_table.loc[:, ['CHROM','POS','REF','ALT','GENE','EFFECT','HGVS_C','HGVS_P']] - + snpsift_table_copy = snpsift_table.copy() for i in range(len(snpsift_table_copy)): for j in range(3,8): snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] - - - - #format of lineages + + + + #format of lineages pangolin_table = pd.read_csv(str(pango_pathname) + '/' + pangolin_list[counter], sep=",", header = "infer") lineages = pangolin_table.loc[:,['taxon','lineage']] - #table long one sample + #table long one sample tl_onesample = pd.DataFrame(data =sample_table) if software=='bcftools': tl_onesample["Sample"] = lineages.iloc[0,0] @@ -116,7 +114,7 @@ def create_long(in_table,snp_table,pango_table,counter,software): def mergetables(sample_intable,snp_intable,path,counter): if not path: - merge_pathname = cwd + merge_pathname = cwd else: merge_pathname = os.path.join(cwd, path) @@ -126,21 +124,21 @@ def mergetables(sample_intable,snp_intable,path,counter): table_list.append(file[0:6]) table_list.sort() - left = snp_intable + left = snp_intable right = sample_intable merged_table_long = pd.merge(left,right,how = 'outer') - + merged_table_long.to_csv(str(merge_pathname) + '/SampleTables/'+ table_list[counter] + '.csv', header='infer', index=None, sep=' ', mode='a') return(merged_table_long) def loop(path_in): - + if not path_in: - in_pathname = cwd + in_pathname = cwd else: in_pathname = os.path.join(cwd, path_in) - + os.chdir(in_pathname) table_list = [] for file in glob.glob("*.table"): @@ -148,23 +146,23 @@ def loop(path_in): table_list.sort() length = len(table_list) - + return length - + def concatenatetable(path_in_concat): if not path_in_concat: concat_pathname = os.path.join(cwd, 'SampleTables') else: concat_pathname = os.path.join(cwd,path_in_concat,'SampleTables') - - + + os.chdir(concat_pathname) extension = 'csv' all_filenames = [i for i in glob.glob('*.{}'.format(extension))] - + #combine all files in the list #combined_csv = pd.concat([pd.read_csv(f) for f in all_filenames ]) @@ -176,25 +174,25 @@ def concatenatetable(path_in_concat): merged_df.to_csv("final_long_table.csv", index=False, encoding='utf-8-sig') return merged_df - - + + def main(args=None): - + args = parser_args(args) - + length = loop(args.sample) - + for i in range(0,length): - + tl_onesample_out,snpsift_table_final,counter_it = create_long(args.sample,args.snpsift, args.pangolin,i,args.software) merged = mergetables(tl_onesample_out,snpsift_table_final,args.sample,counter_it) - + merged_df= concatenatetable(args.sample) - + if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file + sys.exit(main()) From 8f8e0d3d33280b7f7d91b7d5372999020bf9dfa9 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 22:06:14 +0000 Subject: [PATCH 113/238] Remove bcftools isec --- README.md | 1 - docs/output.md | 18 ------------------ modules/local/bcftools_isec.nf | 31 ------------------------------- 3 files changed, 50 deletions(-) delete mode 100644 modules/local/bcftools_isec.nf diff --git a/README.md b/README.md index c4fa3a7e..950fa1a0 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,6 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and * Lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)) * Clade assignment, mutation calling and sequence quality checks ([`Nextclade`](https://github.com/nextstrain/nextclade)) * Individual variant screenshots with annotation tracks ([`ASCIIGenome`](https://asciigenome.readthedocs.io/en/latest/)) - 8. Intersect variants across callers ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html)) 6. _De novo_ assembly 1. Primer trimming ([`Cutadapt`](https://cutadapt.readthedocs.io/en/stable/guide.html); *amplicon data only*) 2. Choice of multiple assembly tools ([`SPAdes`](http://cab.spbu.ru/software/spades/) *||* [`Unicycler`](https://github.com/rrwick/Unicycler) *||* [`minia`](https://github.com/GATB/minia)) diff --git a/docs/output.md b/docs/output.md index 4ae2e65f..a2bfa18a 100644 --- a/docs/output.md +++ b/docs/output.md @@ -301,7 +301,6 @@ An example MultiQC report generated from a full-sized dataset can be viewed on t * [Pangolin](#pangolin) - Lineage analysis * [Nextclade](#nextclade) - Clade assignment, mutation calling and sequence quality checks * [ASCIIGenome](#asciigenome) - Individual variant screenshots with annotation tracks - * [BCFTools isec](#bcftools-isec) - Intersect variants across all callers * [De novo assembly](#illumina-de-novo-assembly) * [Cutadapt](#cutadapt) - Primer trimming for amplicon data * [SPAdes](#spades) *||* [Unicycler](#unicycler) *||* [minia](#minia) - Viral genome assembly @@ -647,23 +646,6 @@ As described in the documentation, [ASCIIGenome](https://asciigenome.readthedocs

ASCIIGenome screenshot

-### BCFTools isec - -
-Output files - -* `variants/intersect//` - * `*.vcf.gz`: VCF file containing variants common to both variant callers. There will be one file for each caller - see `README.txt` for details. - * `*.vcf.gz.tbi`: Index for VCF file. - * `README.txt`: File containing command used and file name mappings. - * `sites.txt`: List of variants common to both variant callers in textual format. The last column indicates presence (1) or absence (0) amongst the 2 different callers. - -**NB:** This process will only be executed when both variant callers are specified to be run i.e. `--callers ivar,bcftools`. - -
- -[BCFTools isec](http://samtools.github.io/bcftools/bcftools.html#isec) can be used to intersect the variant calls generated by the 2 different callers used in the pipeline. This permits a quick assessment of how consistently a particular variant is being called using different algorithms and to prioritise the investigation of the variants. - ## Illumina: De novo assembly A file called `summary_assembly_metrics_mqc.csv` containing a selection of read alignment and *de novo* assembly related metrics will be saved in the `multiqc/` results directory. The same metrics will also be added to the top of the MultiQC report. diff --git a/modules/local/bcftools_isec.nf b/modules/local/bcftools_isec.nf deleted file mode 100644 index a9ce5ea8..00000000 --- a/modules/local/bcftools_isec.nf +++ /dev/null @@ -1,31 +0,0 @@ -process BCFTOOLS_ISEC { - tag "$meta.id" - label 'process_low' - - conda (params.enable_conda ? 'bioconda::bcftools=1.14' : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0' : - 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" - - input: - tuple val(meta), path('ivar/*'), path('ivar/*'), path('bcftools/*'), path('bcftools/*') - - output: - tuple val(meta), path("${prefix}"), emit: results - path "versions.yml" , emit: versions - - script: - def args = task.ext.args ?: '' - prefix = task.ext.prefix ?: "${meta.id}" - """ - bcftools isec \\ - $args \\ - -p $prefix \\ - */*.vcf.gz - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') - END_VERSIONS - """ -} From 6179db00acec555aa6203298509ad8cdc7c9084e Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 23:17:46 +0000 Subject: [PATCH 114/238] Add new params --- CHANGELOG.md | 3 +++ conf/modules_illumina.config | 13 ------------- nextflow.config | 1 + 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c93952..53028e10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | | `--nextclade_dataset_name` | | | `--nextclade_dataset_reference` | | | `--nextclade_dataset_tag` | +| | `--skip_consensus_plots` | +| `--callers` | `--variant_caller` | +| | `--consensus_caller` | > **NB:** Parameter has been __updated__ if both old and new parameter information is present. > **NB:** Parameter has been __added__ if just the new parameter information is present. diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index db244a7c..af334abf 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -748,19 +748,6 @@ if (!params.skip_variants) { } } } - - if (callers.size() > 1) { - process { - withName: 'BCFTOOLS_ISEC' { - ext.args = '--nfiles +2 --output-type z' - publishDir = [ - path: { "${params.outdir}/variants/intersect" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } } if (!params.skip_assembly) { diff --git a/nextflow.config b/nextflow.config index 1f0470a3..9f3027db 100644 --- a/nextflow.config +++ b/nextflow.config @@ -71,6 +71,7 @@ params { skip_ivar_trim = false skip_markduplicates = true skip_picard_metrics = false + skip_consensus_plots = false skip_consensus = false skip_variants = false From 317bda1ea11fe5bbb166a12501f364202564d441 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 23:18:12 +0000 Subject: [PATCH 115/238] Add subworkflow for consensus calling with BCFTools --- subworkflows/local/consensus_bcftools.nf | 84 ++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 subworkflows/local/consensus_bcftools.nf diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf new file mode 100644 index 00000000..14cefb2e --- /dev/null +++ b/subworkflows/local/consensus_bcftools.nf @@ -0,0 +1,84 @@ +// +// Consensus calling with BCFTools and downstream processing QC +// + +include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' +include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' +include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' +include { MAKE_BED_MASK } from '../../modules/local/make_bed_mask' +include { CONSENSUS_QC } from './consensus_qc' + +workflow CONSENSUS_BCFTOOLS { + take: + bam // channel: [ val(meta), [ bam ] ] + vcf // channel: [ val(meta), [ vcf ] ] + tbi // channel: [ val(meta), [ tbi ] ] + fasta // channel: /path/to/genome.fasta + gff // channel: /path/to/genome.gff + nextclade_db // channel: /path/to/nextclade_db/ + + main: + + ch_versions = Channel.empty() + + // + // Create BED file with consensus regions to mask + // + MAKE_BED_MASK ( + bam.join(vcf, by: [0]), + fasta, + params.save_mpileup + ) + ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) + + // + // Merge intervals with BEDTools + // + BEDTOOLS_MERGE ( + MAKE_BED_MASK.out.bed + ) + ch_versions = ch_versions.mix(BEDTOOLS_MERGE.out.versions.first()) + + // + // Mask regions in consensus with BEDTools + // + BEDTOOLS_MASKFASTA ( + BEDTOOLS_MERGE.out.bed, + fasta + ) + ch_versions = ch_versions.mix(BEDTOOLS_MASKFASTA.out.versions.first()) + + // + // Call consensus sequence with BCFTools + // + BCFTOOLS_CONSENSUS ( + vcf.join(tbi, by: [0]).join(BEDTOOLS_MASKFASTA.out.fasta, by: [0]) + ) + ch_versions = ch_versions.mix(BCFTOOLS_CONSENSUS.out.versions.first()) + + // + // Consensus sequence QC + // + CONSENSUS_QC ( + BCFTOOLS_CONSENSUS.out.fasta, + fasta, + gff, + nextclade_db + ) + ch_versions = ch_versions.mix(CONSENSUS_QC.out.versions.first()) + + emit: + consensus = BCFTOOLS_CONSENSUS.out.fasta // channel: [ val(meta), [ fasta ] ] + + quast_results = CONSENSUS_QC.out.quast_results // channel: [ val(meta), [ results ] ] + quast_tsv = CONSENSUS_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] + + pangolin_report = CONSENSUS_QC.out.pangolin_report // channel: [ val(meta), [ csv ] ] + + nextclade_report = CONSENSUS_QC.out.nextclade_report // channel: [ val(meta), [ csv ] ] + + bases_tsv = CONSENSUS_QC.out.bases_tsv // channel: [ val(meta), [ tsv ] ] + bases_pdf = CONSENSUS_QC.out.bases_pdf // channel: [ val(meta), [ pdf ] ] + + versions = ch_versions // channel: [ versions.yml ] +} From 332e12d40d4291b51f0d188aa9ad05617d415b6b Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 23:18:25 +0000 Subject: [PATCH 116/238] Add subworkflow for consensus calling with iVar --- subworkflows/local/consensus_ivar.nf | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 subworkflows/local/consensus_ivar.nf diff --git a/subworkflows/local/consensus_ivar.nf b/subworkflows/local/consensus_ivar.nf new file mode 100644 index 00000000..b3de4d94 --- /dev/null +++ b/subworkflows/local/consensus_ivar.nf @@ -0,0 +1,55 @@ +// +// Consensus calling with iVar and downstream processing QC +// + +include { IVAR_CONSENSUS } from '../../modules/nf-core/modules/ivar/consensus/main' +include { CONSENSUS_QC } from './consensus_qc' + +workflow CONSENSUS_IVAR { + take: + bam // channel: [ val(meta), [ bam ] ] + fasta // channel: /path/to/genome.fasta + gff // channel: /path/to/genome.gff + nextclade_db // channel: /path/to/nextclade_db/ + + main: + + ch_versions = Channel.empty() + + // + // Call consensus sequence with iVar + // + IVAR_CONSENSUS ( + bam, + fasta, + params.save_mpileup + ) + ch_versions = ch_versions.mix(IVAR_CONSENSUS.out.versions.first()) + + // + // Consensus sequence QC + // + CONSENSUS_QC ( + IVAR_CONSENSUS.out.fasta, + fasta, + gff, + nextclade_db + ) + ch_versions = ch_versions.mix(CONSENSUS_QC.out.versions.first()) + + emit: + consensus = IVAR_CONSENSUS.out.fasta // channel: [ val(meta), [ fasta ] ] + consensus_qual = IVAR_CONSENSUS.out.qual // channel: [ val(meta), [ qual.txt ] ] + + quast_results = CONSENSUS_QC.out.quast_results // channel: [ val(meta), [ results ] ] + quast_tsv = CONSENSUS_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] + + pangolin_report = CONSENSUS_QC.out.pangolin_report // channel: [ val(meta), [ csv ] ] + + nextclade_report = CONSENSUS_QC.out.nextclade_report // channel: [ val(meta), [ csv ] ] + + bases_tsv = CONSENSUS_QC.out.bases_tsv // channel: [ val(meta), [ tsv ] ] + bases_pdf = CONSENSUS_QC.out.bases_pdf // channel: [ val(meta), [ pdf ] ] + + versions = ch_versions // channel: [ versions.yml ] +} \ No newline at end of file From 1fba0cc52244c9468f4b3f3e377f1afd1e3b1b6f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 23:18:40 +0000 Subject: [PATCH 117/238] Add Consensus QC sub-workflow --- subworkflows/local/consensus_qc.nf | 90 ++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 subworkflows/local/consensus_qc.nf diff --git a/subworkflows/local/consensus_qc.nf b/subworkflows/local/consensus_qc.nf new file mode 100644 index 00000000..d370f68b --- /dev/null +++ b/subworkflows/local/consensus_qc.nf @@ -0,0 +1,90 @@ +// +// Consensus calling QC +// + +include { QUAST } from '../../modules/nf-core/modules/quast/main' +include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' +include { NEXTCLADE_RUN } from '../../modules/nf-core/modules/nextclade/run/main' +include { PLOT_BASE_DENSITY } from '../../modules/local/plot_base_density' + +workflow CONSENSUS_QC { + take: + consensus // channel: [ val(meta), [ consensus ] ] + fasta // channel: /path/to/genome.fasta + gff // channel: /path/to/genome.gff + nextclade_db // channel: /path/to/nextclade_db/ + + main: + + ch_versions = Channel.empty() + + // + // Consensus QC report across samples with QUAST + // + ch_quast_results = Channel.empty() + ch_quast_tsv = Channel.empty() + if (!params.skip_variants_quast) { + QUAST ( + consensus.collect{ it[1] }, + fasta, + gff, + true, + params.gff + ) + ch_quast_results = QUAST.out.results + ch_quast_tsv = QUAST.out.tsv + ch_versions = ch_versions.mix(QUAST.out.versions) + } + + // + // Lineage analysis with Pangolin + // + ch_pangolin_report = Channel.empty() + if (!params.skip_pangolin) { + PANGOLIN ( + consensus + ) + ch_pangolin_report = PANGOLIN.out.report + ch_versions = ch_versions.mix(PANGOLIN.out.versions.first()) + } + + // + // Lineage analysis with Nextclade + // + ch_nextclade_report = Channel.empty() + if (!params.skip_nextclade) { + NEXTCLADE_RUN ( + consensus, + nextclade_db + ) + ch_nextclade_report = NEXTCLADE_RUN.out.csv + ch_versions = ch_versions.mix(NEXTCLADE_RUN.out.versions.first()) + } + + // + // Plot consensus base density + // + ch_bases_tsv = Channel.empty() + ch_bases_pdf = Channel.empty() + if (!params.skip_consensus_plots) { + PLOT_BASE_DENSITY ( + consensus + ) + ch_bases_tsv = PLOT_BASE_DENSITY.out.tsv + ch_bases_pdf = PLOT_BASE_DENSITY.out.pdf + ch_versions = ch_versions.mix(PLOT_BASE_DENSITY.out.versions.first()) + } + + emit: + quast_results = ch_quast_results // channel: [ val(meta), [ results ] ] + quast_tsv = ch_quast_tsv // channel: [ val(meta), [ tsv ] ] + + pangolin_report = ch_pangolin_report // channel: [ val(meta), [ csv ] ] + + nextclade_report = ch_nextclade_report // channel: [ val(meta), [ csv ] ] + + bases_tsv = ch_bases_tsv // channel: [ val(meta), [ tsv ] ] + bases_pdf = ch_bases_pdf // channel: [ val(meta), [ pdf ] ] + + versions = ch_versions // channel: [ versions.yml ] +} \ No newline at end of file From 6f410c0ff6043bcb5d8d2749a077489b1cd81154 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 23:18:58 +0000 Subject: [PATCH 118/238] Add variants QC sub-workflow --- subworkflows/local/variants_qc.nf | 91 +++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 subworkflows/local/variants_qc.nf diff --git a/subworkflows/local/variants_qc.nf b/subworkflows/local/variants_qc.nf new file mode 100644 index 00000000..cded165c --- /dev/null +++ b/subworkflows/local/variants_qc.nf @@ -0,0 +1,91 @@ +// +// Variant calling QC +// + +include { ASCIIGENOME } from '../../modules/local/asciigenome' +include { SNPEFF_SNPSIFT } from './snpeff_snpsift' + +workflow VARIANTS_QC { + take: + bam // channel: [ val(meta), [ bam ] ] + vcf // channel: [ val(meta), [ vcf ] ] + stats // channel: [ val(meta), [ bcftools_stats ] ] + fasta // channel: /path/to/genome.fasta + sizes // channel: /path/to/genome.sizes + gff // channel: /path/to/genome.gff + bed // channel: /path/to/primers.bed + snpeff_db // channel: /path/to/snpeff_db/ + snpeff_config // channel: /path/to/snpeff.config + + main: + + ch_versions = Channel.empty() + + // + // Annotate variants + // + ch_snpeff_vcf = Channel.empty() + ch_snpeff_tbi = Channel.empty() + ch_snpeff_stats = Channel.empty() + ch_snpeff_csv = Channel.empty() + ch_snpeff_txt = Channel.empty() + ch_snpeff_html = Channel.empty() + ch_snpsift_txt = Channel.empty() + if (params.gff && !params.skip_snpeff) { + SNPEFF_SNPSIFT ( + vcf, + snpeff_db, + snpeff_config, + fasta + ) + ch_snpeff_vcf = SNPEFF_SNPSIFT.out.vcf + ch_snpeff_tbi = SNPEFF_SNPSIFT.out.tbi + ch_snpeff_stats = SNPEFF_SNPSIFT.out.stats + ch_snpeff_csv = SNPEFF_SNPSIFT.out.csv + ch_snpeff_txt = SNPEFF_SNPSIFT.out.txt + ch_snpeff_html = SNPEFF_SNPSIFT.out.html + ch_snpsift_txt = SNPEFF_SNPSIFT.out.snpsift_txt + ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) + } + + // + // Variant screenshots with ASCIIGenome + // + ch_asciigenome_pdf = Channel.empty() + if (!params.skip_asciigenome) { + bam + .join(vcf, by: [0]) + .join(stats, by: [0]) + .map { meta, bam, vcf, stats -> + if (WorkflowCommons.getNumVariantsFromBCFToolsStats(stats) > 0) { + return [ meta, bam, vcf ] + } + } + .set { ch_asciigenome } + + ASCIIGENOME ( + ch_asciigenome, + fasta, + sizes, + gff, + bed, + params.asciigenome_window_size, + params.asciigenome_read_depth + ) + ch_asciigenome_pdf = ASCIIGENOME.out.pdf + ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first()) + } + + emit: + snpeff_vcf = ch_snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] + snpeff_tbi = ch_snpeff_tbi // channel: [ val(meta), [ tbi ] ] + snpeff_stats = ch_snpeff_stats // channel: [ val(meta), [ txt ] ] + snpeff_csv = ch_snpeff_csv // channel: [ val(meta), [ csv ] ] + snpeff_txt = ch_snpeff_txt // channel: [ val(meta), [ txt ] ] + snpeff_html = ch_snpeff_html // channel: [ val(meta), [ html ] ] + snpsift_txt = ch_snpsift_txt // channel: [ val(meta), [ txt ] ] + + asciigenome_pdf = ch_asciigenome_pdf // channel: [ val(meta), [ pdf ] ] + + versions = ch_versions // channel: [ versions.yml ] +} From 201d43efa80d814196537fdd240c2b2d4ea1d70d Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 23:19:29 +0000 Subject: [PATCH 119/238] Add subworkflow for variant calling with BCFTools --- subworkflows/local/make_consensus.nf | 54 -------- subworkflows/local/variants_bcftools.nf | 162 ++++-------------------- 2 files changed, 27 insertions(+), 189 deletions(-) delete mode 100644 subworkflows/local/make_consensus.nf diff --git a/subworkflows/local/make_consensus.nf b/subworkflows/local/make_consensus.nf deleted file mode 100644 index 95481bd1..00000000 --- a/subworkflows/local/make_consensus.nf +++ /dev/null @@ -1,54 +0,0 @@ -// -// Run various tools to generate a masked genome consensus sequence -// - -include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' -include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' -include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' -include { MAKE_BED_MASK } from '../../modules/local/make_bed_mask' -include { PLOT_BASE_DENSITY } from '../../modules/local/plot_base_density' - -workflow MAKE_CONSENSUS { - take: - bam_vcf // channel: [ val(meta), [ bam ], [ vcf ], [ tbi ] ] - fasta - - main: - - ch_versions = Channel.empty() - - MAKE_BED_MASK ( - bam_vcf.map { meta, bam, vcf, tbi -> [ meta, bam, vcf ] }, - fasta, - params.save_mpileup - ) - ch_versions = ch_versions.mix(MAKE_BED_MASK.out.versions.first()) - - BEDTOOLS_MERGE ( - MAKE_BED_MASK.out.bed - ) - ch_versions = ch_versions.mix(BEDTOOLS_MERGE.out.versions.first()) - - BEDTOOLS_MASKFASTA ( - BEDTOOLS_MERGE.out.bed, - fasta - ) - ch_versions = ch_versions.mix(BEDTOOLS_MASKFASTA.out.versions.first()) - - BCFTOOLS_CONSENSUS ( - bam_vcf.map { meta, bam, vcf, tbi -> [ meta, vcf, tbi ] }.join( BEDTOOLS_MASKFASTA.out.fasta, by: [0] ) - ) - ch_versions = ch_versions.mix(BCFTOOLS_CONSENSUS.out.versions.first()) - - PLOT_BASE_DENSITY ( - BCFTOOLS_CONSENSUS.out.fasta - ) - ch_versions = ch_versions.mix(PLOT_BASE_DENSITY.out.versions.first()) - - emit: - fasta = BCFTOOLS_CONSENSUS.out.fasta // channel: [ val(meta), [ fasta ] ] - tsv = PLOT_BASE_DENSITY.out.tsv // channel: [ val(meta), [ tsv ] ] - pdf = PLOT_BASE_DENSITY.out.pdf // channel: [ val(meta), [ pdf ] ] - - versions = ch_versions // channel: [ versions.yml ] -} diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index 501ecd53..100d25dc 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -1,15 +1,9 @@ // -// Variant calling and downstream processing for BCFTools +// Variant calling with BCFTools, downstream processing and QC // include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' -include { QUAST } from '../../modules/nf-core/modules/quast/main' -include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' -include { NEXTCLADE_RUN } from '../../modules/nf-core/modules/nextclade/run/main' -include { ASCIIGENOME } from '../../modules/local/asciigenome' - -include { MAKE_CONSENSUS } from './make_consensus' -include { SNPEFF_SNPSIFT } from './snpeff_snpsift' +include { VARIANTS_QC } from './variants_qc' workflow VARIANTS_BCFTOOLS { take: @@ -18,7 +12,6 @@ workflow VARIANTS_BCFTOOLS { sizes // channel: /path/to/genome.sizes gff // channel: /path/to/genome.gff bed // channel: /path/to/primers.bed - nextclade_db // channel: /path/to/nextclade_db/ snpeff_db // channel: /path/to/snpeff_db/ snpeff_config // channel: /path/to/snpeff.config @@ -37,136 +30,35 @@ workflow VARIANTS_BCFTOOLS { ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) // - // Create genome consensus using variants in VCF, run QUAST and pangolin - // - ch_consensus = Channel.empty() - ch_bases_tsv = Channel.empty() - ch_bases_pdf = Channel.empty() - ch_quast_results = Channel.empty() - ch_quast_tsv = Channel.empty() - ch_pangolin_report = Channel.empty() - ch_nextclade_report = Channel.empty() - if (!params.skip_consensus) { - MAKE_CONSENSUS ( - bam.join(BCFTOOLS_MPILEUP.out.vcf, by: [0]).join(BCFTOOLS_MPILEUP.out.tbi, by: [0]), - fasta - ) - ch_consensus = MAKE_CONSENSUS.out.fasta - ch_bases_tsv = MAKE_CONSENSUS.out.tsv - ch_bases_pdf = MAKE_CONSENSUS.out.pdf - ch_versions = ch_versions.mix(MAKE_CONSENSUS.out.versions) - - if (!params.skip_variants_quast) { - QUAST ( - ch_consensus.collect{ it[1] }, - fasta, - gff, - true, - params.gff - ) - ch_quast_results = QUAST.out.results - ch_quast_tsv = QUAST.out.tsv - ch_versions = ch_versions.mix(QUAST.out.versions) - } - - if (!params.skip_pangolin) { - PANGOLIN ( - ch_consensus - ) - ch_pangolin_report = PANGOLIN.out.report - ch_versions = ch_versions.mix(PANGOLIN.out.versions.first()) - } - - if (!params.skip_nextclade) { - NEXTCLADE_RUN ( - ch_consensus, - nextclade_db - ) - ch_nextclade_report = NEXTCLADE_RUN.out.csv - ch_versions = ch_versions.mix(NEXTCLADE_RUN.out.versions.first()) - } - } - - // - // Annotate variants - // - ch_snpeff_vcf = Channel.empty() - ch_snpeff_tbi = Channel.empty() - ch_snpeff_stats = Channel.empty() - ch_snpeff_csv = Channel.empty() - ch_snpeff_txt = Channel.empty() - ch_snpeff_html = Channel.empty() - ch_snpsift_txt = Channel.empty() - if (params.gff && !params.skip_snpeff) { - SNPEFF_SNPSIFT ( - BCFTOOLS_MPILEUP.out.vcf, - snpeff_db, - snpeff_config, - fasta - ) - ch_snpeff_vcf = SNPEFF_SNPSIFT.out.vcf - ch_snpeff_tbi = SNPEFF_SNPSIFT.out.tbi - ch_snpeff_stats = SNPEFF_SNPSIFT.out.stats - ch_snpeff_csv = SNPEFF_SNPSIFT.out.csv - ch_snpeff_txt = SNPEFF_SNPSIFT.out.txt - ch_snpeff_html = SNPEFF_SNPSIFT.out.html - ch_snpsift_txt = SNPEFF_SNPSIFT.out.snpsift_txt - ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) - } - - // - // Variant screenshots with ASCIIGenome + // Run downstream tools for variants QC // - ch_asciigenome_pdf = Channel.empty() - if (!params.skip_asciigenome) { - bam - .join(BCFTOOLS_MPILEUP.out.vcf, by: [0]) - .join(BCFTOOLS_MPILEUP.out.stats, by: [0]) - .map { meta, bam, vcf, stats -> - if (WorkflowCommons.getNumVariantsFromBCFToolsStats(stats) > 0) { - return [ meta, bam, vcf ] - } - } - .set { ch_asciigenome } - - ASCIIGENOME ( - ch_asciigenome, - fasta, - sizes, - gff, - bed, - params.asciigenome_window_size, - params.asciigenome_read_depth - ) - ch_asciigenome_pdf = ASCIIGENOME.out.pdf - ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first()) - } + VARIANTS_QC ( + bam, + BCFTOOLS_MPILEUP.out.vcf, + BCFTOOLS_MPILEUP.out.stats, + fasta, + sizes, + gff, + bed, + snpeff_db, + snpeff_config + ) + ch_versions = ch_versions.mix(VARIANTS_QC.out.versions) emit: - vcf = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] - stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] - - consensus = ch_consensus // channel: [ val(meta), [ fasta ] ] - bases_tsv = ch_bases_tsv // channel: [ val(meta), [ tsv ] ] - bases_pdf = ch_bases_pdf // channel: [ val(meta), [ pdf ] ] - - quast_results = ch_quast_results // channel: [ val(meta), [ results ] ] - quast_tsv = ch_quast_tsv // channel: [ val(meta), [ tsv ] ] - - snpeff_vcf = ch_snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] - snpeff_tbi = ch_snpeff_tbi // channel: [ val(meta), [ tbi ] ] - snpeff_stats = ch_snpeff_stats // channel: [ val(meta), [ txt ] ] - snpeff_csv = ch_snpeff_csv // channel: [ val(meta), [ csv ] ] - snpeff_txt = ch_snpeff_txt // channel: [ val(meta), [ txt ] ] - snpeff_html = ch_snpeff_html // channel: [ val(meta), [ html ] ] - snpsift_txt = ch_snpsift_txt // channel: [ val(meta), [ txt ] ] - - pangolin_report = ch_pangolin_report // channel: [ val(meta), [ csv ] ] + vcf = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] - nextclade_report = ch_nextclade_report // channel: [ val(meta), [ csv ] ] + snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] + snpeff_tbi = VARIANTS_QC.out.snpeff_tbi // channel: [ val(meta), [ tbi ] ] + snpeff_stats = VARIANTS_QC.out.snpeff_stats // channel: [ val(meta), [ txt ] ] + snpeff_csv = VARIANTS_QC.out.snpeff_csv // channel: [ val(meta), [ csv ] ] + snpeff_txt = VARIANTS_QC.out.snpeff_txt // channel: [ val(meta), [ txt ] ] + snpeff_html = VARIANTS_QC.out.snpeff_html // channel: [ val(meta), [ html ] ] + snpsift_txt = VARIANTS_QC.out.snpsift_txt // channel: [ val(meta), [ txt ] ] - asciigenome_pdf = ch_asciigenome_pdf // channel: [ val(meta), [ pdf ] ] + asciigenome_pdf = VARIANTS_QC.out.asciigenome_pdf // channel: [ val(meta), [ pdf ] ] - versions = ch_versions // channel: [ versions.yml ] + versions = ch_versions // channel: [ versions.yml ] } From b7ad0c4578b280c18c303ff133f978eff22b5117 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 23:19:37 +0000 Subject: [PATCH 120/238] Add subworkflow for variant calling with iVar --- subworkflows/local/variants_ivar.nf | 184 +++++----------------------- 1 file changed, 33 insertions(+), 151 deletions(-) diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index a21a2825..15a10e4e 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -1,18 +1,11 @@ // -// Variant calling and downstream processing for IVar +// Variant calling with IVar, downstream processing and QC // -include { IVAR_VARIANTS_TO_VCF } from '../../modules/local/ivar_variants_to_vcf' -include { PLOT_BASE_DENSITY } from '../../modules/local/plot_base_density' include { IVAR_VARIANTS } from '../../modules/nf-core/modules/ivar/variants/main' -include { IVAR_CONSENSUS } from '../../modules/nf-core/modules/ivar/consensus/main' -include { QUAST } from '../../modules/nf-core/modules/quast/main' -include { PANGOLIN } from '../../modules/nf-core/modules/pangolin/main' -include { NEXTCLADE_RUN } from '../../modules/nf-core/modules/nextclade/run/main' -include { ASCIIGENOME } from '../../modules/local/asciigenome' - +include { IVAR_VARIANTS_TO_VCF } from '../../modules/local/ivar_variants_to_vcf' include { VCF_BGZIP_TABIX_STATS } from '../nf-core/vcf_bgzip_tabix_stats' -include { SNPEFF_SNPSIFT } from './snpeff_snpsift' +include { VARIANTS_QC } from './variants_qc' workflow VARIANTS_IVAR { take: @@ -21,7 +14,6 @@ workflow VARIANTS_IVAR { sizes // channel: /path/to/genome.sizes gff // channel: /path/to/genome.gff bed // channel: /path/to/primers.bed - nextclade_db // channel: /path/to/nextclade_db/ snpeff_db // channel: /path/to/snpeff_db/ snpeff_config // channel: /path/to/snpeff.config ivar_multiqc_header // channel: /path/to/multiqc_header for ivar variants @@ -56,151 +48,41 @@ workflow VARIANTS_IVAR { ch_versions = ch_versions.mix(VCF_BGZIP_TABIX_STATS.out.versions) // - // Create genome consensus - // - ch_consensus = Channel.empty() - ch_consensus_qual = Channel.empty() - ch_bases_tsv = Channel.empty() - ch_bases_pdf = Channel.empty() - ch_quast_results = Channel.empty() - ch_quast_tsv = Channel.empty() - ch_pangolin_report = Channel.empty() - ch_nextclade_report = Channel.empty() - if (!params.skip_consensus) { - IVAR_CONSENSUS ( - bam, - fasta, - params.save_mpileup - ) - ch_consensus = IVAR_CONSENSUS.out.fasta - ch_consensus_qual = IVAR_CONSENSUS.out.qual - ch_versions = ch_versions.mix(IVAR_CONSENSUS.out.versions.first()) - - PLOT_BASE_DENSITY ( - ch_consensus - ) - ch_bases_tsv = PLOT_BASE_DENSITY.out.tsv - ch_bases_pdf = PLOT_BASE_DENSITY.out.pdf - ch_versions = ch_versions.mix(PLOT_BASE_DENSITY.out.versions.first()) - - if (!params.skip_variants_quast) { - QUAST ( - ch_consensus.collect{ it[1] }, - fasta, - gff, - true, - params.gff - ) - ch_quast_results = QUAST.out.results - ch_quast_tsv = QUAST.out.tsv - ch_versions = ch_versions.mix(QUAST.out.versions) - } - - if (!params.skip_pangolin) { - PANGOLIN ( - ch_consensus - ) - ch_pangolin_report = PANGOLIN.out.report - ch_versions = ch_versions.mix(PANGOLIN.out.versions.first()) - } - - if (!params.skip_nextclade) { - NEXTCLADE_RUN ( - ch_consensus, - nextclade_db - ) - ch_nextclade_report = NEXTCLADE_RUN.out.csv - ch_versions = ch_versions.mix(NEXTCLADE_RUN.out.versions.first()) - } - } - - // - // Annotate variants - // - ch_snpeff_vcf = Channel.empty() - ch_snpeff_tbi = Channel.empty() - ch_snpeff_stats = Channel.empty() - ch_snpeff_csv = Channel.empty() - ch_snpeff_txt = Channel.empty() - ch_snpeff_html = Channel.empty() - ch_snpsift_txt = Channel.empty() - if (params.gff && !params.skip_snpeff) { - SNPEFF_SNPSIFT ( - VCF_BGZIP_TABIX_STATS.out.vcf, - snpeff_db, - snpeff_config, - fasta - ) - ch_snpeff_vcf = SNPEFF_SNPSIFT.out.vcf - ch_snpeff_tbi = SNPEFF_SNPSIFT.out.tbi - ch_snpeff_stats = SNPEFF_SNPSIFT.out.stats - ch_snpeff_csv = SNPEFF_SNPSIFT.out.csv - ch_snpeff_txt = SNPEFF_SNPSIFT.out.txt - ch_snpeff_html = SNPEFF_SNPSIFT.out.html - ch_snpsift_txt = SNPEFF_SNPSIFT.out.snpsift_txt - ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) - } - - // - // Variant screenshots with ASCIIGenome + // Run downstream tools for variants QC // - ch_asciigenome_pdf = Channel.empty() - if (!params.skip_asciigenome) { - bam - .join(VCF_BGZIP_TABIX_STATS.out.vcf, by: [0]) - .join(VCF_BGZIP_TABIX_STATS.out.stats, by: [0]) - .map { meta, bam, vcf, stats -> - if (WorkflowCommons.getNumVariantsFromBCFToolsStats(stats) > 0) { - return [ meta, bam, vcf ] - } - } - .set { ch_asciigenome } - - ASCIIGENOME ( - ch_asciigenome, - fasta, - sizes, - gff, - bed, - params.asciigenome_window_size, - params.asciigenome_read_depth - ) - ch_asciigenome_pdf = ASCIIGENOME.out.pdf - ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first()) - } + VARIANTS_QC ( + bam, + VCF_BGZIP_TABIX_STATS.out.vcf, + VCF_BGZIP_TABIX_STATS.out.stats, + fasta, + sizes, + gff, + bed, + snpeff_db, + snpeff_config + ) + ch_versions = ch_versions.mix(VARIANTS_QC.out.versions) emit: - tsv = IVAR_VARIANTS.out.tsv // channel: [ val(meta), [ tsv ] ] - - vcf_orig = IVAR_VARIANTS_TO_VCF.out.vcf // channel: [ val(meta), [ vcf ] ] - log_out = IVAR_VARIANTS_TO_VCF.out.log // channel: [ val(meta), [ log ] ] - multiqc_tsv = IVAR_VARIANTS_TO_VCF.out.tsv // channel: [ val(meta), [ tsv ] ] - - vcf = VCF_BGZIP_TABIX_STATS.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = VCF_BGZIP_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] - stats = VCF_BGZIP_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] - - consensus = ch_consensus // channel: [ val(meta), [ fasta ] ] - consensus_qual = ch_consensus_qual // channel: [ val(meta), [ fasta ] ] - bases_tsv = ch_bases_tsv // channel: [ val(meta), [ tsv ] ] - bases_pdf = ch_bases_pdf // channel: [ val(meta), [ pdf ] ] - - quast_results = ch_quast_results // channel: [ val(meta), [ results ] ] - quast_tsv = ch_quast_tsv // channel: [ val(meta), [ tsv ] ] + tsv = IVAR_VARIANTS.out.tsv // channel: [ val(meta), [ tsv ] ] - snpeff_vcf = ch_snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] - snpeff_tbi = ch_snpeff_tbi // channel: [ val(meta), [ tbi ] ] - snpeff_stats = ch_snpeff_stats // channel: [ val(meta), [ txt ] ] - snpeff_csv = ch_snpeff_csv // channel: [ val(meta), [ csv ] ] - snpeff_txt = ch_snpeff_txt // channel: [ val(meta), [ txt ] ] - snpeff_html = ch_snpeff_html // channel: [ val(meta), [ html ] ] - snpsift_txt = ch_snpsift_txt // channel: [ val(meta), [ txt ] ] + vcf_orig = IVAR_VARIANTS_TO_VCF.out.vcf // channel: [ val(meta), [ vcf ] ] + log_out = IVAR_VARIANTS_TO_VCF.out.log // channel: [ val(meta), [ log ] ] + multiqc_tsv = IVAR_VARIANTS_TO_VCF.out.tsv // channel: [ val(meta), [ tsv ] ] - pangolin_report = ch_pangolin_report // channel: [ val(meta), [ csv ] ] + vcf = VCF_BGZIP_TABIX_STATS.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = VCF_BGZIP_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = VCF_BGZIP_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] - nextclade_report = ch_nextclade_report // channel: [ val(meta), [ csv ] ] + snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] + snpeff_tbi = VARIANTS_QC.out.snpeff_tbi // channel: [ val(meta), [ tbi ] ] + snpeff_stats = VARIANTS_QC.out.snpeff_stats // channel: [ val(meta), [ txt ] ] + snpeff_csv = VARIANTS_QC.out.snpeff_csv // channel: [ val(meta), [ csv ] ] + snpeff_txt = VARIANTS_QC.out.snpeff_txt // channel: [ val(meta), [ txt ] ] + snpeff_html = VARIANTS_QC.out.snpeff_html // channel: [ val(meta), [ html ] ] + snpsift_txt = VARIANTS_QC.out.snpsift_txt // channel: [ val(meta), [ txt ] ] - asciigenome_pdf = ch_asciigenome_pdf // channel: [ val(meta), [ pdf ] ] + asciigenome_pdf = VARIANTS_QC.out.asciigenome_pdf // channel: [ val(meta), [ pdf ] ] - versions = ch_versions // channel: [ versions.yml ] -} + versions = ch_versions // channel: [ versions.yml ] +} \ No newline at end of file From 76f9730d319e38cfcd051775f61902063425d1e1 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 24 Jan 2022 23:20:00 +0000 Subject: [PATCH 121/238] Re-work main workflow for new variant and consensus calling --- workflows/illumina.nf | 125 +++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 56 deletions(-) diff --git a/workflows/illumina.nf b/workflows/illumina.nf index c2807c3b..4998601e 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -53,9 +53,8 @@ ch_ivar_variants_header_mqc = file("$projectDir/assets/headers/ivar_variants_hea // // MODULE: Loaded from modules/local/ // -include { BCFTOOLS_ISEC } from '../modules/local/bcftools_isec' -include { CUTADAPT } from '../modules/local/cutadapt' -include { MULTIQC } from '../modules/local/multiqc_illumina' +include { CUTADAPT } from '../modules/local/cutadapt' +include { MULTIQC } from '../modules/local/multiqc_illumina' include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_GENOME } from '../modules/local/plot_mosdepth_regions' include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_AMPLICON } from '../modules/local/plot_mosdepth_regions' include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_FAIL_READS } from '../modules/local/multiqc_tsv_from_list' @@ -70,6 +69,8 @@ include { INPUT_CHECK } from '../subworkflows/local/input_check' include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_illumina' include { VARIANTS_IVAR } from '../subworkflows/local/variants_ivar' include { VARIANTS_BCFTOOLS } from '../subworkflows/local/variants_bcftools' +include { CONSENSUS_IVAR } from '../subworkflows/local/consensus_ivar' +include { CONSENSUS_BCFTOOLS } from '../subworkflows/local/consensus_bcftools' include { ASSEMBLY_SPADES } from '../subworkflows/local/assembly_spades' include { ASSEMBLY_UNICYCLER } from '../subworkflows/local/assembly_unicycler' include { ASSEMBLY_MINIA } from '../subworkflows/local/assembly_minia' @@ -351,7 +352,6 @@ workflow ILLUMINA { ch_mosdepth_multiqc = Channel.empty() ch_amplicon_heatmap_multiqc = Channel.empty() if (!params.skip_variants && !params.skip_mosdepth) { - MOSDEPTH_GENOME ( ch_bam.join(ch_bai, by: [0]), [], @@ -384,14 +384,11 @@ workflow ILLUMINA { // // SUBWORKFLOW: Call variants with IVar // - ch_ivar_vcf = Channel.empty() - ch_ivar_tbi = Channel.empty() - ch_ivar_counts_multiqc = Channel.empty() - ch_ivar_stats_multiqc = Channel.empty() - ch_ivar_snpeff_multiqc = Channel.empty() - ch_ivar_quast_multiqc = Channel.empty() - ch_ivar_pangolin_multiqc = Channel.empty() - ch_ivar_nextclade_multiqc = Channel.empty() + ch_vcf = Channel.empty() + ch_tbi = Channel.empty() + ch_ivar_counts_multiqc = Channel.empty() + ch_ivar_stats_multiqc = Channel.empty() + ch_ivar_snpeff_multiqc = Channel.empty() if (!params.skip_variants && 'ivar' in callers) { VARIANTS_IVAR ( ch_bam, @@ -399,21 +396,60 @@ workflow ILLUMINA { PREPARE_GENOME.out.chrom_sizes, PREPARE_GENOME.out.gff, (params.protocol == 'amplicon' && params.primer_bed) ? PREPARE_GENOME.out.primer_bed : [], - PREPARE_GENOME.out.nextclade_db, PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config, ch_ivar_variants_header_mqc ) - ch_ivar_vcf = VARIANTS_IVAR.out.vcf - ch_ivar_tbi = VARIANTS_IVAR.out.tbi - ch_ivar_counts_multiqc = VARIANTS_IVAR.out.multiqc_tsv - ch_ivar_stats_multiqc = VARIANTS_IVAR.out.stats - ch_ivar_snpeff_multiqc = VARIANTS_IVAR.out.snpeff_csv - ch_ivar_quast_multiqc = VARIANTS_IVAR.out.quast_tsv - ch_ivar_pangolin_multiqc = VARIANTS_IVAR.out.pangolin_report - ch_ivar_nextclade_report = VARIANTS_IVAR.out.nextclade_report - ch_versions = ch_versions.mix(VARIANTS_IVAR.out.versions) + ch_vcf = VARIANTS_IVAR.out.vcf + ch_tbi = VARIANTS_IVAR.out.tbi + ch_ivar_counts_multiqc = VARIANTS_IVAR.out.multiqc_tsv + ch_ivar_stats_multiqc = VARIANTS_IVAR.out.stats + ch_ivar_snpeff_multiqc = VARIANTS_IVAR.out.snpeff_csv + ch_versions = ch_versions.mix(VARIANTS_IVAR.out.versions) + } + // + // SUBWORKFLOW: Call variants with BCFTools + // + ch_vcf = Channel.empty() + ch_tbi = Channel.empty() + ch_bcftools_stats_multiqc = Channel.empty() + ch_bcftools_snpeff_multiqc = Channel.empty() + if (!params.skip_variants && 'bcftools' in callers) { + VARIANTS_BCFTOOLS ( + ch_bam, + PREPARE_GENOME.out.fasta, + PREPARE_GENOME.out.chrom_sizes, + PREPARE_GENOME.out.gff, + (params.protocol == 'amplicon' && params.primer_bed) ? PREPARE_GENOME.out.primer_bed : [], + PREPARE_GENOME.out.snpeff_db, + PREPARE_GENOME.out.snpeff_config + ) + ch_vcf = VARIANTS_BCFTOOLS.out.vcf + ch_tbi = VARIANTS_BCFTOOLS.out.tbi + ch_bcftools_stats_multiqc = VARIANTS_BCFTOOLS.out.stats + ch_bcftools_snpeff_multiqc = VARIANTS_BCFTOOLS.out.snpeff_csv + ch_versions = ch_versions.mix(VARIANTS_BCFTOOLS.out.versions) + } + + // + // SUBWORKFLOW: Call consensus with iVar and downstream QC + // + ch_ivar_quast_multiqc = Channel.empty() + ch_ivar_pangolin_multiqc = Channel.empty() + ch_ivar_nextclade_multiqc = Channel.empty() + if (!params.skip_consensus && 'ivar' in callers) { + CONSENSUS_IVAR ( + ch_bam, + PREPARE_GENOME.out.fasta, + PREPARE_GENOME.out.gff, + PREPARE_GENOME.out.nextclade_db + ) + ch_ivar_quast_multiqc = CONSENSUS_IVAR.out.quast_tsv + ch_ivar_pangolin_multiqc = CONSENSUS_IVAR.out.pangolin_report + ch_ivar_nextclade_report = CONSENSUS_IVAR.out.nextclade_report + ch_versions = ch_versions.mix(CONSENSUS_IVAR.out.versions) + // // MODULE: Get Nextclade clade information for MultiQC report // @@ -432,35 +468,25 @@ workflow ILLUMINA { .set { ch_ivar_nextclade_multiqc } } + // + // SUBWORKFLOW: Call consensus with BCFTools // - // SUBWORKFLOW: Call variants with BCFTools - // - ch_bcftools_vcf = Channel.empty() - ch_bcftools_tbi = Channel.empty() - ch_bcftools_stats_multiqc = Channel.empty() - ch_bcftools_snpeff_multiqc = Channel.empty() ch_bcftools_quast_multiqc = Channel.empty() ch_bcftools_pangolin_multiqc = Channel.empty() ch_bcftools_nextclade_multiqc = Channel.empty() - if (!params.skip_variants && 'bcftools' in callers) { - VARIANTS_BCFTOOLS ( + if (!params.skip_consensus && 'bcftools' in callers && callers) { + CONSENSUS_BCFTOOLS ( ch_bam, + ch_vcf, + ch_tbi, PREPARE_GENOME.out.fasta, - PREPARE_GENOME.out.chrom_sizes, PREPARE_GENOME.out.gff, - (params.protocol == 'amplicon' && params.primer_bed) ? PREPARE_GENOME.out.primer_bed : [], - PREPARE_GENOME.out.nextclade_db, - PREPARE_GENOME.out.snpeff_db, - PREPARE_GENOME.out.snpeff_config + PREPARE_GENOME.out.nextclade_db ) - ch_bcftools_vcf = VARIANTS_BCFTOOLS.out.vcf - ch_bcftools_tbi = VARIANTS_BCFTOOLS.out.tbi - ch_bcftools_stats_multiqc = VARIANTS_BCFTOOLS.out.stats - ch_bcftools_snpeff_multiqc = VARIANTS_BCFTOOLS.out.snpeff_csv - ch_bcftools_quast_multiqc = VARIANTS_BCFTOOLS.out.quast_tsv - ch_bcftools_pangolin_multiqc = VARIANTS_BCFTOOLS.out.pangolin_report - ch_bcftools_nextclade_report = VARIANTS_BCFTOOLS.out.nextclade_report - ch_versions = ch_versions.mix(VARIANTS_BCFTOOLS.out.versions) + ch_bcftools_quast_multiqc = CONSENSUS_BCFTOOLS.out.quast_tsv + ch_bcftools_pangolin_multiqc = CONSENSUS_BCFTOOLS.out.pangolin_report + ch_bcftools_nextclade_report = CONSENSUS_BCFTOOLS.out.nextclade_report + ch_versions = ch_versions.mix(CONSENSUS_BCFTOOLS.out.versions) // // MODULE: Get Nextclade clade information for MultiQC report @@ -480,19 +506,6 @@ workflow ILLUMINA { .set { ch_bcftools_nextclade_multiqc } } - // - // MODULE: Intersect variants across callers - // - if (!params.skip_variants && callers.size() > 1) { - BCFTOOLS_ISEC ( - ch_ivar_vcf - .join(ch_ivar_tbi, by: [0]) - .join(ch_bcftools_vcf, by: [0]) - .join(ch_bcftools_tbi, by: [0]) - ) - ch_versions = ch_versions.mix(BCFTOOLS_ISEC.out.versions) - } - // // MODULE: Primer trimming with Cutadapt // From bbe4c29feec5e53a713a96cf1c4e613466cbd807 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 10:47:56 +0000 Subject: [PATCH 122/238] Add --variant_caller and --consensus_caller params --- nextflow.config | 3 ++- nextflow_schema.json | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/nextflow.config b/nextflow.config index 9f3027db..1e58c976 100644 --- a/nextflow.config +++ b/nextflow.config @@ -61,7 +61,8 @@ params { skip_cutadapt = false // Illumina variant calling options - callers = null + variant_caller = null + consensus_caller = null min_mapped_reads = 1000 ivar_trim_noprimer = false ivar_trim_offset = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 18214bdf..906b3f97 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -383,10 +383,15 @@ "description": "Various options for the variant calling branch of the Illumina workflow.", "default": "", "properties": { - "callers": { + "variant_caller": { "type": "string", - "description": "Specify which variant calling algorithms you would like to use. Available options are 'ivar' (default for '--protocol amplicon') and 'bcftools' (default for '--protocol metagenomic').", - "fa_icon": "fas fa-phone-volume" + "fa_icon": "fas fa-phone-volume", + "description": "Specify which variant calling algorithm you would like to use. Available options are 'ivar' (default for '--protocol amplicon') and 'bcftools' (default for '--protocol metagenomic')." + }, + "consensus_caller": { + "type": "string", + "fa_icon": "fas fa-phone-volume", + "description": "Specify which consensus calling algorithm you would like to use. Available options are 'bcftools' and 'ivar' (default: 'bcftools')." }, "min_mapped_reads": { "type": "integer", @@ -441,6 +446,11 @@ "fa_icon": "fas fa-fast-forward", "description": "Skip SnpEff and SnpSift annotation of variants." }, + "skip_consensus_plots": { + "type": "boolean", + "fa_icon": "fas fa-fast-forward", + "description": "Skip creation of consensus base density plots." + }, "skip_consensus": { "type": "boolean", "fa_icon": "fas fa-fast-forward", From 765dda58ab8b917c6e4c3e341be2c317aeb7c32f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 12:21:30 +0000 Subject: [PATCH 123/238] Add implementation for --variant_caller and --consensus_caller everywhere --- README.md | 5 +++-- conf/modules_illumina.config | 8 +++++--- conf/test.config | 12 ++++++------ conf/test_full.config | 11 +++++++---- conf/test_full_sispa.config | 12 ++++++++++-- conf/test_sispa.config | 12 ++++++------ docs/output.md | 10 +++++----- lib/WorkflowIllumina.groovy | 17 +++++++++++++---- nextflow.config | 2 +- nextflow_schema.json | 13 +++++++++++-- workflows/illumina.nf | 24 ++++++++++++++---------- 11 files changed, 81 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 950fa1a0..47b8e5d4 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,13 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and 4. Duplicate read marking ([`picard`](https://broadinstitute.github.io/picard/); *optional*) 5. Alignment-level QC ([`picard`](https://broadinstitute.github.io/picard/), [`SAMtools`](https://sourceforge.net/projects/samtools/files/samtools/)) 6. Genome-wide and amplicon coverage QC plots ([`mosdepth`](https://github.com/brentp/mosdepth/)) - 7. Choice of multiple variant calling and consensus sequence generation routes ([`iVar variants and consensus`](https://github.com/andersen-lab/ivar); *default for amplicon data* *||* [`BCFTools`](http://samtools.github.io/bcftools/bcftools.html), [`BEDTools`](https://github.com/arq5x/bedtools2/); *default for metagenomics data*) + 7. Choice of multiple variant callers ([`iVar variants`](https://github.com/andersen-lab/ivar); *default for amplicon data* *||* [`BCFTools`](http://samtools.github.io/bcftools/bcftools.html); *default for metagenomics data*) * Variant annotation ([`SnpEff`](http://snpeff.sourceforge.net/SnpEff.html), [`SnpSift`](http://snpeff.sourceforge.net/SnpSift.html)) + * Individual variant screenshots with annotation tracks ([`ASCIIGenome`](https://asciigenome.readthedocs.io/en/latest/)) + 8. Choice of multiple consensus callers ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html), [`BEDTools`](https://github.com/arq5x/bedtools2/); *default for both amplicon and metagenomics data* *||* [`iVar consensus`](https://github.com/andersen-lab/ivar)) * Consensus assessment report ([`QUAST`](http://quast.sourceforge.net/quast)) * Lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)) * Clade assignment, mutation calling and sequence quality checks ([`Nextclade`](https://github.com/nextstrain/nextclade)) - * Individual variant screenshots with annotation tracks ([`ASCIIGenome`](https://asciigenome.readthedocs.io/en/latest/)) 6. _De novo_ assembly 1. Primer trimming ([`Cutadapt`](https://cutadapt.readthedocs.io/en/stable/guide.html); *amplicon data only*) 2. Choice of multiple assembly tools ([`SPAdes`](http://cab.spbu.ru/software/spades/) *||* [`Unicycler`](https://github.com/rrwick/Unicycler) *||* [`minia`](https://github.com/GATB/minia)) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index af334abf..24fb8607 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -10,8 +10,10 @@ ---------------------------------------------------------------------------------------- */ +def variant_caller = params.variant_caller +if (!variant_caller) { variant_caller = params.protocol == 'amplicon' ? 'ivar' : 'bcftools' } + def assemblers = params.assemblers ? params.assemblers.split(',').collect{ it.trim().toLowerCase() } : [] -def callers = params.callers ? params.callers.split(',').collect{ it.trim().toLowerCase() } : [] // // Pre-processing and general configuration options @@ -378,7 +380,7 @@ if (!params.skip_variants) { } } - if ('ivar' in callers) { + if (variant_caller == 'ivar') { process { withName: 'IVAR_VARIANTS' { ext.args = '-t 0.25 -q 20 -m 10' @@ -569,7 +571,7 @@ if (!params.skip_variants) { } } - if ('bcftools' in callers) { + if (variant_caller == 'bcftools') { process { withName: 'BCFTOOLS_MPILEUP' { ext.args = '--ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' diff --git a/conf/test.config b/conf/test.config index 44eca7bb..c3109b6a 100644 --- a/conf/test.config +++ b/conf/test.config @@ -30,10 +30,10 @@ params { genome = 'MN908947.3' kraken2_db = 'https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/genome/kraken2/kraken2_hs22.tar.gz' - // Other pipeline options - callers = 'ivar,bcftools' - assemblers = 'spades,unicycler,minia' - - // Skip this by default to bypass Github Actions disk quota errors - skip_plasmidid = true + // Variant calling options + variant_caller = 'ivar' + + // Assembly options + assemblers = 'spades,unicycler,minia' + skip_plasmidid = true // Skip this by default to bypass Github Actions disk quota errors } diff --git a/conf/test_full.config b/conf/test_full.config index 10041d28..e8a6d2cc 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -24,13 +24,16 @@ params { // Genome references genome = 'MN908947.3' - // Other pipeline options - callers = 'ivar,bcftools' - assemblers = 'spades,unicycler,minia' + // Variant calling options + variant_caller = 'ivar' + + // Assembly options + assemblers = 'spades,unicycler,minia' + skip_plasmidid = true // Skip this by default to bypass Github Actions disk quota errors } process { - withName:PLASMIDID { + withName: 'PLASMIDID' { errorStrategy = 'ignore' } } diff --git a/conf/test_full_sispa.config b/conf/test_full_sispa.config index 49b295fc..2e8b04d2 100644 --- a/conf/test_full_sispa.config +++ b/conf/test_full_sispa.config @@ -22,7 +22,15 @@ params { // Genome references genome = 'MN908947.3' - // Other pipeline options - callers = 'ivar,bcftools' + // Variant calling options + variant_caller = 'bcftools' + + // Assembly options assemblers = 'spades,unicycler,minia' } + +process { + withName: 'PLASMIDID' { + errorStrategy = 'ignore' + } +} \ No newline at end of file diff --git a/conf/test_sispa.config b/conf/test_sispa.config index 526bf56d..9aaff136 100644 --- a/conf/test_sispa.config +++ b/conf/test_sispa.config @@ -28,10 +28,10 @@ params { genome = 'MN908947.3' kraken2_db = 'https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/genome/kraken2/kraken2_hs22.tar.gz' - // Other pipeline options - callers = 'ivar,bcftools' - assemblers = 'spades,unicycler,minia' - - // Skip this by default to bypass Github Actions disk quota errors - skip_plasmidid = true + // Variant calling options + variant_caller = 'bcftools' + + // Assembly options + assemblers = 'spades,unicycler,minia' + skip_plasmidid = true // Skip this by default to bypass Github Actions disk quota errors } diff --git a/docs/output.md b/docs/output.md index a2bfa18a..4be3d50d 100644 --- a/docs/output.md +++ b/docs/output.md @@ -578,7 +578,7 @@ iVar outputs a tsv format, which is not compatible with downstream analysis such * `variants//snpeff/bcftools_stats/` * `*.bcftools_stats.txt`: Statistics and counts obtained from VCF file. -**NB:** The value of `` in the output directory name above is determined by the `--callers` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). @@ -596,7 +596,7 @@ iVar outputs a tsv format, which is not compatible with downstream analysis such * `variants//quast/` * `report.html`: Results report in HTML format. Also available in various other file formats i.e. `report.pdf`, `report.tex`, `report.tsv` and `report.txt`. -**NB:** The value of `` in the output directory name above is determined by the `--callers` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). @@ -610,7 +610,7 @@ iVar outputs a tsv format, which is not compatible with downstream analysis such * `variants//pangolin/` * `*.pangolin.csv`: Lineage analysis results from Pangolin. -**NB:** The value of `` in the output directory name above is determined by the `--callers` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). @@ -624,7 +624,7 @@ Phylogenetic Assignment of Named Global Outbreak LINeages ([Pangolin](https://gi * `variants//nextclade/` * `*.csv`: Analysis results from Nextlade containing genome clade assignment, mutation calling and sequence quality checks. -**NB:** The value of `` in the output directory name above is determined by the `--callers` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). @@ -638,7 +638,7 @@ Phylogenetic Assignment of Named Global Outbreak LINeages ([Pangolin](https://gi * `variants//asciigenome//` * `*.pdf`: Individual variant screenshots with annotation tracks in PDF format. -**NB:** The value of `` in the output directory name above is determined by the `--callers` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). diff --git a/lib/WorkflowIllumina.groovy b/lib/WorkflowIllumina.groovy index f9531376..56b20612 100755 --- a/lib/WorkflowIllumina.groovy +++ b/lib/WorkflowIllumina.groovy @@ -31,10 +31,19 @@ class WorkflowIllumina { } // Variant calling parameter validation - def callers = params.callers ? params.callers.split(',').collect{ it.trim().toLowerCase() } : [] - if ((valid_params['callers'] + callers).unique().size() != valid_params['callers'].size()) { - log.error "Invalid option: ${params.callers}. Valid options for '--callers': ${valid_params['callers'].join(', ')}." - System.exit(1) + if (params.variant_caller) { + if (!valid_params['variant_callers'].contains(params.variant_caller)) { + log.error "Invalid option: ${params.variant_caller}. Valid options for '--variant_caller': ${valid_params['variant_callers'].join(', ')}." + System.exit(1) + } + } + + // Consensus calling parameter validation + if (params.consensus_caller) { + if (!valid_params['consensus_callers'].contains(params.consensus_caller)) { + log.error "Invalid option: ${params.consensus_caller}. Valid options for '--consensus_caller': ${valid_params['consensus_callers'].join(', ')}." + System.exit(1) + } } if (params.protocol == 'amplicon' && !params.skip_variants && !params.primer_bed) { diff --git a/nextflow.config b/nextflow.config index 1e58c976..c28cc0a9 100644 --- a/nextflow.config +++ b/nextflow.config @@ -62,7 +62,7 @@ params { // Illumina variant calling options variant_caller = null - consensus_caller = null + consensus_caller = 'bcftools' min_mapped_reads = 1000 ivar_trim_noprimer = false ivar_trim_offset = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 906b3f97..34154e20 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -386,12 +386,21 @@ "variant_caller": { "type": "string", "fa_icon": "fas fa-phone-volume", - "description": "Specify which variant calling algorithm you would like to use. Available options are 'ivar' (default for '--protocol amplicon') and 'bcftools' (default for '--protocol metagenomic')." + "description": "Specify which variant calling algorithm you would like to use. Available options are 'ivar' (default for '--protocol amplicon') and 'bcftools' (default for '--protocol metagenomic').", + "enum": [ + "ivar", + "bcftools" + ] }, "consensus_caller": { "type": "string", + "default": "bcftools", "fa_icon": "fas fa-phone-volume", - "description": "Specify which consensus calling algorithm you would like to use. Available options are 'bcftools' and 'ivar' (default: 'bcftools')." + "description": "Specify which consensus calling algorithm you would like to use. Available options are 'bcftools' and 'ivar' (default: 'bcftools').", + "enum": [ + "ivar", + "bcftools" + ] }, "min_mapped_reads": { "type": "integer", diff --git a/workflows/illumina.nf b/workflows/illumina.nf index 4998601e..935f3280 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -5,10 +5,11 @@ */ def valid_params = [ - protocols : ['metagenomic', 'amplicon'], - callers : ['ivar', 'bcftools'], - assemblers : ['spades', 'unicycler', 'minia'], - spades_modes: ['rnaviral', 'corona', 'metaviral', 'meta', 'metaplasmid', 'plasmid', 'isolate', 'rna', 'bio'] + protocols : ['metagenomic', 'amplicon'], + variant_callers : ['ivar', 'bcftools'], + consensus_callers : ['ivar', 'bcftools'], + assemblers : ['spades', 'unicycler', 'minia'], + spades_modes : ['rnaviral', 'corona', 'metaviral', 'meta', 'metaplasmid', 'plasmid', 'isolate', 'rna', 'bio'] ] def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) @@ -28,8 +29,11 @@ if (params.input) { ch_input = file(params.input) } else { exit 1 if (params.spades_hmm) { ch_spades_hmm = file(params.spades_hmm) } else { ch_spades_hmm = [] } def assemblers = params.assemblers ? params.assemblers.split(',').collect{ it.trim().toLowerCase() } : [] -def callers = params.callers ? params.callers.split(',').collect{ it.trim().toLowerCase() } : [] -if (!callers) { callers = params.protocol == 'amplicon' ? ['ivar'] : ['bcftools'] } + +def variant_caller = params.variant_caller +if (!variant_caller) { variant_caller = params.protocol == 'amplicon' ? 'ivar' : 'bcftools' } + +def consensus_caller = params.consensus_caller ?: 'bcftools' /* ======================================================================================== @@ -389,7 +393,7 @@ workflow ILLUMINA { ch_ivar_counts_multiqc = Channel.empty() ch_ivar_stats_multiqc = Channel.empty() ch_ivar_snpeff_multiqc = Channel.empty() - if (!params.skip_variants && 'ivar' in callers) { + if (!params.skip_variants && variant_caller == 'ivar') { VARIANTS_IVAR ( ch_bam, PREPARE_GENOME.out.fasta, @@ -415,7 +419,7 @@ workflow ILLUMINA { ch_tbi = Channel.empty() ch_bcftools_stats_multiqc = Channel.empty() ch_bcftools_snpeff_multiqc = Channel.empty() - if (!params.skip_variants && 'bcftools' in callers) { + if (!params.skip_variants && variant_caller == 'bcftools') { VARIANTS_BCFTOOLS ( ch_bam, PREPARE_GENOME.out.fasta, @@ -438,7 +442,7 @@ workflow ILLUMINA { ch_ivar_quast_multiqc = Channel.empty() ch_ivar_pangolin_multiqc = Channel.empty() ch_ivar_nextclade_multiqc = Channel.empty() - if (!params.skip_consensus && 'ivar' in callers) { + if (!params.skip_consensus && consensus_caller == 'ivar') { CONSENSUS_IVAR ( ch_bam, PREPARE_GENOME.out.fasta, @@ -474,7 +478,7 @@ workflow ILLUMINA { ch_bcftools_quast_multiqc = Channel.empty() ch_bcftools_pangolin_multiqc = Channel.empty() ch_bcftools_nextclade_multiqc = Channel.empty() - if (!params.skip_consensus && 'bcftools' in callers && callers) { + if (!params.skip_consensus && consensus_caller == 'bcftools' && variant_caller) { CONSENSUS_BCFTOOLS ( ch_bam, ch_vcf, From 4e8b93ac04541afa90d3704a68471bd2781fa696 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 12:22:14 +0000 Subject: [PATCH 124/238] Add implementation for --variant_caller and --consensus_caller to main workflow --- workflows/illumina.nf | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/workflows/illumina.nf b/workflows/illumina.nf index 935f3280..7d6af82b 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -33,8 +33,6 @@ def assemblers = params.assemblers ? params.assemblers.split(',').collect{ it.tr def variant_caller = params.variant_caller if (!variant_caller) { variant_caller = params.protocol == 'amplicon' ? 'ivar' : 'bcftools' } -def consensus_caller = params.consensus_caller ?: 'bcftools' - /* ======================================================================================== CONFIG FILES @@ -442,7 +440,7 @@ workflow ILLUMINA { ch_ivar_quast_multiqc = Channel.empty() ch_ivar_pangolin_multiqc = Channel.empty() ch_ivar_nextclade_multiqc = Channel.empty() - if (!params.skip_consensus && consensus_caller == 'ivar') { + if (!params.skip_consensus && params.consensus_caller == 'ivar') { CONSENSUS_IVAR ( ch_bam, PREPARE_GENOME.out.fasta, @@ -478,7 +476,7 @@ workflow ILLUMINA { ch_bcftools_quast_multiqc = Channel.empty() ch_bcftools_pangolin_multiqc = Channel.empty() ch_bcftools_nextclade_multiqc = Channel.empty() - if (!params.skip_consensus && consensus_caller == 'bcftools' && variant_caller) { + if (!params.skip_consensus && params.consensus_caller == 'bcftools' && variant_caller) { CONSENSUS_BCFTOOLS ( ch_bam, ch_vcf, From 0bdb1a728694014dedc90c19937cc914e6c710fd Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 13:01:35 +0000 Subject: [PATCH 125/238] Fix config options for new caller params --- conf/modules_illumina.config | 363 +++++++++++++++++------------------ 1 file changed, 181 insertions(+), 182 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 24fb8607..c51a54e1 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -391,23 +391,14 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - if (params.protocol == 'amplicon') { - withName: 'IVAR_VARIANTS_TO_VCF' { - ext.args = '--ignore_strand_bias' - publishDir = [ - path: { "${params.outdir}/variants/ivar/log" }, - mode: 'copy', - pattern: '*.log' - ] - } - }else{ - withName: 'IVAR_VARIANTS_TO_VCF' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/log" }, - mode: 'copy', - pattern: '*.log' - ] - } + + withName: 'IVAR_VARIANTS_TO_VCF' { + ext.args = params.protocol == 'amplicon' ? '--ignore_strand_bias' : '' + publishDir = [ + path: { "${params.outdir}/variants/ivar/log" }, + mode: 'copy', + pattern: '*.log' + ] } withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:TABIX_BGZIP' { @@ -434,86 +425,11 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - - withName: 'MULTIQC_TSV_IVAR_NEXTCLADE' { - publishDir = [ - path: { "${params.outdir}/multiqc" }, - enabled: false - ] - } } - - if (!params.skip_consensus) { - process { - withName: 'IVAR_CONSENSUS' { - ext.args = '-t 0.75 -q 20 -m 10 -n N' - ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' - ext.prefix = { "${meta.id}.consensus" } - publishDir = [ - [ - path: { "${params.outdir}/variants/ivar/consensus" }, - mode: 'copy', - pattern: "*.{fa,txt}", - ], - [ - path: { "${params.outdir}/variants/ivar/consensus" }, - mode: 'copy', - pattern: "*.mpileup", - enabled: params.save_mpileup - ] - ] - } - - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PLOT_BASE_DENSITY' { - ext.prefix = { "${meta.id}.consensus" } - publishDir = [ - path: { "${params.outdir}/variants/ivar/consensus/base_qc" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - - if (!params.skip_pangolin) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:PANGOLIN' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/pangolin" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - - if (!params.skip_nextclade) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:NEXTCLADE_RUN' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/nextclade" }, - mode: 'copy', - pattern: "*.csv" - ] - } - } - } - - if (!params.skip_variants_quast) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:QUAST' { - publishDir = [ - path: { "${params.outdir}/variants/ivar" }, - mode: 'copy', - pattern: "quast" - ] - } - } - } - } - + if (!params.skip_asciigenome) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:ASCIIGENOME' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:ASCIIGENOME' { publishDir = [ path: { "${params.outdir}/variants/ivar/asciigenome/${meta.id}" }, mode: 'copy', @@ -525,7 +441,7 @@ if (!params.skip_variants) { if (!params.skip_snpeff) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPEFF_ANN' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:SNPEFF_ANN' { publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff" }, mode: 'copy', @@ -533,7 +449,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff" }, @@ -542,7 +458,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff" }, @@ -551,7 +467,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff/bcftools_stats" }, @@ -560,7 +476,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff" }, mode: 'copy', @@ -571,33 +487,28 @@ if (!params.skip_variants) { } } - if (variant_caller == 'bcftools') { + if (!params.skip_consensus && params.consensus_caller == 'ivar') { process { - withName: 'BCFTOOLS_MPILEUP' { - ext.args = '--ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' - ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' - ext.args3 = "--include 'INFO/DP>=10'" + withName: 'IVAR_CONSENSUS' { + ext.args = '-t 0.75 -q 20 -m 10 -n N' + ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' + ext.prefix = { "${meta.id}.consensus" } publishDir = [ [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/ivar/consensus" }, mode: 'copy', - pattern: '*.{gz,tbi}' + pattern: "*.{fa,txt}", ], [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/ivar/consensus" }, mode: 'copy', - pattern: '*.mpileup', + pattern: "*.mpileup", enabled: params.save_mpileup - ], - [ - path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, - mode: 'copy', - pattern: '*stats.txt' ] ] } - withName: 'MULTIQC_TSV_BCFTOOLS_NEXTCLADE' { + withName: 'MULTIQC_TSV_IVAR_NEXTCLADE' { publishDir = [ path: { "${params.outdir}/multiqc" }, enabled: false @@ -605,95 +516,86 @@ if (!params.skip_variants) { } } - if (!params.skip_consensus) { + if (!params.skip_consensus_plots) { process { - withName: 'MAKE_BED_MASK' { - ext.args = "-a --ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 0" - ext.args2 = 10 - ext.prefix = { "${meta.id}.coverage.masked" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - mode: 'copy', - pattern: "*.mpileup", - enabled: params.save_mpileup - ] - } - - withName: 'BEDTOOLS_MERGE' { - ext.prefix = { "${meta.id}.coverage.merged" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - enabled: false - ] - } - - withName: 'BEDTOOLS_MASKFASTA' { - ext.prefix = { "${meta.id}.masked" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - enabled: false - ] - } - - withName: 'BCFTOOLS_CONSENSUS' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_IVAR:.*:PLOT_BASE_DENSITY' { ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/bcftools/consensus" }, + path: { "${params.outdir}/variants/ivar/consensus/base_qc" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } + } + } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:PLOT_BASE_DENSITY' { - ext.prefix = { "${meta.id}.consensus" } + if (!params.skip_pangolin) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_IVAR:.*:PANGOLIN' { publishDir = [ - path: { "${params.outdir}/variants/bcftools/consensus/base_qc" }, + path: { "${params.outdir}/variants/ivar/pangolin" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } + } - if (!params.skip_pangolin) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:PANGOLIN' { - publishDir = [ - path: { "${params.outdir}/variants/bcftools/pangolin" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + if (!params.skip_nextclade) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_IVAR:.*:NEXTCLADE_RUN' { + publishDir = [ + path: { "${params.outdir}/variants/ivar/nextclade" }, + mode: 'copy', + pattern: "*.csv" + ] } } + } - if (!params.skip_nextclade) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:NEXTCLADE_RUN' { - publishDir = [ - path: { "${params.outdir}/variants/bcftools/nextclade" }, - mode: 'copy', - pattern: "*.csv" - ] - } + if (!params.skip_variants_quast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_IVAR:.*:QUAST' { + publishDir = [ + path: { "${params.outdir}/variants/ivar" }, + mode: 'copy', + pattern: "quast" + ] } } + } + } - if (!params.skip_variants_quast) { - process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:QUAST' { - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - mode: 'copy', - pattern: "quast" - ] - } - } + if (variant_caller == 'bcftools') { + process { + withName: 'BCFTOOLS_MPILEUP' { + ext.args = '--ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' + ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' + ext.args3 = "--include 'INFO/DP>=10'" + publishDir = [ + [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: '*.{gz,tbi}' + ], + [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: '*.mpileup', + enabled: params.save_mpileup + ], + [ + path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, + mode: 'copy', + pattern: '*stats.txt' + ] + ] } } if (!params.skip_asciigenome) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:ASCIIGENOME' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:ASCIIGENOME' { publishDir = [ path: { "${params.outdir}/variants/bcftools/asciigenome/${meta.id}" }, mode: 'copy', @@ -705,7 +607,7 @@ if (!params.skip_variants) { if (!params.skip_snpeff) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPEFF_ANN' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:SNPEFF_ANN' { publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff" }, mode: 'copy', @@ -713,7 +615,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff" }, @@ -722,7 +624,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff" }, @@ -731,7 +633,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff/bcftools_stats" }, @@ -740,7 +642,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { + withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff" }, mode: 'copy', @@ -750,6 +652,103 @@ if (!params.skip_variants) { } } } + + if (!params.skip_consensus && params.consensus_caller == 'bcftools') { + process { + withName: 'MAKE_BED_MASK' { + ext.args = "-a --ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 0" + ext.args2 = 10 + ext.prefix = { "${meta.id}.coverage.masked" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: "*.mpileup", + enabled: params.save_mpileup + ] + } + + withName: 'BEDTOOLS_MERGE' { + ext.prefix = { "${meta.id}.coverage.merged" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } + + withName: 'BEDTOOLS_MASKFASTA' { + ext.prefix = { "${meta.id}.masked" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + enabled: false + ] + } + + withName: 'BCFTOOLS_CONSENSUS' { + ext.prefix = { "${meta.id}.consensus" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools/consensus" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'MULTIQC_TSV_BCFTOOLS_NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/multiqc" }, + enabled: false + ] + } + } + + if (!params.skip_pangolin) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_BCFTOOLS:.*:PANGOLIN' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/pangolin" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + + if (!params.skip_nextclade) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_BCFTOOLS:.*:NEXTCLADE_RUN' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/nextclade" }, + mode: 'copy', + pattern: "*.csv" + ] + } + } + } + + if (!params.skip_variants_quast) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_BCFTOOLS:.*:QUAST' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: "quast" + ] + } + } + } + + if (!params.skip_consensus_plots) { + process { + withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_BCFTOOLS:.*:PLOT_BASE_DENSITY' { + ext.prefix = { "${meta.id}.consensus" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools/consensus/base_qc" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } + } } if (!params.skip_assembly) { From 653d5306ffb0f5b09f42cc0cfa6abbf05929a54d Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 13:05:46 +0000 Subject: [PATCH 126/238] Update CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53028e10..d2e4791a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [[#232](https://github.com/nf-core/viralrecon/issues/232)] - Remove duplicate variants called by ARTIC ONT pipeline * [[#235](https://github.com/nf-core/viralrecon/issues/235)] - Nextclade version bump * [[#244](https://github.com/nf-core/viralrecon/issues/244)] - Fix BCFtools consensus generation and masking +* [[#245](https://github.com/nf-core/viralrecon/issues/245)] - Mpileup file as output +* [[#247](https://github.com/nf-core/viralrecon/issues/247)] - Add strand-bias filtering option and codon fix in consecutive positions in ivar tsv conversion to vcf ### Parameters @@ -24,8 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | | `--nextclade_dataset_reference` | | | `--nextclade_dataset_tag` | | | `--skip_consensus_plots` | -| `--callers` | `--variant_caller` | | | `--consensus_caller` | +| `--callers` | `--variant_caller` | > **NB:** Parameter has been __updated__ if both old and new parameter information is present. > **NB:** Parameter has been __added__ if just the new parameter information is present. From e937a45c08325d2aff5acd9d13855406022f1a21 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 16:52:50 +0000 Subject: [PATCH 127/238] Replace initial process path with glob in modules configs --- conf/modules_illumina.config | 110 +++++++++++++++++------------------ conf/modules_nanopore.config | 16 ++--- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index c51a54e1..1736e38c 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -20,7 +20,7 @@ def assemblers = params.assemblers ? params.assemblers.split(',').collect{ it.tr // process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:GUNZIP_.*' { + withName: '.*:.*:PREPARE_GENOME:GUNZIP_.*' { publishDir = [ path: { "${params.outdir}/genome" }, mode: 'copy', @@ -29,7 +29,7 @@ process { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:PREPARE_GENOME:UNTAR_.*' { + withName: '.*:.*:PREPARE_GENOME:UNTAR_.*' { ext.args2 = '--no-same-owner' publishDir = [ path: { "${params.outdir}/genome" }, @@ -49,7 +49,7 @@ process { if (!params.skip_fastqc) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_RAW' { + withName: '.*:.*:FASTQC_FASTP:FASTQC_RAW' { ext.args = '--quiet' publishDir = [ path: { "${params.outdir}/fastqc/raw" }, @@ -94,7 +94,7 @@ if (!params.skip_fastp) { if (!params.skip_fastqc) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC_FASTP:FASTQC_TRIM' { + withName: '.*:.*:FASTQC_FASTP:FASTQC_TRIM' { ext.args = '--quiet' publishDir = [ path: { "${params.outdir}/fastqc/trim" }, @@ -162,7 +162,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { + withName: '.*:.*:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { ext.prefix = { "${meta.id}.sorted" } publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, @@ -171,7 +171,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { + withName: '.*:.*:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, mode: 'copy', @@ -179,7 +179,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + withName: '.*:.*:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { publishDir = [ path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, mode: 'copy', @@ -237,7 +237,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { + withName: '.*:.*:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { ext.prefix = { "${meta.id}.ivar_trim.sorted" } publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, @@ -247,7 +247,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { + withName: '.*:.*:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, mode: 'copy', @@ -256,7 +256,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + withName: '.*:.*:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { publishDir = [ path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, mode: 'copy', @@ -401,7 +401,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:TABIX_BGZIP' { + withName: '.*:.*:VARIANTS_IVAR:.*:TABIX_BGZIP' { publishDir = [ path: { "${params.outdir}/variants/ivar" }, mode: 'copy', @@ -409,7 +409,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:.*:TABIX_TABIX' { + withName: '.*:.*:VARIANTS_IVAR:.*:.*:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/ivar" }, @@ -418,7 +418,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:.*:BCFTOOLS_STATS' { + withName: '.*:.*:VARIANTS_IVAR:.*:.*:BCFTOOLS_STATS' { publishDir = [ path: { "${params.outdir}/variants/ivar/bcftools_stats" }, mode: 'copy', @@ -429,7 +429,7 @@ if (!params.skip_variants) { if (!params.skip_asciigenome) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:ASCIIGENOME' { + withName: '.*:.*:VARIANTS_IVAR:.*:ASCIIGENOME' { publishDir = [ path: { "${params.outdir}/variants/ivar/asciigenome/${meta.id}" }, mode: 'copy', @@ -441,7 +441,7 @@ if (!params.skip_variants) { if (!params.skip_snpeff) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:SNPEFF_ANN' { + withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:SNPEFF_ANN' { publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff" }, mode: 'copy', @@ -449,7 +449,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { + withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff" }, @@ -458,7 +458,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { + withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff" }, @@ -467,7 +467,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { + withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff/bcftools_stats" }, @@ -476,7 +476,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { + withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { publishDir = [ path: { "${params.outdir}/variants/ivar/snpeff" }, mode: 'copy', @@ -518,7 +518,7 @@ if (!params.skip_variants) { if (!params.skip_consensus_plots) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_IVAR:.*:PLOT_BASE_DENSITY' { + withName: '.*:.*:CONSENSUS_IVAR:.*:PLOT_BASE_DENSITY' { ext.prefix = { "${meta.id}.consensus" } publishDir = [ path: { "${params.outdir}/variants/ivar/consensus/base_qc" }, @@ -531,7 +531,7 @@ if (!params.skip_variants) { if (!params.skip_pangolin) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_IVAR:.*:PANGOLIN' { + withName: '.*:.*:CONSENSUS_IVAR:.*:PANGOLIN' { publishDir = [ path: { "${params.outdir}/variants/ivar/pangolin" }, mode: 'copy', @@ -543,7 +543,7 @@ if (!params.skip_variants) { if (!params.skip_nextclade) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_IVAR:.*:NEXTCLADE_RUN' { + withName: '.*:.*:CONSENSUS_IVAR:.*:NEXTCLADE_RUN' { publishDir = [ path: { "${params.outdir}/variants/ivar/nextclade" }, mode: 'copy', @@ -555,7 +555,7 @@ if (!params.skip_variants) { if (!params.skip_variants_quast) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_IVAR:.*:QUAST' { + withName: '.*:.*:CONSENSUS_IVAR:.*:QUAST' { publishDir = [ path: { "${params.outdir}/variants/ivar" }, mode: 'copy', @@ -595,7 +595,7 @@ if (!params.skip_variants) { if (!params.skip_asciigenome) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:ASCIIGENOME' { + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:ASCIIGENOME' { publishDir = [ path: { "${params.outdir}/variants/bcftools/asciigenome/${meta.id}" }, mode: 'copy', @@ -607,7 +607,7 @@ if (!params.skip_variants) { if (!params.skip_snpeff) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:SNPEFF_ANN' { + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:SNPEFF_ANN' { publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff" }, mode: 'copy', @@ -615,7 +615,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff" }, @@ -624,7 +624,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff" }, @@ -633,7 +633,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff/bcftools_stats" }, @@ -642,7 +642,7 @@ if (!params.skip_variants) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { publishDir = [ path: { "${params.outdir}/variants/bcftools/snpeff" }, mode: 'copy', @@ -702,7 +702,7 @@ if (!params.skip_variants) { if (!params.skip_pangolin) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_BCFTOOLS:.*:PANGOLIN' { + withName: '.*:.*:CONSENSUS_BCFTOOLS:.*:PANGOLIN' { publishDir = [ path: { "${params.outdir}/variants/bcftools/pangolin" }, mode: 'copy', @@ -714,7 +714,7 @@ if (!params.skip_variants) { if (!params.skip_nextclade) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_BCFTOOLS:.*:NEXTCLADE_RUN' { + withName: '.*:.*:CONSENSUS_BCFTOOLS:.*:NEXTCLADE_RUN' { publishDir = [ path: { "${params.outdir}/variants/bcftools/nextclade" }, mode: 'copy', @@ -726,7 +726,7 @@ if (!params.skip_variants) { if (!params.skip_variants_quast) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_BCFTOOLS:.*:QUAST' { + withName: '.*:.*:CONSENSUS_BCFTOOLS:.*:QUAST' { publishDir = [ path: { "${params.outdir}/variants/bcftools" }, mode: 'copy', @@ -738,7 +738,7 @@ if (!params.skip_variants) { if (!params.skip_consensus_plots) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:CONSENSUS_BCFTOOLS:.*:PLOT_BASE_DENSITY' { + withName: '.*:.*:CONSENSUS_BCFTOOLS:.*:PLOT_BASE_DENSITY' { ext.prefix = { "${meta.id}.consensus" } publishDir = [ path: { "${params.outdir}/variants/bcftools/consensus/base_qc" }, @@ -791,7 +791,7 @@ if (!params.skip_assembly) { if (!params.skip_fastqc) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:FASTQC' { + withName: '.*:.*:FASTQC' { ext.args = '--quiet' ext.prefix = { "${meta.id}.primer_trim" } publishDir = [ @@ -822,14 +822,14 @@ if (!params.skip_assembly) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:GUNZIP_SCAFFOLDS' { + withName: '.*:.*:ASSEMBLY_SPADES:GUNZIP_SCAFFOLDS' { publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, enabled: false ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:GUNZIP_GFA' { + withName: '.*:.*:ASSEMBLY_SPADES:GUNZIP_GFA' { publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, enabled: false @@ -839,7 +839,7 @@ if (!params.skip_assembly) { if (!params.skip_bandage) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:BANDAGE_IMAGE' { + withName: '.*:.*:ASSEMBLY_SPADES:BANDAGE_IMAGE' { ext.args = '--height 1000' publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}/bandage" }, @@ -852,7 +852,7 @@ if (!params.skip_assembly) { if (!params.skip_blast) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:BLAST_BLASTN' { + withName: '.*:.*:ASSEMBLY_SPADES:.*:BLAST_BLASTN' { ext.args = "-outfmt '6 stitle std slen qlen qcovs'" publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}/blastn" }, @@ -861,7 +861,7 @@ if (!params.skip_assembly) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:FILTER_BLASTN' { + withName: '.*:.*:ASSEMBLY_SPADES:.*:FILTER_BLASTN' { ext.prefix = { "${meta.id}.filter.blastn" } publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}/blastn" }, @@ -874,7 +874,7 @@ if (!params.skip_assembly) { if (!params.skip_assembly_quast) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:QUAST' { + withName: '.*:.*:ASSEMBLY_SPADES:.*:QUAST' { publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}" }, mode: 'copy', @@ -886,7 +886,7 @@ if (!params.skip_assembly) { if (!params.skip_abacas) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:ABACAS' { + withName: '.*:.*:ASSEMBLY_SPADES:.*:ABACAS' { ext.args = '-m -p nucmer' publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}/abacas" }, @@ -899,7 +899,7 @@ if (!params.skip_assembly) { if (!params.skip_plasmidid) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_SPADES:.*:PLASMIDID' { + withName: '.*:.*:ASSEMBLY_SPADES:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ path: { "${params.outdir}/assembly/spades/${params.spades_mode}/plasmidid" }, @@ -928,14 +928,14 @@ if (!params.skip_assembly) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:GUNZIP_SCAFFOLDS' { + withName: '.*:.*:ASSEMBLY_UNICYCLER:GUNZIP_SCAFFOLDS' { publishDir = [ path: { "${params.outdir}/assembly/unicycler" }, enabled: false ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:GUNZIP_GFA' { + withName: '.*:.*:ASSEMBLY_UNICYCLER:GUNZIP_GFA' { publishDir = [ path: { "${params.outdir}/assembly/unicycler" }, enabled: false @@ -945,7 +945,7 @@ if (!params.skip_assembly) { if (!params.skip_bandage) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:BANDAGE_IMAGE' { + withName: '.*:.*:ASSEMBLY_UNICYCLER:BANDAGE_IMAGE' { ext.args = '--height 1000' publishDir = [ path: { "${params.outdir}/assembly/unicycler/bandage" }, @@ -958,7 +958,7 @@ if (!params.skip_assembly) { if (!params.skip_blast) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:BLAST_BLASTN' { + withName: '.*:.*:ASSEMBLY_UNICYCLER:.*:BLAST_BLASTN' { ext.args = "-outfmt '6 stitle std slen qlen qcovs'" publishDir = [ path: { "${params.outdir}/assembly/unicycler/blastn" }, @@ -967,7 +967,7 @@ if (!params.skip_assembly) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:FILTER_BLASTN' { + withName: '.*:.*:ASSEMBLY_UNICYCLER:.*:FILTER_BLASTN' { ext.prefix = { "${meta.id}.filter.blastn" } publishDir = [ path: { "${params.outdir}/assembly/unicycler/blastn" }, @@ -980,7 +980,7 @@ if (!params.skip_assembly) { if (!params.skip_assembly_quast) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:QUAST' { + withName: '.*:.*:ASSEMBLY_UNICYCLER:.*:QUAST' { publishDir = [ path: { "${params.outdir}/assembly/unicycler" }, mode: 'copy', @@ -992,7 +992,7 @@ if (!params.skip_assembly) { if (!params.skip_abacas) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:ABACAS' { + withName: '.*:.*:ASSEMBLY_UNICYCLER:.*:ABACAS' { ext.args = '-m -p nucmer' publishDir = [ path: { "${params.outdir}/assembly/unicycler/abacas" }, @@ -1005,7 +1005,7 @@ if (!params.skip_assembly) { if (!params.skip_plasmidid) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_UNICYCLER:.*:PLASMIDID' { + withName: '.*:.*:ASSEMBLY_UNICYCLER:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ path: { "${params.outdir}/assembly/unicycler/plasmidid" }, @@ -1031,7 +1031,7 @@ if (!params.skip_assembly) { if (!params.skip_blast) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:BLAST_BLASTN' { + withName: '.*:.*:ASSEMBLY_MINIA:.*:BLAST_BLASTN' { ext.args = "-outfmt '6 stitle std slen qlen qcovs'" publishDir = [ path: { "${params.outdir}/assembly/minia/blastn" }, @@ -1040,7 +1040,7 @@ if (!params.skip_assembly) { ] } - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:FILTER_BLASTN' { + withName: '.*:.*:ASSEMBLY_MINIA:.*:FILTER_BLASTN' { ext.prefix = { "${meta.id}.filter.blastn" } publishDir = [ path: { "${params.outdir}/assembly/minia/blastn" }, @@ -1053,7 +1053,7 @@ if (!params.skip_assembly) { if (!params.skip_assembly_quast) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:QUAST' { + withName: '.*:.*:ASSEMBLY_MINIA:.*:QUAST' { publishDir = [ path: { "${params.outdir}/assembly/minia" }, mode: 'copy', @@ -1065,7 +1065,7 @@ if (!params.skip_assembly) { if (!params.skip_abacas) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:ABACAS' { + withName: '.*:.*:ASSEMBLY_MINIA:.*:ABACAS' { ext.args = '-m -p nucmer' publishDir = [ path: { "${params.outdir}/assembly/minia/abacas" }, @@ -1078,7 +1078,7 @@ if (!params.skip_assembly) { if (!params.skip_plasmidid) { process { - withName: 'NFCORE_VIRALRECON:ILLUMINA:ASSEMBLY_MINIA:.*:PLASMIDID' { + withName: '.*:.*:ASSEMBLY_MINIA:.*:PLASMIDID' { ext.args = '--only-reconstruct -C 47 -S 47 -i 60 --no-trim -k 0.80' publishDir = [ path: { "${params.outdir}/assembly/minia/plasmidid" }, diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 2e31cb98..c35d0f62 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -61,7 +61,7 @@ process { ] } - withName: 'NFCORE_VIRALRECON:NANOPORE:TABIX_TABIX' { + withName: '.*:.*:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}" }, @@ -70,7 +70,7 @@ process { ] } - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_VIEW' { + withName: '.*:.*:.*:SAMTOOLS_VIEW' { ext.args = '-b -F 4' ext.prefix = { "${meta.id}.mapped.sorted" } publishDir = [ @@ -80,7 +80,7 @@ process { ] } - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:SAMTOOLS_INDEX' { + withName: '.*:.*:.*:SAMTOOLS_INDEX' { ext.prefix = { "${meta.id}.mapped.sorted" } publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}" }, @@ -89,7 +89,7 @@ process { ] } - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:BAM_STATS_SAMTOOLS:.*' { + withName: '.*:.*:.*:BAM_STATS_SAMTOOLS:.*' { ext.prefix = { "${meta.id}.mapped.sorted" } publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/samtools_stats" }, @@ -98,7 +98,7 @@ process { ] } - withName: 'NFCORE_VIRALRECON:NANOPORE:BCFTOOLS_STATS' { + withName: '.*:.*:BCFTOOLS_STATS' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/bcftools_stats" }, mode: 'copy', @@ -275,7 +275,7 @@ if (!params.skip_snpeff) { ] } - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:.*:TABIX_BGZIP' { + withName: '.*:.*:.*:.*:TABIX_BGZIP' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, @@ -284,7 +284,7 @@ if (!params.skip_snpeff) { ] } - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:.*:.*:TABIX_TABIX' { + withName: '.*:.*:.*:.*:.*:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff" }, @@ -293,7 +293,7 @@ if (!params.skip_snpeff) { ] } - withName: 'NFCORE_VIRALRECON:NANOPORE:.*:.*:.*:BCFTOOLS_STATS' { + withName: '.*:.*:.*:.*:.*:BCFTOOLS_STATS' { ext.prefix = { "${meta.id}.snpeff" } publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/snpeff/bcftools_stats" }, From 87365b9a15ca248a365328ba827971ff89cef40e Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 17:37:20 +0000 Subject: [PATCH 128/238] Add tests for new caller options --- .github/workflows/ci.yml | 2 + conf/modules_illumina.config | 330 ++++++++++++----------------------- workflows/illumina.nf | 6 +- 3 files changed, 112 insertions(+), 226 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b58b90f..249864d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,8 @@ jobs: strategy: matrix: parameters: + - "--consensus_caller ivar" + - "--variant_caller bcftools --consensus_caller ivar" - "--skip_fastp" - "--skip_variants" - "--skip_cutadapt" diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 1736e38c..098d7c69 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -162,7 +162,7 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { + withName: '.*:.*:ALIGN_BOWTIE2:.*:SAMTOOLS_SORT' { ext.prefix = { "${meta.id}.sorted" } publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, @@ -171,7 +171,7 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { + withName: '.*:.*:ALIGN_BOWTIE2:.*:SAMTOOLS_INDEX' { publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, mode: 'copy', @@ -179,7 +179,7 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:ALIGN_BOWTIE2:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + withName: '.*:.*:ALIGN_BOWTIE2:.*:BAM_STATS_SAMTOOLS:.*' { publishDir = [ path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, mode: 'copy', @@ -195,32 +195,6 @@ if (!params.skip_variants) { } } - if (!params.skip_asciigenome) { - process { - withName: 'CUSTOM_GETCHROMSIZES' { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - } - } - - if (!params.skip_snpeff) { - process { - withName: 'SNPEFF_BUILD' { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - } - } - if (!params.skip_ivar_trim && params.protocol == 'amplicon') { process { withName: 'IVAR_TRIM' { @@ -237,7 +211,7 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_SORT' { + withName: '.*:.*:PRIMER_TRIM_IVAR:.*:SAMTOOLS_SORT' { ext.prefix = { "${meta.id}.ivar_trim.sorted" } publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, @@ -247,7 +221,7 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:SAMTOOLS_INDEX' { + withName: '.*:.*:PRIMER_TRIM_IVAR:.*:SAMTOOLS_INDEX' { publishDir = [ path: { "${params.outdir}/variants/bowtie2" }, mode: 'copy', @@ -256,7 +230,7 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:PRIMER_TRIM_IVAR:BAM_SORT_SAMTOOLS:BAM_STATS_SAMTOOLS:.*' { + withName: '.*:.*:PRIMER_TRIM_IVAR:.*:BAM_STATS_SAMTOOLS:.*' { publishDir = [ path: { "${params.outdir}/variants/bowtie2/samtools_stats" }, mode: 'copy', @@ -268,7 +242,7 @@ if (!params.skip_variants) { if (!params.skip_markduplicates) { process { - withName: '.*:MARK_DUPLICATES_PICARD:PICARD_MARKDUPLICATES' { + withName: 'PICARD_MARKDUPLICATES' { ext.args = [ 'ASSUME_SORTED=true VALIDATION_STRINGENCY=LENIENT TMP_DIR=tmp', params.filter_duplicates ? 'REMOVE_DUPLICATES=true' : '' @@ -426,229 +400,139 @@ if (!params.skip_variants) { ] } } - - if (!params.skip_asciigenome) { - process { - withName: '.*:.*:VARIANTS_IVAR:.*:ASCIIGENOME' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/asciigenome/${meta.id}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } + } - if (!params.skip_snpeff) { - process { - withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:SNPEFF_ANN' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff" }, + if (variant_caller == 'bcftools') { + process { + withName: 'BCFTOOLS_MPILEUP' { + ext.args = '--ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' + ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' + ext.args3 = "--include 'INFO/DP>=10'" + publishDir = [ + [ + path: { "${params.outdir}/variants/bcftools" }, mode: 'copy', - pattern: "*.{csv,txt,html}" - ] - } - - withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { - ext.prefix = { "${meta.id}.snpeff" } - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff" }, + pattern: '*.{gz,tbi}' + ], + [ + path: { "${params.outdir}/variants/bcftools" }, mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { - ext.args = '-p vcf -f' - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff" }, + pattern: '*.mpileup', + enabled: params.save_mpileup + ], + [ + path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + pattern: '*stats.txt' ] - } + ] + } + } + } - withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { - ext.prefix = { "${meta.id}.snpeff" } - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff/bcftools_stats" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + if (!params.skip_asciigenome) { + process { + withName: 'CUSTOM_GETCHROMSIZES' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } - withName: '.*:.*:VARIANTS_IVAR:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + withName: 'ASCIIGENOME' { + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}/asciigenome/${meta.id}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } } } - if (!params.skip_consensus && params.consensus_caller == 'ivar') { + if (!params.skip_snpeff) { process { - withName: 'IVAR_CONSENSUS' { - ext.args = '-t 0.75 -q 20 -m 10 -n N' - ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' - ext.prefix = { "${meta.id}.consensus" } + withName: 'SNPEFF_BUILD' { publishDir = [ - [ - path: { "${params.outdir}/variants/ivar/consensus" }, - mode: 'copy', - pattern: "*.{fa,txt}", - ], - [ - path: { "${params.outdir}/variants/ivar/consensus" }, - mode: 'copy', - pattern: "*.mpileup", - enabled: params.save_mpileup - ] + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference ] } - withName: 'MULTIQC_TSV_IVAR_NEXTCLADE' { + withName: 'SNPEFF_ANN' { publishDir = [ - path: { "${params.outdir}/multiqc" }, - enabled: false + path: { "${params.outdir}/variants/${variant_caller}/snpeff" }, + mode: 'copy', + pattern: "*.{csv,txt,html}" ] } - } - if (!params.skip_consensus_plots) { - process { - withName: '.*:.*:CONSENSUS_IVAR:.*:PLOT_BASE_DENSITY' { - ext.prefix = { "${meta.id}.consensus" } - publishDir = [ - path: { "${params.outdir}/variants/ivar/consensus/base_qc" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + withName: '.*:.*:.*:.*:SNPEFF_SNPSIFT:.*:TABIX_BGZIP' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - } - if (!params.skip_pangolin) { - process { - withName: '.*:.*:CONSENSUS_IVAR:.*:PANGOLIN' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/pangolin" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + withName: '.*:.*:.*:.*:SNPEFF_SNPSIFT:.*:.*:TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - } - if (!params.skip_nextclade) { - process { - withName: '.*:.*:CONSENSUS_IVAR:.*:NEXTCLADE_RUN' { - publishDir = [ - path: { "${params.outdir}/variants/ivar/nextclade" }, - mode: 'copy', - pattern: "*.csv" - ] - } + withName: '.*:.*:.*:.*:SNPEFF_SNPSIFT:.*:.*:BCFTOOLS_STATS' { + ext.prefix = { "${meta.id}.snpeff" } + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}/snpeff/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } - } - if (!params.skip_variants_quast) { - process { - withName: '.*:.*:CONSENSUS_IVAR:.*:QUAST' { - publishDir = [ - path: { "${params.outdir}/variants/ivar" }, - mode: 'copy', - pattern: "quast" - ] - } + withName: '.*:.*:.*:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}/snpeff" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } } } - if (variant_caller == 'bcftools') { + if (!params.skip_consensus && params.consensus_caller == 'ivar') { process { - withName: 'BCFTOOLS_MPILEUP' { - ext.args = '--ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' - ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' - ext.args3 = "--include 'INFO/DP>=10'" + withName: 'IVAR_CONSENSUS' { + ext.args = '-t 0.75 -q 20 -m 10 -n N' + ext.args2 = '--count-orphans --no-BAQ --max-depth 0 --min-BQ 0 -aa' + ext.prefix = { "${meta.id}.consensus" } publishDir = [ [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/ivar/consensus" }, mode: 'copy', - pattern: '*.{gz,tbi}' + pattern: "*.{fa,txt}", ], [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/ivar/consensus" }, mode: 'copy', - pattern: '*.mpileup', + pattern: "*.mpileup", enabled: params.save_mpileup - ], - [ - path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, - mode: 'copy', - pattern: '*stats.txt' ] ] } - } - - if (!params.skip_asciigenome) { - process { - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:ASCIIGENOME' { - publishDir = [ - path: { "${params.outdir}/variants/bcftools/asciigenome/${meta.id}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - - if (!params.skip_snpeff) { - process { - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:SNPEFF_ANN' { - publishDir = [ - path: { "${params.outdir}/variants/bcftools/snpeff" }, - mode: 'copy', - pattern: "*.{csv,txt,html}" - ] - } - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:TABIX_BGZIP' { - ext.prefix = { "${meta.id}.snpeff" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:TABIX_TABIX' { - ext.args = '-p vcf -f' - publishDir = [ - path: { "${params.outdir}/variants/bcftools/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:VCF_BGZIP_TABIX_STATS:VCF_TABIX_STATS:BCFTOOLS_STATS' { - ext.prefix = { "${meta.id}.snpeff" } - publishDir = [ - path: { "${params.outdir}/variants/bcftools/snpeff/bcftools_stats" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:SNPEFF_SNPSIFT:SNPSIFT_EXTRACTFIELDS' { - publishDir = [ - path: { "${params.outdir}/variants/bcftools/snpeff" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } + withName: 'MULTIQC_TSV_IVAR_NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/multiqc" }, + enabled: false + ] } } } @@ -699,12 +583,14 @@ if (!params.skip_variants) { ] } } + } + if (!params.skip_consensus) { if (!params.skip_pangolin) { process { - withName: '.*:.*:CONSENSUS_BCFTOOLS:.*:PANGOLIN' { + withName: 'PANGOLIN' { publishDir = [ - path: { "${params.outdir}/variants/bcftools/pangolin" }, + path: { "${params.outdir}/variants/${params.consensus_caller}/pangolin" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] @@ -714,9 +600,9 @@ if (!params.skip_variants) { if (!params.skip_nextclade) { process { - withName: '.*:.*:CONSENSUS_BCFTOOLS:.*:NEXTCLADE_RUN' { + withName: 'NEXTCLADE_RUN' { publishDir = [ - path: { "${params.outdir}/variants/bcftools/nextclade" }, + path: { "${params.outdir}/variants/${params.consensus_caller}/nextclade" }, mode: 'copy', pattern: "*.csv" ] @@ -726,9 +612,9 @@ if (!params.skip_variants) { if (!params.skip_variants_quast) { process { - withName: '.*:.*:CONSENSUS_BCFTOOLS:.*:QUAST' { + withName: '.*:.*:CONSENSUS_.*:.*:QUAST' { publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/${params.consensus_caller}" }, mode: 'copy', pattern: "quast" ] @@ -738,10 +624,10 @@ if (!params.skip_variants) { if (!params.skip_consensus_plots) { process { - withName: '.*:.*:CONSENSUS_BCFTOOLS:.*:PLOT_BASE_DENSITY' { + withName: 'PLOT_BASE_DENSITY' { ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/bcftools/consensus/base_qc" }, + path: { "${params.outdir}/variants/${params.consensus_caller}/consensus/base_qc" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] diff --git a/workflows/illumina.nf b/workflows/illumina.nf index 7d6af82b..c9ec299e 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -413,8 +413,6 @@ workflow ILLUMINA { // // SUBWORKFLOW: Call variants with BCFTools // - ch_vcf = Channel.empty() - ch_tbi = Channel.empty() ch_bcftools_stats_multiqc = Channel.empty() ch_bcftools_snpeff_multiqc = Channel.empty() if (!params.skip_variants && variant_caller == 'bcftools') { @@ -451,7 +449,7 @@ workflow ILLUMINA { ch_ivar_pangolin_multiqc = CONSENSUS_IVAR.out.pangolin_report ch_ivar_nextclade_report = CONSENSUS_IVAR.out.nextclade_report ch_versions = ch_versions.mix(CONSENSUS_IVAR.out.versions) - + // // MODULE: Get Nextclade clade information for MultiQC report // @@ -470,7 +468,7 @@ workflow ILLUMINA { .set { ch_ivar_nextclade_multiqc } } - // + // // SUBWORKFLOW: Call consensus with BCFTools // ch_bcftools_quast_multiqc = Channel.empty() From 9019c0dddbbf7178ed40dd271bfae863a376d8f7 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 18:09:43 +0000 Subject: [PATCH 129/238] Change publishDir by consensus caller --- conf/modules_illumina.config | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 098d7c69..4fe305f7 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -515,12 +515,12 @@ if (!params.skip_variants) { ext.prefix = { "${meta.id}.consensus" } publishDir = [ [ - path: { "${params.outdir}/variants/ivar/consensus" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/ivar" }, mode: 'copy', pattern: "*.{fa,txt}", ], [ - path: { "${params.outdir}/variants/ivar/consensus" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/ivar" }, mode: 'copy', pattern: "*.mpileup", enabled: params.save_mpileup @@ -544,7 +544,7 @@ if (!params.skip_variants) { ext.args2 = 10 ext.prefix = { "${meta.id}.coverage.masked" } publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, mode: 'copy', pattern: "*.mpileup", enabled: params.save_mpileup @@ -554,7 +554,7 @@ if (!params.skip_variants) { withName: 'BEDTOOLS_MERGE' { ext.prefix = { "${meta.id}.coverage.merged" } publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, enabled: false ] } @@ -562,7 +562,7 @@ if (!params.skip_variants) { withName: 'BEDTOOLS_MASKFASTA' { ext.prefix = { "${meta.id}.masked" } publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, enabled: false ] } @@ -570,7 +570,7 @@ if (!params.skip_variants) { withName: 'BCFTOOLS_CONSENSUS' { ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/bcftools/consensus" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] @@ -590,7 +590,7 @@ if (!params.skip_variants) { process { withName: 'PANGOLIN' { publishDir = [ - path: { "${params.outdir}/variants/${params.consensus_caller}/pangolin" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/${params.consensus_caller}/pangolin" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] @@ -602,7 +602,7 @@ if (!params.skip_variants) { process { withName: 'NEXTCLADE_RUN' { publishDir = [ - path: { "${params.outdir}/variants/${params.consensus_caller}/nextclade" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/${params.consensus_caller}/nextclade" }, mode: 'copy', pattern: "*.csv" ] @@ -614,7 +614,7 @@ if (!params.skip_variants) { process { withName: '.*:.*:CONSENSUS_.*:.*:QUAST' { publishDir = [ - path: { "${params.outdir}/variants/${params.consensus_caller}" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/${params.consensus_caller}" }, mode: 'copy', pattern: "quast" ] @@ -627,7 +627,7 @@ if (!params.skip_variants) { withName: 'PLOT_BASE_DENSITY' { ext.prefix = { "${meta.id}.consensus" } publishDir = [ - path: { "${params.outdir}/variants/${params.consensus_caller}/consensus/base_qc" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/${params.consensus_caller}/base_qc" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] From 0a51f5bbbe19573301dfeef5bd626fb0b9f43d3c Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 18:20:07 +0000 Subject: [PATCH 130/238] Push some updates to output docs --- docs/output.md | 60 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/output.md b/docs/output.md index 4be3d50d..699e3c7b 100644 --- a/docs/output.md +++ b/docs/output.md @@ -512,19 +512,19 @@ Unless you are using [UMIs](https://emea.illumina.com/science/sequencing-method- * `*.tsv`: Original iVar variants in TSV format. * `*.vcf.gz`: iVar variants in VCF format. Converted using custom `ivar_variants_to_vcf.py` python script. * `*.vcf.gz.tbi`: iVar variants in VCF index file. -* `variants/ivar/consensus/` +* `variants/ivar/log/` + * `*.variant_counts.log`: Counts for type of variants called by iVar. +* `variants/ivar/bcftools_stats/` + * `*.bcftools_stats.txt`: Statistics and counts obtained from iVar variants VCF file. +* `variants//consensus/ivar/` * `*.consensus.fa`: Consensus Fasta file generated by iVar. * `*.consensus.qual.txt`: File with the average quality of each base in the consensus sequence. -* `variants/ivar/consensus/base_qc/` +* `variants//consensus/ivar/base_qc/` * `*.ACTG_density.pdf`: Plot showing density of ACGT bases within the consensus sequence. * `*.base_counts.pdf`: Plot showing frequency and percentages of all bases in consensus sequence. * `*.base_counts.tsv`: File containing frequency and percentages of all bases in consensus sequence. * `*.N_density.pdf`: Plot showing density of N bases within the consensus sequence. * `*.N_run.tsv`: File containing start positions and width of N bases in consensus sequence. -* `variants/ivar/log/` - * `*.variant_counts.log`: Counts for type of variants called by iVar. -* `variants/ivar/bcftools_stats/` - * `*.bcftools_stats.txt`: Statistics and counts obtained from iVar variants VCF file. @@ -542,16 +542,16 @@ iVar outputs a tsv format, which is not compatible with downstream analysis such * `variants/bcftools/` * `*.vcf.gz`: Variants VCF file. * `*.vcf.gz.tbi`: Variants VCF index file. -* `variants/bcftools/consensus/` +* `variants/bcftools/bcftools_stats/` + * `*.bcftools_stats.txt`: Statistics and counts obtained from VCF file. +* `variants//consensus/bcftools/` * `*.consensus.fa`: Consensus Fasta file generated by integrating the variants called by BCFTools into the reference genome. -* `variants/bcftools/consensus/base_qc/` +* `variants//consensus/bcftools/base_qc/` * `*.ACTG_density.pdf`: Plot showing density of ACGT bases within the consensus sequence. * `*.base_counts.pdf`: Plot showing frequency and percentages of all bases in consensus sequence. * `*.base_counts.tsv`: File containing frequency and percentages of all bases in consensus sequence. * `*.N_density.pdf`: Plot showing density of N bases within the consensus sequence. * `*.N_run.tsv`: File containing start positions and width of N bases in consensus sequence. -* `variants/bcftools/bcftools_stats/` - * `*.bcftools_stats.txt`: Statistics and counts obtained from VCF file. @@ -588,63 +588,63 @@ iVar outputs a tsv format, which is not compatible with downstream analysis such ![MultiQC - SnpEff annotation counts](images/mqc_snpeff_plot.png) -### QUAST +### ASCIIGenome
Output files -* `variants//quast/` - * `report.html`: Results report in HTML format. Also available in various other file formats i.e. `report.pdf`, `report.tex`, `report.tsv` and `report.txt`. +* `variants//asciigenome//` + * `*.pdf`: Individual variant screenshots with annotation tracks in PDF format. **NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic').
-[QUAST](http://bioinf.spbau.ru/quast) is used to generate a single report with which to evaluate the quality of the consensus sequence across all of the samples provided to the pipeline. The HTML results can be opened within any browser (we recommend using Google Chrome). Please see the [QUAST output docs](http://quast.sourceforge.net/docs/manual.html#sec3) for more detailed information regarding the output files. +As described in the documentation, [ASCIIGenome](https://asciigenome.readthedocs.io/en/latest/) is a command-line genome browser that can be run from a terminal window and is solely based on ASCII characters. The closest program to ASCIIGenome is probably [samtools tview](http://www.htslib.org/doc/samtools-tview.html) but ASCIIGenome offers much more flexibility, similar to popular GUI viewers like the [IGV](https://software.broadinstitute.org/software/igv/) browser. We are using the batch processing mode of ASCIIGenome in this pipeline to generate individual screenshots for all of the variant sites reported for each sample in the VCF files. This is incredibly useful to be able to quickly QC the variants called by the pipeline without having to tediously load all of the relevant tracks into a conventional genome browser. Where possible, the BAM read alignments, VCF variant file, primer BED file and GFF annotation track will be represented in the screenshot for contextual purposes. The screenshot below shows a SNP called relative to the MN908947.3 SARS-CoV-2 reference genome that overlaps the ORF7a protein and the nCoV-2019_91_LEFT primer from the ARIC v3 protocol. -### Pangolin +

ASCIIGenome screenshot

+ +### QUAST
Output files -* `variants//pangolin/` - * `*.pangolin.csv`: Lineage analysis results from Pangolin. +* `variants//consensus//quast/` + * `report.html`: Results report in HTML format. Also available in various other file formats i.e. `report.pdf`, `report.tex`, `report.tsv` and `report.txt`. **NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic').
-Phylogenetic Assignment of Named Global Outbreak LINeages ([Pangolin](https://github.com/cov-lineages/pangolin)) has been used extensively during the COVID-19 pandemic in order to to assign lineages to SARS-CoV-2 genome sequenced samples. A [web application](https://pangolin.cog-uk.io/) also exists that allows users to upload genome sequences via a web browser to assign lineages to genome sequences of SARS-CoV-2, view descriptive characteristics of the assigned lineage(s), view the placement of the lineage in a phylogeny of global samples, and view the temporal and geographic distribution of the assigned lineage(s). +[QUAST](http://bioinf.spbau.ru/quast) is used to generate a single report with which to evaluate the quality of the consensus sequence across all of the samples provided to the pipeline. The HTML results can be opened within any browser (we recommend using Google Chrome). Please see the [QUAST output docs](http://quast.sourceforge.net/docs/manual.html#sec3) for more detailed information regarding the output files. -### Nextclade +### Pangolin
Output files -* `variants//nextclade/` - * `*.csv`: Analysis results from Nextlade containing genome clade assignment, mutation calling and sequence quality checks. - -**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +* `variants//consensus//pangolin/` + * `*.pangolin.csv`: Lineage analysis results from Pangolin.
-[Nextclade](https://github.com/nextstrain/nextclade) performs viral genome clade assignment, mutation calling and sequence quality checks for the consensus sequences generated in this pipeline. Similar to Pangolin, it has been used extensively during the COVID-19 pandemic. A [web application](https://clades.nextstrain.org/) also exists that allows users to upload genome sequences via a web browser. +Phylogenetic Assignment of Named Global Outbreak LINeages ([Pangolin](https://github.com/cov-lineages/pangolin)) has been used extensively during the COVID-19 pandemic in order to to assign lineages to SARS-CoV-2 genome sequenced samples. A [web application](https://pangolin.cog-uk.io/) also exists that allows users to upload genome sequences via a web browser to assign lineages to genome sequences of SARS-CoV-2, view descriptive characteristics of the assigned lineage(s), view the placement of the lineage in a phylogeny of global samples, and view the temporal and geographic distribution of the assigned lineage(s). -### ASCIIGenome +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). + +### Nextclade
Output files -* `variants//asciigenome//` - * `*.pdf`: Individual variant screenshots with annotation tracks in PDF format. +* `variants//consensus//nextclade/` + * `*.csv`: Analysis results from Nextlade containing genome clade assignment, mutation calling and sequence quality checks. **NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic').
-As described in the documentation, [ASCIIGenome](https://asciigenome.readthedocs.io/en/latest/) is a command-line genome browser that can be run from a terminal window and is solely based on ASCII characters. The closest program to ASCIIGenome is probably [samtools tview](http://www.htslib.org/doc/samtools-tview.html) but ASCIIGenome offers much more flexibility, similar to popular GUI viewers like the [IGV](https://software.broadinstitute.org/software/igv/) browser. We are using the batch processing mode of ASCIIGenome in this pipeline to generate individual screenshots for all of the variant sites reported for each sample in the VCF files. This is incredibly useful to be able to quickly QC the variants called by the pipeline without having to tediously load all of the relevant tracks into a conventional genome browser. Where possible, the BAM read alignments, VCF variant file, primer BED file and GFF annotation track will be represented in the screenshot for contextual purposes. The screenshot below shows a SNP called relative to the MN908947.3 SARS-CoV-2 reference genome that overlaps the ORF7a protein and the nCoV-2019_91_LEFT primer from the ARIC v3 protocol. - -

ASCIIGenome screenshot

+[Nextclade](https://github.com/nextstrain/nextclade) performs viral genome clade assignment, mutation calling and sequence quality checks for the consensus sequences generated in this pipeline. Similar to Pangolin, it has been used extensively during the COVID-19 pandemic. A [web application](https://clades.nextstrain.org/) also exists that allows users to upload genome sequences via a web browser. ## Illumina: De novo assembly From f38b236a8451260fbf92d6140c74215b09f448d8 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 18:22:25 +0000 Subject: [PATCH 131/238] Update output docs --- docs/output.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/output.md b/docs/output.md index 699e3c7b..afa08c3b 100644 --- a/docs/output.md +++ b/docs/output.md @@ -553,6 +553,8 @@ iVar outputs a tsv format, which is not compatible with downstream analysis such * `*.N_density.pdf`: Plot showing density of N bases within the consensus sequence. * `*.N_run.tsv`: File containing start positions and width of N bases in consensus sequence. +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). + [BCFtools](http://samtools.github.io/bcftools/bcftools.html) can be used to call variants directly from BAM alignment files. The functionality to call variants with BCFTools in this pipeline was inspired by work carried out by [Conor Walker](https://github.com/conorwalker/covid19/blob/3cb26ec399417bedb7e60487415c78a405f517d6/scripts/call_variants.sh). From 2970912685d08f4accf08bdda9d615ddea635b9c Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 18:28:11 +0000 Subject: [PATCH 132/238] Fix ECLint --- subworkflows/local/consensus_bcftools.nf | 2 +- subworkflows/local/consensus_ivar.nf | 4 ++-- subworkflows/local/consensus_qc.nf | 4 ++-- subworkflows/local/variants_ivar.nf | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf index 14cefb2e..7db20187 100644 --- a/subworkflows/local/consensus_bcftools.nf +++ b/subworkflows/local/consensus_bcftools.nf @@ -69,7 +69,7 @@ workflow CONSENSUS_BCFTOOLS { emit: consensus = BCFTOOLS_CONSENSUS.out.fasta // channel: [ val(meta), [ fasta ] ] - + quast_results = CONSENSUS_QC.out.quast_results // channel: [ val(meta), [ results ] ] quast_tsv = CONSENSUS_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] diff --git a/subworkflows/local/consensus_ivar.nf b/subworkflows/local/consensus_ivar.nf index b3de4d94..15fc326e 100644 --- a/subworkflows/local/consensus_ivar.nf +++ b/subworkflows/local/consensus_ivar.nf @@ -40,7 +40,7 @@ workflow CONSENSUS_IVAR { emit: consensus = IVAR_CONSENSUS.out.fasta // channel: [ val(meta), [ fasta ] ] consensus_qual = IVAR_CONSENSUS.out.qual // channel: [ val(meta), [ qual.txt ] ] - + quast_results = CONSENSUS_QC.out.quast_results // channel: [ val(meta), [ results ] ] quast_tsv = CONSENSUS_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] @@ -52,4 +52,4 @@ workflow CONSENSUS_IVAR { bases_pdf = CONSENSUS_QC.out.bases_pdf // channel: [ val(meta), [ pdf ] ] versions = ch_versions // channel: [ versions.yml ] -} \ No newline at end of file +} diff --git a/subworkflows/local/consensus_qc.nf b/subworkflows/local/consensus_qc.nf index d370f68b..8b24e60e 100644 --- a/subworkflows/local/consensus_qc.nf +++ b/subworkflows/local/consensus_qc.nf @@ -13,7 +13,7 @@ workflow CONSENSUS_QC { fasta // channel: /path/to/genome.fasta gff // channel: /path/to/genome.gff nextclade_db // channel: /path/to/nextclade_db/ - + main: ch_versions = Channel.empty() @@ -87,4 +87,4 @@ workflow CONSENSUS_QC { bases_pdf = ch_bases_pdf // channel: [ val(meta), [ pdf ] ] versions = ch_versions // channel: [ versions.yml ] -} \ No newline at end of file +} diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index 15a10e4e..c4bf2eeb 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -85,4 +85,4 @@ workflow VARIANTS_IVAR { asciigenome_pdf = VARIANTS_QC.out.asciigenome_pdf // channel: [ val(meta), [ pdf ] ] versions = ch_versions // channel: [ versions.yml ] -} \ No newline at end of file +} From 445b636692359e3065c34512b65f06b78e1089e7 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 25 Jan 2022 21:12:12 +0000 Subject: [PATCH 133/238] Add Anthony as contributor --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 47b8e5d4..98e5dc50 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ Many thanks to others who have helped out and contributed along the way too, inc | [Aengus Stewart](https://github.com/stewarta) | [The Francis Crick Institute, UK](https://www.crick.ac.uk/) | | [Alexander Peltzer](https://github.com/apeltzer) | [Boehringer Ingelheim, Germany](https://www.boehringer-ingelheim.de/) | | [Alison Meynert](https://github.com/ameynert) | [University of Edinburgh, Scotland](https://www.ed.ac.uk/) | +| [Anthony Underwood](https://github.com/antunderwood) | [Centre for Genomic Pathogen Surveillance](https://www.pathogensurveillance.net) | | [Anton Korobeynikov](https://github.com/asl) | [Saint Petersburg State University, Russia](https://english.spbu.ru/) | | [Artem Babaian](https://github.com/ababaian) | [University of British Columbia, Canada](https://www.ubc.ca/) | | [Dmitry Meleshko](https://github.com/1dayac) | [Saint Petersburg State University, Russia](https://english.spbu.ru/) | From c6cc00d02f11fb54a8828eb04aceb9f37b551986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Wed, 26 Jan 2022 13:52:20 +0100 Subject: [PATCH 134/238] Fix if folder does not exist, fix blank lines --- bin/parser_ivar_bcftools.py | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/bin/parser_ivar_bcftools.py b/bin/parser_ivar_bcftools.py index e0410a62..33717b01 100755 --- a/bin/parser_ivar_bcftools.py +++ b/bin/parser_ivar_bcftools.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + from matplotlib import table import pandas as pd import sys @@ -6,6 +8,7 @@ import argparse import glob, os import io + pd.set_option('display.max_columns', None) pd.set_option('display.max_rows', None) @@ -14,8 +17,9 @@ os.chdir(cwd) #create SampleTables folder in the folder where the script is running -sample_table_path = os.path.join(cwd, "SampleTables") -os.mkdir(sample_table_path) +if not os.path.isdir("SampleTables"): + sample_table_path = os.path.join(cwd, "SampleTables") + os.mkdir(sample_table_path) def parser_args(args=None): Description = 'Create long/wide tables fo ivar/bcftools' @@ -30,18 +34,17 @@ def parser_args(args=None): def create_long(in_table,snp_table,pango_table,counter,software): - if not in_table: intable_pathname = cwd else: intable_pathname = os.path.join(cwd, in_table) + os.chdir(intable_pathname) table_list = [] for file in glob.glob("*_norm.table"): table_list.append(file) table_list.sort() - snp_pathname = os.path.join(cwd, snp_table) os.chdir(snp_pathname) snp_list = [] @@ -92,8 +95,6 @@ def create_long(in_table,snp_table,pango_table,counter,software): for j in range(3,8): snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] - - #format of lineages pangolin_table = pd.read_csv(str(pango_pathname) + '/' + pangolin_list[counter], sep=",", header = "infer") lineages = pangolin_table.loc[:,['taxon','lineage']] @@ -109,8 +110,6 @@ def create_long(in_table,snp_table,pango_table,counter,software): return tl_onesample,snpsift_table_copy,counter - - def mergetables(sample_intable,snp_intable,path,counter): if not path: @@ -132,13 +131,11 @@ def mergetables(sample_intable,snp_intable,path,counter): return(merged_table_long) def loop(path_in): - if not path_in: in_pathname = cwd else: in_pathname = os.path.join(cwd, path_in) - os.chdir(in_pathname) table_list = [] for file in glob.glob("*.table"): @@ -157,13 +154,11 @@ def concatenatetable(path_in_concat): else: concat_pathname = os.path.join(cwd,path_in_concat,'SampleTables') - os.chdir(concat_pathname) extension = 'csv' all_filenames = [i for i in glob.glob('*.{}'.format(extension))] - #combine all files in the list #combined_csv = pd.concat([pd.read_csv(f) for f in all_filenames ]) list_of_dataframes = [] @@ -174,20 +169,11 @@ def concatenatetable(path_in_concat): merged_df.to_csv("final_long_table.csv", index=False, encoding='utf-8-sig') return merged_df - - - def main(args=None): - - - args = parser_args(args) - - length = loop(args.sample) for i in range(0,length): - tl_onesample_out,snpsift_table_final,counter_it = create_long(args.sample,args.snpsift, args.pangolin,i,args.software) merged = mergetables(tl_onesample_out,snpsift_table_final,args.sample,counter_it) From 3ebe1258535c485983a523d3982294511d7cb59a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Wed, 26 Jan 2022 14:48:34 +0100 Subject: [PATCH 135/238] fix file and path handling --- bin/parser_ivar_bcftools.py | 123 +++++++++++++++--------------------- 1 file changed, 52 insertions(+), 71 deletions(-) diff --git a/bin/parser_ivar_bcftools.py b/bin/parser_ivar_bcftools.py index 33717b01..62455065 100755 --- a/bin/parser_ivar_bcftools.py +++ b/bin/parser_ivar_bcftools.py @@ -33,55 +33,30 @@ def parser_args(args=None): return parser.parse_args(args) -def create_long(in_table,snp_table,pango_table,counter,software): - if not in_table: - intable_pathname = cwd - else: - intable_pathname = os.path.join(cwd, in_table) - - os.chdir(intable_pathname) - table_list = [] - for file in glob.glob("*_norm.table"): - table_list.append(file) - table_list.sort() - - snp_pathname = os.path.join(cwd, snp_table) - os.chdir(snp_pathname) - snp_list = [] - for file in glob.glob("*_norm.snpsift.txt"): - snp_list.append(file) - snp_list.sort() - - pango_pathname = os.path.join(cwd, pango_table) - os.chdir(pango_pathname) - pangolin_list = [] - for file in glob.glob("*.csv"): - pangolin_list.append(file) - pangolin_list.sort() +def create_long(snp_file,snpsift_file,pangolin_file,software): - ### format of sample table - sample_table=pd.read_table(str(intable_pathname) + "/" + table_list[counter], header='infer') - sample_table = sample_table.dropna(how = 'all', axis =1) + ### format of sample table + snp_table=pd.read_table(snp_file, header='infer') + snp_table = snp_table.dropna(how = 'all', axis =1) if software=='bcftools': - sample_table.rename(columns={sample_table.columns[5]: "DP",sample_table.columns[6]: "AD"}, inplace=True) - - new_column = sample_table - new_column[['REF_DP','ALT_DP']] = sample_table['AD'].str.split(',', expand=True) - sample_table = pd.merge(sample_table,new_column,how = 'left') - sample_table[["ALT_DP", "DP"]] = sample_table[["ALT_DP", "DP"]].apply(pd.to_numeric) - sample_table['AF']=sample_table['ALT_DP']/sample_table['DP'] - sample_table['AF'] = sample_table['AF'].round(2) - sample_table = sample_table.loc[:, ~sample_table.columns.str.contains('AD')] + snp_table.rename(columns={snp_table.columns[5]: "DP",snp_table.columns[6]: "AD"}, inplace=True) + new_column = snp_table + new_column[['REF_DP','ALT_DP']] = snp_table['AD'].str.split(',', expand=True) + snp_table = pd.merge(snp_table,new_column,how = 'left') + snp_table[["ALT_DP", "DP"]] = snp_table[["ALT_DP", "DP"]].apply(pd.to_numeric) + snp_table['AF']=snp_table['ALT_DP']/snp_table['DP'] + snp_table['AF'] = snp_table['AF'].round(2) + snp_table = snp_table.loc[:, ~snp_table.columns.str.contains('AD')] elif software=='ivar': - sample_table.rename(columns={sample_table.columns[0]: "CHROM",sample_table.columns[1]: "POS",sample_table.columns[2]: "REF",sample_table.columns[3]: "ALT",sample_table.columns[4]: "FILTER",sample_table.columns[5]: "DP",sample_table.columns[6]: "REF_DP", sample_table.columns[7]: "ALT_DP"}, inplace=True) - sample_table[["ALT_DP", "DP"]] = sample_table[["ALT_DP", "DP"]].apply(pd.to_numeric) - sample_table['AF']=sample_table['ALT_DP']/sample_table['DP'] - sample_table['AF'] = sample_table['AF'].round(2) + snp_table.rename(columns={snp_table.columns[0]: "CHROM",snp_table.columns[1]: "POS",snp_table.columns[2]: "REF",snp_table.columns[3]: "ALT",snp_table.columns[4]: "FILTER",snp_table.columns[5]: "DP",snp_table.columns[6]: "REF_DP", snp_table.columns[7]: "ALT_DP"}, inplace=True) + snp_table[["ALT_DP", "DP"]] = snp_table[["ALT_DP", "DP"]].apply(pd.to_numeric) + snp_table['AF']=snp_table['ALT_DP']/snp_table['DP'] + snp_table['AF'] = snp_table['AF'].round(2) ### format of snpsift table - snpsift_table = pd.read_csv(str(snp_pathname) + '/' + snp_list[counter], sep="\t", header = "infer") + snpsift_table = pd.read_csv(snpsift_file, sep="\t", header = "infer") snpsift_table = snpsift_table.loc[:, ~snpsift_table.columns.str.contains('^Unnamed')] colnames_snpsift = list(snpsift_table.columns) colnames_snpsift = [i.replace('ANN[*].', '') for i in colnames_snpsift] @@ -96,11 +71,11 @@ def create_long(in_table,snp_table,pango_table,counter,software): snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] #format of lineages - pangolin_table = pd.read_csv(str(pango_pathname) + '/' + pangolin_list[counter], sep=",", header = "infer") + pangolin_table = pd.read_csv(pangolin_file, sep=",", header = "infer") lineages = pangolin_table.loc[:,['taxon','lineage']] #table long one sample - tl_onesample = pd.DataFrame(data =sample_table) + tl_onesample = pd.DataFrame(data =snp_table) if software=='bcftools': tl_onesample["Sample"] = lineages.iloc[0,0] elif software=='ivar': @@ -108,14 +83,14 @@ def create_long(in_table,snp_table,pango_table,counter,software): tl_onesample["software"] = software tl_onesample["Lineage"] = lineages.iloc[0,1] - return tl_onesample,snpsift_table_copy,counter + return tl_onesample,snpsift_table_copy def mergetables(sample_intable,snp_intable,path,counter): - if not path: - merge_pathname = cwd - else: - merge_pathname = os.path.join(cwd, path) +# if not path: +# merge_pathname = cwd +# else: +# merge_pathname = os.path.join(cwd, path) os.chdir(merge_pathname) table_list = [] @@ -126,26 +101,11 @@ def mergetables(sample_intable,snp_intable,path,counter): left = snp_intable right = sample_intable merged_table_long = pd.merge(left,right,how = 'outer') - merged_table_long.to_csv(str(merge_pathname) + '/SampleTables/'+ table_list[counter] + '.csv', header='infer', index=None, sep=' ', mode='a') return(merged_table_long) -def loop(path_in): - if not path_in: - in_pathname = cwd - else: - in_pathname = os.path.join(cwd, path_in) - - os.chdir(in_pathname) - table_list = [] - for file in glob.glob("*.table"): - table_list.append(file) - table_list.sort() - - length = len(table_list) - - return length - +def same_len(list): + return len(set(list)) == 1 def concatenatetable(path_in_concat): @@ -171,14 +131,35 @@ def concatenatetable(path_in_concat): def main(args=None): args = parser_args(args) - length = loop(args.sample) - for i in range(0,length): - tl_onesample_out,snpsift_table_final,counter_it = create_long(args.sample,args.snpsift, args.pangolin,i,args.software) - merged = mergetables(tl_onesample_out,snpsift_table_final,args.sample,counter_it) + # List vcf table files + table_list = [] + for file in glob.glob(args.sample + "/*_norm.table"): + table_list.append(file) + table_list.sort() + + #List snpsift files + snpsift_list = [] + for file in glob.glob(args.snpsift + "/*_norm.snpsift.txt"): + snpsift_list.append(file) + snpsift_list.sort() + + # List pangolin files + pangolin_list = [] + for file in glob.glob(args.pangolin + "/*.csv"): + pangolin_list.append(file) + pangolin_list.sort() + + if not same_len([len(table_list),len(snpsift_list),len(pangolin_list)]): + print("not same number of files for variants, snpsift and pangolin results ") + exit() - merged_df= concatenatetable(args.sample) + sample_names = [os.path.basename(filename).split("_norm.table")[0] for filename in table_list] + for table,snpsift,pangolin in zip(table_list,snpsift_list,pangolin_list): + tl_onesample_out,snpsift_table_final = create_long(table,snpsift,pangolin,args.software) + #merged = mergetables(tl_onesample_out,snpsift_table_final,args.sample,counter_it) +# merged_df= concatenatetable(args.sample) if __name__ == '__main__': sys.exit(main()) From 607223670f3f35ce5a5373a8441780d9265606de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Wed, 26 Jan 2022 15:14:29 +0100 Subject: [PATCH 136/238] final fixes for everything to work --- bin/parser_ivar_bcftools.py | 68 +++++++++++-------------------------- 1 file changed, 19 insertions(+), 49 deletions(-) diff --git a/bin/parser_ivar_bcftools.py b/bin/parser_ivar_bcftools.py index 62455065..4cdbe4ed 100755 --- a/bin/parser_ivar_bcftools.py +++ b/bin/parser_ivar_bcftools.py @@ -12,14 +12,6 @@ pd.set_option('display.max_columns', None) pd.set_option('display.max_rows', None) -#Current working directory -cwd = os.getcwd() -os.chdir(cwd) - -#create SampleTables folder in the folder where the script is running -if not os.path.isdir("SampleTables"): - sample_table_path = os.path.join(cwd, "SampleTables") - os.mkdir(sample_table_path) def parser_args(args=None): Description = 'Create long/wide tables fo ivar/bcftools' @@ -83,50 +75,20 @@ def create_long(snp_file,snpsift_file,pangolin_file,software): tl_onesample["software"] = software tl_onesample["Lineage"] = lineages.iloc[0,1] - return tl_onesample,snpsift_table_copy - -def mergetables(sample_intable,snp_intable,path,counter): - -# if not path: -# merge_pathname = cwd -# else: -# merge_pathname = os.path.join(cwd, path) - - os.chdir(merge_pathname) - table_list = [] - for file in glob.glob("*.table"): - table_list.append(file[0:6]) - table_list.sort() + merged_table_long = pd.merge(tl_onesample,snpsift_table_copy,how = 'outer') - left = snp_intable - right = sample_intable - merged_table_long = pd.merge(left,right,how = 'outer') - merged_table_long.to_csv(str(merge_pathname) + '/SampleTables/'+ table_list[counter] + '.csv', header='infer', index=None, sep=' ', mode='a') return(merged_table_long) def same_len(list): return len(set(list)) == 1 -def concatenatetable(path_in_concat): - - if not path_in_concat: - concat_pathname = os.path.join(cwd, 'SampleTables') - else: - concat_pathname = os.path.join(cwd,path_in_concat,'SampleTables') - - os.chdir(concat_pathname) - - extension = 'csv' - all_filenames = [i for i in glob.glob('*.{}'.format(extension))] +def concatenatetable(path): + all_filenames = [file for file in glob.glob(path + '/*.csv')] - #combine all files in the list - #combined_csv = pd.concat([pd.read_csv(f) for f in all_filenames ]) - list_of_dataframes = [] - for i in all_filenames: - list_of_dataframes.append(pd.read_csv(i,sep=" ")) - - merged_df = pd.concat(list_of_dataframes) - merged_df.to_csv("final_long_table.csv", index=False, encoding='utf-8-sig') + dataframe_list = [] + for file in all_filenames: + dataframe_list.append(pd.read_csv(file,sep=",")) + merged_df = pd.concat(dataframe_list) return merged_df def main(args=None): @@ -155,11 +117,19 @@ def main(args=None): exit() sample_names = [os.path.basename(filename).split("_norm.table")[0] for filename in table_list] - for table,snpsift,pangolin in zip(table_list,snpsift_list,pangolin_list): - tl_onesample_out,snpsift_table_final = create_long(table,snpsift,pangolin,args.software) - #merged = mergetables(tl_onesample_out,snpsift_table_final,args.sample,counter_it) -# merged_df= concatenatetable(args.sample) + #create SampleTables folder in the folder where the script is running + if os.path.exists("SampleTables"): + "SampleTables already exists from previous run, please delete or rename the folder." + else: + os.mkdir("SampleTables") + + for sample_name,table,snpsift,pangolin in zip(sample_names,table_list,snpsift_list,pangolin_list): + long_table_sample = create_long(table,snpsift,pangolin,args.software) + long_table_sample.to_csv('./SampleTables/'+ sample_name + '.csv', header='infer', index=None, sep=',', mode='a') + + merged_df= concatenatetable("./SampleTables") + merged_df.to_csv("final_long_table.csv", index=False, encoding='utf-8-sig') if __name__ == '__main__': sys.exit(main()) From d8e51f3547ebac53c92c11dc7d84d44fde2216cb Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 26 Jan 2022 16:14:01 +0000 Subject: [PATCH 137/238] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2e4791a..7f479bae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [[#235](https://github.com/nf-core/viralrecon/issues/235)] - Nextclade version bump * [[#244](https://github.com/nf-core/viralrecon/issues/244)] - Fix BCFtools consensus generation and masking * [[#245](https://github.com/nf-core/viralrecon/issues/245)] - Mpileup file as output +* [[#246](https://github.com/nf-core/viralrecon/issues/246)] - Option to generate consensus with BCFTools / BEDTools using iVar variants * [[#247](https://github.com/nf-core/viralrecon/issues/247)] - Add strand-bias filtering option and codon fix in consecutive positions in ivar tsv conversion to vcf ### Parameters From d967c09c9ee89f021e05abbddba27a3e65e3e596 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 26 Jan 2022 16:17:23 +0000 Subject: [PATCH 138/238] Update CHANGELOG --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f479bae..b500581e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unpublished Version / DEV] +## [[2.3](https://github.com/nf-core/viralrecon/releases/tag/2.3)] - 2022-01-28 ### Enhancements & fixes @@ -34,7 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 > **NB:** Parameter has been __added__ if just the new parameter information is present. > **NB:** Parameter has been __removed__ if new parameter information isn't present. -## [[2.2](https://github.com/nf-core/rnaseq/releases/tag/2.2)] - 2021-07-29 +## [[2.2](https://github.com/nf-core/viralrecon/releases/tag/2.2)] - 2021-07-29 ### Enhancements & fixes @@ -57,7 +57,7 @@ Note, since the pipeline is now using Nextflow DSL2, each process will be run wi > **NB:** Dependency has been __added__ if just the new version information is present. > **NB:** Dependency has been __removed__ if new version information isn't present. -## [[2.1](https://github.com/nf-core/rnaseq/releases/tag/2.1)] - 2021-06-15 +## [[2.1](https://github.com/nf-core/viralrecon/releases/tag/2.1)] - 2021-06-15 ### Enhancements & fixes @@ -98,7 +98,7 @@ Note, since the pipeline is now using Nextflow DSL2, each process will be run wi > **NB:** Dependency has been __added__ if just the new version information is present. > **NB:** Dependency has been __removed__ if new version information isn't present. -## [[2.0](https://github.com/nf-core/rnaseq/releases/tag/2.0)] - 2021-05-13 +## [[2.0](https://github.com/nf-core/viralrecon/releases/tag/2.0)] - 2021-05-13 ### :warning: Major enhancements @@ -251,7 +251,7 @@ Note, since the pipeline is now using Nextflow DSL2, each process will be run wi > **NB:** Dependency has been __added__ if just the new version information is present. > **NB:** Dependency has been __removed__ if new version information isn't present. -## [[1.1.0](https://github.com/nf-core/rnaseq/releases/tag/1.1.0)] - 2020-06-23 +## [[1.1.0](https://github.com/nf-core/viralrecon/releases/tag/1.1.0)] - 2020-06-23 ### Added @@ -294,7 +294,7 @@ Note, since the pipeline is now using Nextflow DSL2, each process will be run wi * Update minia `3.2.3` -> `3.2.4` * Update plasmidid `1.5.2` -> `1.6.3` -## [[1.0.0](https://github.com/nf-core/rnaseq/releases/tag/1.0.0)] - 2020-06-01 +## [[1.0.0](https://github.com/nf-core/viralrecon/releases/tag/1.0.0)] - 2020-06-01 Initial release of nf-core/viralrecon, created with the [nf-core](http://nf-co.re/) template. From 127eade596672e8a70bd792f6f185bd5804a0edb Mon Sep 17 00:00:00 2001 From: svarona Date: Wed, 26 Jan 2022 17:49:02 +0100 Subject: [PATCH 139/238] Added bcftools filter module --- modules.json | 3 ++ .../nf-core/modules/bcftools/filter/main.nf | 31 ++++++++++++++ .../nf-core/modules/bcftools/filter/meta.yml | 41 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 modules/nf-core/modules/bcftools/filter/main.nf create mode 100644 modules/nf-core/modules/bcftools/filter/meta.yml diff --git a/modules.json b/modules.json index 6aac9011..6d3593bb 100644 --- a/modules.json +++ b/modules.json @@ -18,6 +18,9 @@ "bcftools/consensus": { "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, + "bcftools/filter": { + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + }, "bcftools/mpileup": { "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, diff --git a/modules/nf-core/modules/bcftools/filter/main.nf b/modules/nf-core/modules/bcftools/filter/main.nf new file mode 100644 index 00000000..98b422b1 --- /dev/null +++ b/modules/nf-core/modules/bcftools/filter/main.nf @@ -0,0 +1,31 @@ +process BCFTOOLS_FILTER { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? 'bioconda::bcftools=1.14' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0' : + 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" + + input: + tuple val(meta), path(vcf) + + output: + tuple val(meta), path("*.gz"), emit: vcf + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + bcftools filter \\ + --output ${prefix}.vcf.gz \\ + $args \\ + $vcf + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/bcftools/filter/meta.yml b/modules/nf-core/modules/bcftools/filter/meta.yml new file mode 100644 index 00000000..72d28bf0 --- /dev/null +++ b/modules/nf-core/modules/bcftools/filter/meta.yml @@ -0,0 +1,41 @@ +name: bcftools_filter +description: Filters VCF files +keywords: + - variant calling + - filtering + - VCF +tools: + - filter: + description: | + Apply fixed-threshold filters to VCF files. + homepage: http://samtools.github.io/bcftools/bcftools.html + documentation: http://www.htslib.org/doc/bcftools.html + doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: VCF input file + pattern: "*.{vcf}" +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: VCF filtered output file + pattern: "*.{vcf}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@joseespinosa" + - "@drpatelh" From 6cc7056615cda4bf72fc0bae20bcca27ca400d33 Mon Sep 17 00:00:00 2001 From: svarona Date: Wed, 26 Jan 2022 18:04:35 +0100 Subject: [PATCH 140/238] Included bcftools filter --- subworkflows/local/consensus_bcftools.nf | 1 + 1 file changed, 1 insertion(+) diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf index 7db20187..eed8d56d 100644 --- a/subworkflows/local/consensus_bcftools.nf +++ b/subworkflows/local/consensus_bcftools.nf @@ -2,6 +2,7 @@ // Consensus calling with BCFTools and downstream processing QC // +include { BCFTOOLS_FILTER } from '../../modules/nf-core/modules/bcftools/filter/main' include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' From 8d68748a40e20e6bb353bddaa648607ed8d3bab0 Mon Sep 17 00:00:00 2001 From: svarona Date: Wed, 26 Jan 2022 18:09:20 +0100 Subject: [PATCH 141/238] Added bcftools filter process --- subworkflows/local/consensus_bcftools.nf | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf index eed8d56d..28da3158 100644 --- a/subworkflows/local/consensus_bcftools.nf +++ b/subworkflows/local/consensus_bcftools.nf @@ -22,11 +22,20 @@ workflow CONSENSUS_BCFTOOLS { ch_versions = Channel.empty() + + // + // Filter variants by allele frequency + // + BCFTOOLS_FILTER( + vcf + ) + ch_versions = ch_versions.mix(BCFTOOLS_FILTER.out.versions.first()) + // // Create BED file with consensus regions to mask // MAKE_BED_MASK ( - bam.join(vcf, by: [0]), + bam.join(BCFTOOLS_FILTER.out.vcf, by: [0]), fasta, params.save_mpileup ) From cca5f6c8a4ad3a57c26e4bd9027f814ec313d900 Mon Sep 17 00:00:00 2001 From: svarona Date: Wed, 26 Jan 2022 19:05:55 +0100 Subject: [PATCH 142/238] Added bcftools filter process arguments --- conf/modules_illumina.config | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 4fe305f7..b13dde48 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -539,6 +539,16 @@ if (!params.skip_variants) { if (!params.skip_consensus && params.consensus_caller == 'bcftools') { process { + withName: 'BCFTOOLS_FILTER' { + ext.args = "--output-type z --include 'FORMAT/ALT_FREQ[0] > 0.75'" + ext.prefix = { "${meta.id}.0.75" } + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, + mode: 'copy', + pattern: "*.0.75.vcf", + ] + } + withName: 'MAKE_BED_MASK' { ext.args = "-a --ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 0" ext.args2 = 10 From fec62659578f92534f2393b43520ae5d6f483b74 Mon Sep 17 00:00:00 2001 From: svarona Date: Wed, 26 Jan 2022 19:06:39 +0100 Subject: [PATCH 143/238] Fixed Alle Freq format type to float --- bin/ivar_variants_to_vcf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 93ab6d52..91024652 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -176,7 +176,7 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= '##FORMAT=\n' '##FORMAT=\n' '##FORMAT=\n' - '##FORMAT=\n' + '##FORMAT=\n' ) header += ( "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t" + filename + "\n" From d124bcef4b05412d440e9a4a7c360cfb4bdb4728 Mon Sep 17 00:00:00 2001 From: svarona Date: Wed, 26 Jan 2022 19:22:32 +0100 Subject: [PATCH 144/238] Added tabix for filtered vcf --- conf/modules_illumina.config | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index b13dde48..449834a3 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -543,12 +543,21 @@ if (!params.skip_variants) { ext.args = "--output-type z --include 'FORMAT/ALT_FREQ[0] > 0.75'" ext.prefix = { "${meta.id}.0.75" } publishDir = [ - path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, + path: { "${params.outdir}/variants/${variant_caller}" }, mode: 'copy', pattern: "*.0.75.vcf", ] } + withName: 'TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: 'MAKE_BED_MASK' { ext.args = "-a --ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 0" ext.args2 = 10 From 1810c051b45df21a19bd9746884aa57f4e2dff30 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 26 Jan 2022 23:09:46 +0000 Subject: [PATCH 145/238] Fix #209 --- CHANGELOG.md | 1 + conf/modules_illumina.config | 22 ++++++----- conf/modules_nanopore.config | 18 ++++----- lib/WorkflowCommons.groovy | 38 ++++++++++++++++++- subworkflows/local/prepare_genome_illumina.nf | 5 ++- subworkflows/local/prepare_genome_nanopore.nf | 33 ++++++++-------- workflows/illumina.nf | 19 +++++++++- workflows/nanopore.nf | 22 ++++++++++- 8 files changed, 118 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b500581e..577c355c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Port pipeline to the updated Nextflow DSL2 syntax adopted on nf-core/modules * Bump minimum Nextflow version from `21.04.0` -> `21.10.3` * Updated pipeline template to [nf-core/tools 2.2](https://github.com/nf-core/tools/releases/tag/2.2) +* [[#209](https://github.com/nf-core/viralrecon/issues/209)] - Check that contig in primer BED and genome fasta match * [[#218](https://github.com/nf-core/viralrecon/issues/218)] - Support for compressed FastQ files for Nanopore data * [[#232](https://github.com/nf-core/viralrecon/issues/232)] - Remove duplicate variants called by ARTIC ONT pipeline * [[#235](https://github.com/nf-core/viralrecon/issues/235)] - Nextclade version bump diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 4fe305f7..6c2db4e8 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -195,6 +195,19 @@ if (!params.skip_variants) { } } + if (params.protocol == 'amplicon' || !params.skip_asciigenome) { + process { + withName: 'CUSTOM_GETCHROMSIZES' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + } + } + if (!params.skip_ivar_trim && params.protocol == 'amplicon') { process { withName: 'IVAR_TRIM' { @@ -432,15 +445,6 @@ if (!params.skip_variants) { if (!params.skip_asciigenome) { process { - withName: 'CUSTOM_GETCHROMSIZES' { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - withName: 'ASCIIGENOME' { publishDir = [ path: { "${params.outdir}/variants/${variant_caller}/asciigenome/${meta.id}" }, diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index c35d0f62..2dd14426 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -24,6 +24,15 @@ process { ] } + withName: 'CUSTOM_GETCHROMSIZES' { + publishDir = [ + path: { "${params.outdir}/genome" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: params.save_reference + ] + } + withName: 'MULTIQC_TSV_BARCODE_COUNT|MULTIQC_TSV_GUPPYPLEX_COUNT' { publishDir = [ path: { "${params.outdir}/multiqc/${params.artic_minion_caller}" }, @@ -314,15 +323,6 @@ if (!params.skip_snpeff) { if (!params.skip_asciigenome) { process { - withName: 'CUSTOM_GETCHROMSIZES' { - publishDir = [ - path: { "${params.outdir}/genome" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: params.save_reference - ] - } - withName: 'ASCIIGENOME' { publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}/asciigenome/${meta.id}" }, diff --git a/lib/WorkflowCommons.groovy b/lib/WorkflowCommons.groovy index 9483a91e..8722d4d1 100755 --- a/lib/WorkflowCommons.groovy +++ b/lib/WorkflowCommons.groovy @@ -13,7 +13,7 @@ class WorkflowCommons { " Genome '${params.genome}' not found in any config files provided to the pipeline.\n" + " Currently, the available genome keys are:\n" + " ${params.genomes.keySet().join(", ")}\n" + - "===================================================================================" + "=============================================================================" System.exit(1) } } @@ -73,6 +73,42 @@ class WorkflowCommons { } } + // + // Function to get column entries from a file + // + public static ArrayList getColFromFile(input_file, col=0, uniqify=false, sep='\t') { + def vals = [] + input_file.eachLine { line -> + def val = line.split(sep)[col] + if (uniqify) { + if (!vals.contains(val)) { + vals << val + } + } else { + vals << val + } + } + return vals + } + + // + // Function to generate an error if contigs in BED file do not match those in reference genome + // + public static void checkContigsInBED(fai_contigs, bed_contigs, log) { + def intersect = bed_contigs.intersect(fai_contigs) + if (intersect.size() != bed_contigs.size()) { + def diff = bed_contigs.minus(intersect).sort() + log.error "=============================================================================\n" + + " Contigs in primer BED file do not match those in the reference genome:\n\n" + + " ${diff.join('\n ')}\n\n" + + " Please check:\n" + + " - Primer BED file supplied with --primer_bed\n" + + " - Genome FASTA file supplied with --fasta\n" + + "=============================================================================" + System.exit(1) + } + } + // // Function to read in all fields into a Groovy Map from Nextclade CSV output file // diff --git a/subworkflows/local/prepare_genome_illumina.nf b/subworkflows/local/prepare_genome_illumina.nf index 860d49a4..1a9a54c5 100644 --- a/subworkflows/local/prepare_genome_illumina.nf +++ b/subworkflows/local/prepare_genome_illumina.nf @@ -57,11 +57,13 @@ workflow PREPARE_GENOME { // // Create chromosome sizes file // + ch_fai = Channel.empty() ch_chrom_sizes = Channel.empty() - if (!params.skip_asciigenome) { + if (params.protocol == 'amplicon' || !params.skip_asciigenome) { CUSTOM_GETCHROMSIZES ( ch_fasta ) + ch_fai = CUSTOM_GETCHROMSIZES.out.fai ch_chrom_sizes = CUSTOM_GETCHROMSIZES.out.sizes ch_versions = ch_versions.mix(CUSTOM_GETCHROMSIZES.out.versions) } @@ -235,6 +237,7 @@ workflow PREPARE_GENOME { emit: fasta = ch_fasta // path: genome.fasta gff = ch_gff // path: genome.gff + fai = ch_fai // path: genome.fai chrom_sizes = ch_chrom_sizes // path: genome.sizes bowtie2_index = ch_bowtie2_index // path: bowtie2/index/ primer_bed = ch_primer_bed // path: primer.bed diff --git a/subworkflows/local/prepare_genome_nanopore.nf b/subworkflows/local/prepare_genome_nanopore.nf index 124904fb..6d2449d7 100644 --- a/subworkflows/local/prepare_genome_nanopore.nf +++ b/subworkflows/local/prepare_genome_nanopore.nf @@ -49,14 +49,12 @@ workflow PREPARE_GENOME { // // Create chromosome sizes file // - ch_chrom_sizes = Channel.empty() - if (!params.skip_asciigenome) { - CUSTOM_GETCHROMSIZES ( - ch_fasta - ) - ch_chrom_sizes = CUSTOM_GETCHROMSIZES.out.sizes - ch_versions = ch_versions.mix(CUSTOM_GETCHROMSIZES.out.versions) - } + CUSTOM_GETCHROMSIZES ( + ch_fasta + ) + ch_fai = CUSTOM_GETCHROMSIZES.out.fai + ch_chrom_sizes = CUSTOM_GETCHROMSIZES.out.sizes + ch_versions = ch_versions.mix(CUSTOM_GETCHROMSIZES.out.versions) // // Uncompress primer BED file @@ -130,14 +128,15 @@ workflow PREPARE_GENOME { } emit: - fasta = ch_fasta // path: genome.fasta - gff = ch_gff // path: genome.gff - chrom_sizes = ch_chrom_sizes // path: genome.sizes - primer_bed = ch_primer_bed // path: primer.bed - primer_collapsed_bed = ch_primer_collapsed_bed // path: primer.collapsed.bed - nextclade_db = ch_nextclade_db // path: nextclade_db - snpeff_db = ch_snpeff_db // path: snpeff_db - snpeff_config = ch_snpeff_config // path: snpeff.config + fasta = ch_fasta // path: genome.fasta + gff = ch_gff // path: genome.gff + fai = ch_fai // path: genome.fai + chrom_sizes = ch_chrom_sizes // path: genome.sizes + primer_bed = ch_primer_bed // path: primer.bed + primer_collapsed_bed = ch_primer_collapsed_bed // path: primer.collapsed.bed + nextclade_db = ch_nextclade_db // path: nextclade_db + snpeff_db = ch_snpeff_db // path: snpeff_db + snpeff_config = ch_snpeff_config // path: snpeff.config - versions = ch_versions // channel: [ versions.yml ] + versions = ch_versions // channel: [ versions.yml ] } diff --git a/workflows/illumina.nf b/workflows/illumina.nf index c9ec299e..205941ac 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -136,7 +136,24 @@ workflow ILLUMINA { .primer_bed .map { WorkflowCommons.checkPrimerSuffixes(it, params.primer_left_suffix, params.primer_right_suffix, log) } - // Check if the primer BED file supplied to the pipeline is from the SWIFT/SNAP protocol + // + // Check whether the contigs in the primer BED file are present in the reference genome + // + PREPARE_GENOME + .out + .primer_bed + .map { [ WorkflowCommons.getColFromFile(it, col=0, uniqify=true, sep='\t') ] } + .set { ch_bed_contigs } + + PREPARE_GENOME + .out + .fai + .map { [ WorkflowCommons.getColFromFile(it, col=0, uniqify=true, sep='\t') ] } + .concat(ch_bed_contigs) + .collect() + .map { fai, bed -> WorkflowCommons.checkContigsInBED(fai, bed, log) } + + // Check whether the primer BED file supplied to the pipeline is from the SWIFT/SNAP protocol if (!params.ivar_trim_offset) { PREPARE_GENOME .out diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 06eb74c7..2a20efb1 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -50,8 +50,9 @@ ch_multiqc_custom_config = params.multiqc_config ? file(params.multiqc_config) : // // MODULE: Loaded from modules/local/ // -include { ASCIIGENOME } from '../modules/local/asciigenome' -include { MULTIQC } from '../modules/local/multiqc_nanopore' +include { ASCIIGENOME } from '../modules/local/asciigenome' +include { MULTIQC } from '../modules/local/multiqc_nanopore' +include { BED_CONTIGS_IN_FASTA } from '../modules/local/bed_contigs_in_fasta' include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_GENOME } from '../modules/local/plot_mosdepth_regions' include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_AMPLICON } from '../modules/local/plot_mosdepth_regions' include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NO_SAMPLE_NAME } from '../modules/local/multiqc_tsv_from_list' @@ -134,6 +135,23 @@ workflow NANOPORE { .primer_bed .map { WorkflowCommons.checkPrimerSuffixes(it, params.primer_left_suffix, params.primer_right_suffix, log) } + // + // Check whether the contigs in the primer BED file are present in the reference genome + // + PREPARE_GENOME + .out + .primer_bed + .map { [ WorkflowCommons.getColFromFile(it, col=0, uniqify=true, sep='\t') ] } + .set { ch_bed_contigs } + + PREPARE_GENOME + .out + .fai + .map { [ WorkflowCommons.getColFromFile(it, col=0, uniqify=true, sep='\t') ] } + .concat(ch_bed_contigs) + .collect() + .map { fai, bed -> WorkflowCommons.checkContigsInBED(fai, bed, log) } + barcode_dirs = file("${params.fastq_dir}/barcode*", type: 'dir' , maxdepth: 1) single_barcode_dir = file("${params.fastq_dir}/*.fastq" , type: 'file', maxdepth: 1) ch_custom_no_sample_name_multiqc = Channel.empty() From 14bab04bb9d31d43628d90c298c49728e1a7bbd8 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 26 Jan 2022 23:12:39 +0000 Subject: [PATCH 146/238] Fix comments --- workflows/illumina.nf | 2 -- workflows/nanopore.nf | 2 -- 2 files changed, 4 deletions(-) diff --git a/workflows/illumina.nf b/workflows/illumina.nf index 205941ac..e7298005 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -136,9 +136,7 @@ workflow ILLUMINA { .primer_bed .map { WorkflowCommons.checkPrimerSuffixes(it, params.primer_left_suffix, params.primer_right_suffix, log) } - // // Check whether the contigs in the primer BED file are present in the reference genome - // PREPARE_GENOME .out .primer_bed diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 2a20efb1..7a033bc2 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -135,9 +135,7 @@ workflow NANOPORE { .primer_bed .map { WorkflowCommons.checkPrimerSuffixes(it, params.primer_left_suffix, params.primer_right_suffix, log) } - // // Check whether the contigs in the primer BED file are present in the reference genome - // PREPARE_GENOME .out .primer_bed From 403e173a77a727a809ccc68663d298a3ebac29ec Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 26 Jan 2022 23:17:51 +0000 Subject: [PATCH 147/238] Remove deleted module include --- workflows/nanopore.nf | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 7a033bc2..6faf2b79 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -50,9 +50,8 @@ ch_multiqc_custom_config = params.multiqc_config ? file(params.multiqc_config) : // // MODULE: Loaded from modules/local/ // -include { ASCIIGENOME } from '../modules/local/asciigenome' -include { MULTIQC } from '../modules/local/multiqc_nanopore' -include { BED_CONTIGS_IN_FASTA } from '../modules/local/bed_contigs_in_fasta' +include { ASCIIGENOME } from '../modules/local/asciigenome' +include { MULTIQC } from '../modules/local/multiqc_nanopore' include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_GENOME } from '../modules/local/plot_mosdepth_regions' include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_AMPLICON } from '../modules/local/plot_mosdepth_regions' include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NO_SAMPLE_NAME } from '../modules/local/multiqc_tsv_from_list' From 9910e58339d9b5aa6dbe473a854f1697af122722 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 26 Jan 2022 23:23:12 +0000 Subject: [PATCH 148/238] Fix spacing --- lib/WorkflowCommons.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/WorkflowCommons.groovy b/lib/WorkflowCommons.groovy index 8722d4d1..395cb1c2 100755 --- a/lib/WorkflowCommons.groovy +++ b/lib/WorkflowCommons.groovy @@ -102,8 +102,8 @@ class WorkflowCommons { " Contigs in primer BED file do not match those in the reference genome:\n\n" + " ${diff.join('\n ')}\n\n" + " Please check:\n" + - " - Primer BED file supplied with --primer_bed\n" + - " - Genome FASTA file supplied with --fasta\n" + + " - Primer BED file supplied with --primer_bed\n" + + " - Genome FASTA file supplied with --fasta\n" + "=============================================================================" System.exit(1) } From a77da904bfba4f1cfb9880c5800486eed8572466 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:02:08 +0100 Subject: [PATCH 149/238] Removed deletions and insertions from the codon fix --- bin/ivar_variants_to_vcf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 91024652..b21b5e12 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -262,7 +262,7 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= ) param_list = [CHROM,POS,ID,REF,ALT,REF_DP,REF_RV,ALT_DP,ALT_RV,QUAL,REF_CODON,ALT_CODON,FILTER,INFO,FORMAT,SAMPLE] - if NotMergeCodon: + if NotMergeCodon or var_type != "SNP": writeLine = True oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) @@ -272,9 +272,9 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= ## Always fill dict_lines until size 2. if len(dict_lines["POS"]) ==0 or len(dict_lines["POS"]) ==1 : - for i,j in enumerate(dict_lines): - dict_lines.setdefault(j, []).append(param_list[i]) - writeLine=False + for i,j in enumerate(dict_lines): + dict_lines.setdefault(j, []).append(param_list[i]) + writeLine=False # If queue has size 2, we include the third line elif len(dict_lines["POS"]) == 2: From 0c001c680fb4958f635099bac8a34647d0488d35 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:02:47 +0100 Subject: [PATCH 150/238] Added tabix config after bcftools sort --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 449834a3..d30f941e 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -549,7 +549,7 @@ if (!params.skip_variants) { ] } - withName: 'TABIX_TABIX' { + withName: '.*:.*:CONSENSUS_BCFTOOLS:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/${variant_caller}" }, From c76e5557da7342a322d16f658d9d4af9d1a5c316 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:03:11 +0100 Subject: [PATCH 151/238] Added tabix after bcftools sort --- subworkflows/local/consensus_bcftools.nf | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf index 28da3158..084c6d12 100644 --- a/subworkflows/local/consensus_bcftools.nf +++ b/subworkflows/local/consensus_bcftools.nf @@ -3,6 +3,7 @@ // include { BCFTOOLS_FILTER } from '../../modules/nf-core/modules/bcftools/filter/main' +include { TABIX_TABIX } from '../../modules/nf-core/modules/tabix/tabix/main' include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' @@ -24,13 +25,18 @@ workflow CONSENSUS_BCFTOOLS { // - // Filter variants by allele frequency + // Filter variants by allele frequency, zip and index // BCFTOOLS_FILTER( vcf ) ch_versions = ch_versions.mix(BCFTOOLS_FILTER.out.versions.first()) + TABIX_TABIX( + BCFTOOLS_FILTER.out.vcf + ) + ch_versions = ch_versions.mix(TABIX_TABIX.out.versions.first()) + // // Create BED file with consensus regions to mask // @@ -62,7 +68,7 @@ workflow CONSENSUS_BCFTOOLS { // Call consensus sequence with BCFTools // BCFTOOLS_CONSENSUS ( - vcf.join(tbi, by: [0]).join(BEDTOOLS_MASKFASTA.out.fasta, by: [0]) + BCFTOOLS_FILTER.out.vcf.join(TABIX_TABIX.out.tbi, by: [0]).join(BEDTOOLS_MASKFASTA.out.fasta, by: [0]) ) ch_versions = ch_versions.mix(BCFTOOLS_CONSENSUS.out.versions.first()) From cb0f58ff30f435006f5e83426dacceb1125daa85 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:06:20 +0100 Subject: [PATCH 152/238] Added bcftools sort for ivar variants after excluding indels from codon fix --- modules.json | 3 ++ modules/nf-core/modules/bcftools/sort/main.nf | 32 ++++++++++++++ .../nf-core/modules/bcftools/sort/meta.yml | 43 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 modules/nf-core/modules/bcftools/sort/main.nf create mode 100644 modules/nf-core/modules/bcftools/sort/meta.yml diff --git a/modules.json b/modules.json index 6d3593bb..21b563a1 100644 --- a/modules.json +++ b/modules.json @@ -24,6 +24,9 @@ "bcftools/mpileup": { "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, + "bcftools/sort": { + "git_sha": "2d4b0f77873e15b79599cc77ff92acbb880f3ac2" + }, "bcftools/stats": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, diff --git a/modules/nf-core/modules/bcftools/sort/main.nf b/modules/nf-core/modules/bcftools/sort/main.nf new file mode 100644 index 00000000..2fcadc98 --- /dev/null +++ b/modules/nf-core/modules/bcftools/sort/main.nf @@ -0,0 +1,32 @@ +process BCFTOOLS_SORT { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? "bioconda::bcftools=1.14" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0': + 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" + + input: + tuple val(meta), path(vcf) + + output: + tuple val(meta), path("*.gz"), emit: vcf + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + bcftools \\ + sort \\ + --output ${prefix}.vcf.gz \\ + $args \\ + $vcf + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/bcftools/sort/meta.yml b/modules/nf-core/modules/bcftools/sort/meta.yml new file mode 100644 index 00000000..af894e82 --- /dev/null +++ b/modules/nf-core/modules/bcftools/sort/meta.yml @@ -0,0 +1,43 @@ +name: bcftools_sort +description: Sorts VCF files +keywords: + - sorting + - VCF + - variant calling +tools: + - sort: + description: Sort VCF files by coordinates. + homepage: http://samtools.github.io/bcftools/bcftools.html + documentation: http://www.htslib.org/doc/bcftools.html + tool_dev_url: https://github.com/samtools/bcftools + doi: "10.1093/bioinformatics/btp352" + licence: ['MIT'] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: The VCF/BCF file to be sorted + pattern: "*.{vcf.gz,vcf,bcf}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - vcf: + type: file + description: Sorted VCF file + pattern: "*.{vcf.gz}" + +authors: + - "@Gwennid" From 88aa7095d645c935ca1cac9d4800634aad9deb0e Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:16:05 +0100 Subject: [PATCH 153/238] Added bcftools sort config --- conf/modules_illumina.config | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index d30f941e..7f75fd6e 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -375,6 +375,16 @@ if (!params.skip_variants) { ] } + withName: 'BCFTOOLS_SORT' { + ext.args = '--output-type z' + ext.prefix = { "${meta.id}.sorted" } + publishDir = [ + path: { "${params.outdir}/variants/ivar" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: '.*:.*:VARIANTS_IVAR:.*:TABIX_BGZIP' { publishDir = [ path: { "${params.outdir}/variants/ivar" }, From 1f3ae31c33235dc4ee2f099d6d157bcc9dfb02da Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:16:42 +0100 Subject: [PATCH 154/238] Added bcftools sort after ivar variants to vcf --- subworkflows/local/variants_ivar.nf | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index c4bf2eeb..c82a4fd0 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -4,6 +4,9 @@ include { IVAR_VARIANTS } from '../../modules/nf-core/modules/ivar/variants/main' include { IVAR_VARIANTS_TO_VCF } from '../../modules/local/ivar_variants_to_vcf' +include { BCFTOOLS_SORT } from '../../modules/nf-core/modules/bcftools/sort/main' + + include { VCF_BGZIP_TABIX_STATS } from '../nf-core/vcf_bgzip_tabix_stats' include { VARIANTS_QC } from './variants_qc' @@ -34,7 +37,7 @@ workflow VARIANTS_IVAR { ch_versions = ch_versions.mix(IVAR_VARIANTS.out.versions.first()) // - // Convert original iVar output to VCF, zip and index + // Convert original iVar output to VCF, zip, sort and index // IVAR_VARIANTS_TO_VCF ( IVAR_VARIANTS.out.tsv, @@ -42,9 +45,14 @@ workflow VARIANTS_IVAR { ) ch_versions = ch_versions.mix(IVAR_VARIANTS_TO_VCF.out.versions.first()) - VCF_BGZIP_TABIX_STATS ( + BCFTOOLS_SORT ( IVAR_VARIANTS_TO_VCF.out.vcf ) + ch_versions = ch_versions.mix(BCFTOOLS_SORT.out.versions) + + VCF_BGZIP_TABIX_STATS ( + BCFTOOLS_SORT.out.vcf + ) ch_versions = ch_versions.mix(VCF_BGZIP_TABIX_STATS.out.versions) // From 7c31e0f46f5cef4e46fedcd9b3800e41c9765ed7 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:50:17 +0100 Subject: [PATCH 155/238] Revert "Added bcftools sort after ivar variants to vcf" This reverts commit 1f3ae31c33235dc4ee2f099d6d157bcc9dfb02da. --- subworkflows/local/variants_ivar.nf | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index c82a4fd0..c4bf2eeb 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -4,9 +4,6 @@ include { IVAR_VARIANTS } from '../../modules/nf-core/modules/ivar/variants/main' include { IVAR_VARIANTS_TO_VCF } from '../../modules/local/ivar_variants_to_vcf' -include { BCFTOOLS_SORT } from '../../modules/nf-core/modules/bcftools/sort/main' - - include { VCF_BGZIP_TABIX_STATS } from '../nf-core/vcf_bgzip_tabix_stats' include { VARIANTS_QC } from './variants_qc' @@ -37,7 +34,7 @@ workflow VARIANTS_IVAR { ch_versions = ch_versions.mix(IVAR_VARIANTS.out.versions.first()) // - // Convert original iVar output to VCF, zip, sort and index + // Convert original iVar output to VCF, zip and index // IVAR_VARIANTS_TO_VCF ( IVAR_VARIANTS.out.tsv, @@ -45,13 +42,8 @@ workflow VARIANTS_IVAR { ) ch_versions = ch_versions.mix(IVAR_VARIANTS_TO_VCF.out.versions.first()) - BCFTOOLS_SORT ( - IVAR_VARIANTS_TO_VCF.out.vcf - ) - ch_versions = ch_versions.mix(BCFTOOLS_SORT.out.versions) - VCF_BGZIP_TABIX_STATS ( - BCFTOOLS_SORT.out.vcf + IVAR_VARIANTS_TO_VCF.out.vcf ) ch_versions = ch_versions.mix(VCF_BGZIP_TABIX_STATS.out.versions) From 5443d3f86f22900f66ac6a7760ca87a09722256f Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:51:18 +0100 Subject: [PATCH 156/238] Revert "Added bcftools sort config" This reverts commit 88aa7095d645c935ca1cac9d4800634aad9deb0e. --- conf/modules_illumina.config | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 7f75fd6e..d30f941e 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -375,16 +375,6 @@ if (!params.skip_variants) { ] } - withName: 'BCFTOOLS_SORT' { - ext.args = '--output-type z' - ext.prefix = { "${meta.id}.sorted" } - publishDir = [ - path: { "${params.outdir}/variants/ivar" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: '.*:.*:VARIANTS_IVAR:.*:TABIX_BGZIP' { publishDir = [ path: { "${params.outdir}/variants/ivar" }, From 58768e83f8fa2dc0a7c26e723c072dfb3f77d2ad Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:51:24 +0100 Subject: [PATCH 157/238] Revert "Added bcftools sort for ivar variants after excluding indels from codon fix" This reverts commit cb0f58ff30f435006f5e83426dacceb1125daa85. --- modules.json | 3 -- modules/nf-core/modules/bcftools/sort/main.nf | 32 -------------- .../nf-core/modules/bcftools/sort/meta.yml | 43 ------------------- 3 files changed, 78 deletions(-) delete mode 100644 modules/nf-core/modules/bcftools/sort/main.nf delete mode 100644 modules/nf-core/modules/bcftools/sort/meta.yml diff --git a/modules.json b/modules.json index 21b563a1..6d3593bb 100644 --- a/modules.json +++ b/modules.json @@ -24,9 +24,6 @@ "bcftools/mpileup": { "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, - "bcftools/sort": { - "git_sha": "2d4b0f77873e15b79599cc77ff92acbb880f3ac2" - }, "bcftools/stats": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, diff --git a/modules/nf-core/modules/bcftools/sort/main.nf b/modules/nf-core/modules/bcftools/sort/main.nf deleted file mode 100644 index 2fcadc98..00000000 --- a/modules/nf-core/modules/bcftools/sort/main.nf +++ /dev/null @@ -1,32 +0,0 @@ -process BCFTOOLS_SORT { - tag "$meta.id" - label 'process_medium' - - conda (params.enable_conda ? "bioconda::bcftools=1.14" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0': - 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" - - input: - tuple val(meta), path(vcf) - - output: - tuple val(meta), path("*.gz"), emit: vcf - path "versions.yml" , emit: versions - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - """ - bcftools \\ - sort \\ - --output ${prefix}.vcf.gz \\ - $args \\ - $vcf - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') - END_VERSIONS - """ -} diff --git a/modules/nf-core/modules/bcftools/sort/meta.yml b/modules/nf-core/modules/bcftools/sort/meta.yml deleted file mode 100644 index af894e82..00000000 --- a/modules/nf-core/modules/bcftools/sort/meta.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: bcftools_sort -description: Sorts VCF files -keywords: - - sorting - - VCF - - variant calling -tools: - - sort: - description: Sort VCF files by coordinates. - homepage: http://samtools.github.io/bcftools/bcftools.html - documentation: http://www.htslib.org/doc/bcftools.html - tool_dev_url: https://github.com/samtools/bcftools - doi: "10.1093/bioinformatics/btp352" - licence: ['MIT'] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - vcf: - type: file - description: The VCF/BCF file to be sorted - pattern: "*.{vcf.gz,vcf,bcf}" - -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - - vcf: - type: file - description: Sorted VCF file - pattern: "*.{vcf.gz}" - -authors: - - "@Gwennid" From ac4089069c35c97253b37963eec7a76207591fbf Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:51:29 +0100 Subject: [PATCH 158/238] Revert "Added tabix after bcftools sort" This reverts commit c76e5557da7342a322d16f658d9d4af9d1a5c316. --- subworkflows/local/consensus_bcftools.nf | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf index 084c6d12..28da3158 100644 --- a/subworkflows/local/consensus_bcftools.nf +++ b/subworkflows/local/consensus_bcftools.nf @@ -3,7 +3,6 @@ // include { BCFTOOLS_FILTER } from '../../modules/nf-core/modules/bcftools/filter/main' -include { TABIX_TABIX } from '../../modules/nf-core/modules/tabix/tabix/main' include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' @@ -25,18 +24,13 @@ workflow CONSENSUS_BCFTOOLS { // - // Filter variants by allele frequency, zip and index + // Filter variants by allele frequency // BCFTOOLS_FILTER( vcf ) ch_versions = ch_versions.mix(BCFTOOLS_FILTER.out.versions.first()) - TABIX_TABIX( - BCFTOOLS_FILTER.out.vcf - ) - ch_versions = ch_versions.mix(TABIX_TABIX.out.versions.first()) - // // Create BED file with consensus regions to mask // @@ -68,7 +62,7 @@ workflow CONSENSUS_BCFTOOLS { // Call consensus sequence with BCFTools // BCFTOOLS_CONSENSUS ( - BCFTOOLS_FILTER.out.vcf.join(TABIX_TABIX.out.tbi, by: [0]).join(BEDTOOLS_MASKFASTA.out.fasta, by: [0]) + vcf.join(tbi, by: [0]).join(BEDTOOLS_MASKFASTA.out.fasta, by: [0]) ) ch_versions = ch_versions.mix(BCFTOOLS_CONSENSUS.out.versions.first()) From a44f38c9156a4b55009ccc34c08b446c82e8cad9 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:51:39 +0100 Subject: [PATCH 159/238] Revert "Added tabix config after bcftools sort" This reverts commit 0c001c680fb4958f635099bac8a34647d0488d35. --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index d30f941e..449834a3 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -549,7 +549,7 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:CONSENSUS_BCFTOOLS:TABIX_TABIX' { + withName: 'TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/${variant_caller}" }, From 263708a3cbda17001321af4b07f9c079152f2227 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 12:55:21 +0100 Subject: [PATCH 160/238] Fixed left padding space --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 449834a3..3336b7af 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -539,7 +539,7 @@ if (!params.skip_variants) { if (!params.skip_consensus && params.consensus_caller == 'bcftools') { process { - withName: 'BCFTOOLS_FILTER' { + withName: 'BCFTOOLS_FILTER' { ext.args = "--output-type z --include 'FORMAT/ALT_FREQ[0] > 0.75'" ext.prefix = { "${meta.id}.0.75" } publishDir = [ From 600308f37850a0a5edd21f3f3e65ba12429428e3 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 14:17:54 +0100 Subject: [PATCH 161/238] Sort ivar vcf variants --- modules/local/ivar_variants_to_vcf.nf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/local/ivar_variants_to_vcf.nf b/modules/local/ivar_variants_to_vcf.nf index ad4dcbc3..3c911e19 100644 --- a/modules/local/ivar_variants_to_vcf.nf +++ b/modules/local/ivar_variants_to_vcf.nf @@ -22,10 +22,12 @@ process IVAR_VARIANTS_TO_VCF { """ ivar_variants_to_vcf.py \\ $tsv \\ - ${prefix}.vcf \\ + unsorted.txt \\ $args \\ > ${prefix}.variant_counts.log + head -1000 unsorted.txt | grep "^#" > ${prefix}.vcf; cat unsorted.txt | grep -v "^#" | sort -k1,1d -k2,2n >> ${prefix}.vcf + cat $header ${prefix}.variant_counts.log > ${prefix}.variant_counts_mqc.tsv cat <<-END_VERSIONS > versions.yml From 91ad7981a22a5c49d8c401fa3cd2bf598dceeb75 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 14:27:15 +0100 Subject: [PATCH 162/238] Fixed tabix name --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 3336b7af..ad0882fd 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -549,7 +549,7 @@ if (!params.skip_variants) { ] } - withName: 'TABIX_TABIX' { + withName: '.*:.*:CONSENSUS_BCFTOOLS:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/${variant_caller}" }, From 1481aed8ad8bd7a2ab02f7a589487c9f99941aee Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 14:33:04 +0100 Subject: [PATCH 163/238] Added tabix after bcftools sort --- subworkflows/local/consensus_bcftools.nf | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf index 28da3158..084c6d12 100644 --- a/subworkflows/local/consensus_bcftools.nf +++ b/subworkflows/local/consensus_bcftools.nf @@ -3,6 +3,7 @@ // include { BCFTOOLS_FILTER } from '../../modules/nf-core/modules/bcftools/filter/main' +include { TABIX_TABIX } from '../../modules/nf-core/modules/tabix/tabix/main' include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' @@ -24,13 +25,18 @@ workflow CONSENSUS_BCFTOOLS { // - // Filter variants by allele frequency + // Filter variants by allele frequency, zip and index // BCFTOOLS_FILTER( vcf ) ch_versions = ch_versions.mix(BCFTOOLS_FILTER.out.versions.first()) + TABIX_TABIX( + BCFTOOLS_FILTER.out.vcf + ) + ch_versions = ch_versions.mix(TABIX_TABIX.out.versions.first()) + // // Create BED file with consensus regions to mask // @@ -62,7 +68,7 @@ workflow CONSENSUS_BCFTOOLS { // Call consensus sequence with BCFTools // BCFTOOLS_CONSENSUS ( - vcf.join(tbi, by: [0]).join(BEDTOOLS_MASKFASTA.out.fasta, by: [0]) + BCFTOOLS_FILTER.out.vcf.join(TABIX_TABIX.out.tbi, by: [0]).join(BEDTOOLS_MASKFASTA.out.fasta, by: [0]) ) ch_versions = ch_versions.mix(BCFTOOLS_CONSENSUS.out.versions.first()) From ce042e585062f303bc1c2354dd35082ce92d79a0 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 27 Jan 2022 13:55:17 +0000 Subject: [PATCH 164/238] Bump Nextclade version to 1.10.1 --- docs/images/nextclade_tag_example.png | Bin 38057 -> 39138 bytes docs/usage.md | 10 +++++----- modules.json | 4 ++-- .../modules/nextclade/datasetget/main.nf | 6 +++--- modules/nf-core/modules/nextclade/run/main.nf | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/images/nextclade_tag_example.png b/docs/images/nextclade_tag_example.png index 8c409fc9f13c3f30cb3217e6c33d3079b7944899..1d29b64e94487566ff0292b1eb831af0c89e2d4b 100644 GIT binary patch literal 39138 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 I#x4Bh@2N+fsQ>@~ literal 38057 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 * Let the pipeline create and use the latest version by setting: `--nextclade_dataset false --nextclade_dataset_tag false` * Let the pipeline create and use a specific, tagged version by setting: `--nextclade_dataset false --nextclade_dataset_tag ` -The Nextclade dataset releases can be found on their [Github page](https://github.com/nextstrain/nextclade_data/releases). Use the tag specified for each release e.g `2022-01-05T19:54:31Z` in the example below: +The Nextclade dataset releases can be found on their [Github page](https://github.com/nextstrain/nextclade_data/releases). Use the tag specified for each release e.g `2022-01-18T12:00:00Z` in the example below: ![Nextclade tag example](images/nextclade_tag_example.png) diff --git a/modules.json b/modules.json index 6aac9011..e1c2af95 100644 --- a/modules.json +++ b/modules.json @@ -85,10 +85,10 @@ "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "nextclade/datasetget": { - "git_sha": "796dbb573e52403bb644a65f0639af0a121c90f1" + "git_sha": "d70526c80665d412404e50708998b19cfb0dee7d" }, "nextclade/run": { - "git_sha": "796dbb573e52403bb644a65f0639af0a121c90f1" + "git_sha": "d70526c80665d412404e50708998b19cfb0dee7d" }, "pangolin": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" diff --git a/modules/nf-core/modules/nextclade/datasetget/main.nf b/modules/nf-core/modules/nextclade/datasetget/main.nf index 55371168..00dc8ff9 100644 --- a/modules/nf-core/modules/nextclade/datasetget/main.nf +++ b/modules/nf-core/modules/nextclade/datasetget/main.nf @@ -2,10 +2,10 @@ process NEXTCLADE_DATASETGET { tag "$dataset" label 'process_low' - conda (params.enable_conda ? "bioconda::nextclade=1.9.0" : null) + conda (params.enable_conda ? "bioconda::nextclade=1.10.1" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/nextclade:1.9.0--h9ee0642_0' : - 'quay.io/biocontainers/nextclade:1.9.0--h9ee0642_0' }" + 'https://depot.galaxyproject.org/singularity/nextclade:1.10.1--h9ee0642_0' : + 'quay.io/biocontainers/nextclade:1.10.1--h9ee0642_0' }" input: val dataset diff --git a/modules/nf-core/modules/nextclade/run/main.nf b/modules/nf-core/modules/nextclade/run/main.nf index e29dd8ce..36e19aab 100644 --- a/modules/nf-core/modules/nextclade/run/main.nf +++ b/modules/nf-core/modules/nextclade/run/main.nf @@ -2,10 +2,10 @@ process NEXTCLADE_RUN { tag "$meta.id" label 'process_low' - conda (params.enable_conda ? "bioconda::nextclade=1.9.0" : null) + conda (params.enable_conda ? "bioconda::nextclade=1.10.1" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/nextclade:1.9.0--h9ee0642_0' : - 'quay.io/biocontainers/nextclade:1.9.0--h9ee0642_0' }" + 'https://depot.galaxyproject.org/singularity/nextclade:1.10.1--h9ee0642_0' : + 'quay.io/biocontainers/nextclade:1.10.1--h9ee0642_0' }" input: tuple val(meta), path(fasta) From b33b71d49f470ab2bb5ab36259003b1c7d469c79 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 16:29:48 +0100 Subject: [PATCH 165/238] Fixed sintaxys of bcftools filter --- conf/modules_illumina.config | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index ad0882fd..9d5c8012 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -540,12 +540,12 @@ if (!params.skip_variants) { if (!params.skip_consensus && params.consensus_caller == 'bcftools') { process { withName: 'BCFTOOLS_FILTER' { - ext.args = "--output-type z --include 'FORMAT/ALT_FREQ[0] > 0.75'" - ext.prefix = { "${meta.id}.0.75" } + ext.args = "--output-type z --include 'FORMAT/ALT_FREQ >= 0.75'" + ext.prefix = { "${meta.id}.AF0.75" } publishDir = [ path: { "${params.outdir}/variants/${variant_caller}" }, mode: 'copy', - pattern: "*.0.75.vcf", + pattern: "*.AF0.75.vcf", ] } From 99c01076c5eed070d8ad177c6888a5d615d0c561 Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 20:42:25 +0100 Subject: [PATCH 166/238] Fixed file name format to output --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 9d5c8012..f7ce1f47 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -545,7 +545,7 @@ if (!params.skip_variants) { publishDir = [ path: { "${params.outdir}/variants/${variant_caller}" }, mode: 'copy', - pattern: "*.AF0.75.vcf", + pattern: "*.AF0.75.vcf.gz", ] } From e34c04790065a575b665efe4107f153644ac2d3b Mon Sep 17 00:00:00 2001 From: svarona Date: Thu, 27 Jan 2022 21:54:54 +0100 Subject: [PATCH 167/238] Added bcftools norm for bcftools variants --- modules.json | 3 ++ modules/nf-core/modules/bcftools/norm/main.nf | 34 ++++++++++++++ .../nf-core/modules/bcftools/norm/meta.yml | 46 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 modules/nf-core/modules/bcftools/norm/main.nf create mode 100644 modules/nf-core/modules/bcftools/norm/meta.yml diff --git a/modules.json b/modules.json index 6d3593bb..715ad456 100644 --- a/modules.json +++ b/modules.json @@ -24,6 +24,9 @@ "bcftools/mpileup": { "git_sha": "bb90e4fb78a977d469aad2a614c673b1981e7806" }, + "bcftools/norm": { + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + }, "bcftools/stats": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, diff --git a/modules/nf-core/modules/bcftools/norm/main.nf b/modules/nf-core/modules/bcftools/norm/main.nf new file mode 100644 index 00000000..e8bf6324 --- /dev/null +++ b/modules/nf-core/modules/bcftools/norm/main.nf @@ -0,0 +1,34 @@ +process BCFTOOLS_NORM { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? 'bioconda::bcftools=1.14' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0' : + 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" + + input: + tuple val(meta), path(vcf) + path(fasta) + + output: + tuple val(meta), path("*.gz") , emit: vcf + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + bcftools norm \\ + --fasta-ref ${fasta} \\ + --output ${prefix}.vcf.gz \\ + $args \\ + --threads $task.cpus \\ + ${vcf} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/bcftools/norm/meta.yml b/modules/nf-core/modules/bcftools/norm/meta.yml new file mode 100644 index 00000000..27978a53 --- /dev/null +++ b/modules/nf-core/modules/bcftools/norm/meta.yml @@ -0,0 +1,46 @@ +name: bcftools_norm +description: Normalize VCF file +keywords: + - normalize + - norm + - variant calling + - VCF +tools: + - norm: + description: | + Normalize VCF files. + homepage: http://samtools.github.io/bcftools/bcftools.html + documentation: http://www.htslib.org/doc/bcftools.html + doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: | + The vcf file to be normalized + e.g. 'file1.vcf' + - fasta: + type: file + description: FASTA reference file + pattern: "*.{fasta,fa}" +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: VCF normalized output file + pattern: "*.{vcf.gz}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@abhi18av" From 613d98a9dede211fdae9c4227909fa0822b50b3f Mon Sep 17 00:00:00 2001 From: svarona Date: Fri, 28 Jan 2022 11:04:06 +0100 Subject: [PATCH 168/238] Added filter for bcftools --- conf/modules_illumina.config | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index f7ce1f47..b22fd9c3 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -539,14 +539,28 @@ if (!params.skip_variants) { if (!params.skip_consensus && params.consensus_caller == 'bcftools') { process { - withName: 'BCFTOOLS_FILTER' { - ext.args = "--output-type z --include 'FORMAT/ALT_FREQ >= 0.75'" - ext.prefix = { "${meta.id}.AF0.75" } - publishDir = [ - path: { "${params.outdir}/variants/${variant_caller}" }, - mode: 'copy', - pattern: "*.AF0.75.vcf.gz", - ] + if (params.variant_caller == 'bcftools') { + withName: 'BCFTOOLS_FILTER' { + ext.args = "--output-type z --include 'FORMAT/AD[:1] / FORMAT/DP >= 0.75'" + ext.prefix = { "${meta.id}.AF0.75" } + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}" }, + mode: 'copy', + pattern: "*.AF0.75.vcf.gz", + ] + } + } + + else if (params.variant_caller == 'ivar') { + withName: 'BCFTOOLS_FILTER' { + ext.args = "--output-type z --include 'FORMAT/ALT_FREQ >= 0.75'" + ext.prefix = { "${meta.id}.AF0.75" } + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}" }, + mode: 'copy', + pattern: "*.AF0.75.vcf.gz", + ] + } } withName: '.*:.*:CONSENSUS_BCFTOOLS:TABIX_TABIX' { From 8875b19d9da59892a9b838430f83e90ea2d80103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 11:49:53 +0100 Subject: [PATCH 169/238] Added aa one letter annotation to long table --- bin/parser_ivar_bcftools.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/bin/parser_ivar_bcftools.py b/bin/parser_ivar_bcftools.py index 4cdbe4ed..b4c8539e 100755 --- a/bin/parser_ivar_bcftools.py +++ b/bin/parser_ivar_bcftools.py @@ -24,6 +24,19 @@ def parser_args(args=None): return parser.parse_args(args) +def aa_three_to_one_letter(hgvs_three): + three_syntax_dict= {'Ala': 'A', 'Arg': 'R', 'Asn': 'N', 'Asp': 'D', 'Cys': 'C', + 'Gln': 'Q', 'Glu': 'E', 'Gly': 'G', 'His': 'H', 'Ile': 'I', + 'Leu': 'L', 'Lys': 'K', 'Met': 'M', 'Phe': 'F', 'Pro': 'P', + 'Pyl': 'O', 'Ser': 'S', 'Sec': 'U', 'Thr': 'T', 'Trp': 'W', + 'Tyr': 'Y', 'Val': 'V', 'Asx': 'B', 'Glx': 'Z', 'Xaa': 'X', + 'Xle': 'J', 'Ter': '*'} + hgvs_one=hgvs_three + for key in three_syntax_dict: + if key in hgvs_one: + hgvs_one = hgvs_one.replace(str(key),str(three_syntax_dict[key])) + + return hgvs_one def create_long(snp_file,snpsift_file,pangolin_file,software): @@ -55,13 +68,19 @@ def create_long(snp_file,snpsift_file,pangolin_file,software): for i in range(len(colnames_snpsift)): snpsift_table.rename(columns = {snpsift_table.columns[i]:colnames_snpsift[i]}, inplace = True) snpsift_table = snpsift_table.loc[:, ['CHROM','POS','REF','ALT','GENE','EFFECT','HGVS_C','HGVS_P']] - snpsift_table_copy = snpsift_table.copy() for i in range(len(snpsift_table_copy)): for j in range(3,8): snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] + oneletter_s = [] + for index,item in snpsift_table_copy["HGVS_P"].iteritems(): + hgvs_p_oneletter = aa_three_to_one_letter(str(item)) + oneletter_s.append(hgvs_p_oneletter) + + snpsift_table_copy["HGVS_P_1Letter"] = pd.Series(oneletter_s) + #format of lineages pangolin_table = pd.read_csv(pangolin_file, sep=",", header = "infer") lineages = pangolin_table.loc[:,['taxon','lineage']] From b2bb3195d9561fe3de427c0a3cceba12e2c54b56 Mon Sep 17 00:00:00 2001 From: svarona Date: Fri, 28 Jan 2022 12:14:48 +0100 Subject: [PATCH 170/238] Added bcftools norm config --- conf/modules_illumina.config | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index b22fd9c3..c70c48e4 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -427,6 +427,16 @@ if (!params.skip_variants) { ] ] } + + withName: 'BCFTOOLS_NORM' { + ext.args = '--do-not-normalize --output-type z --multiallelics -any' + ext.prefix = { "${meta.id}.norm" } + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: "*.norm.vcf.gz" + ] + } } } From 8e212d023b028357b647650fb789c3ccc4a23934 Mon Sep 17 00:00:00 2001 From: svarona Date: Fri, 28 Jan 2022 12:15:33 +0100 Subject: [PATCH 171/238] Added bcftools norm to split multiallelic positions in bcftools --- subworkflows/local/variants_bcftools.nf | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index 100d25dc..8ebd845e 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -3,6 +3,7 @@ // include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' +include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm/main' include { VARIANTS_QC } from './variants_qc' workflow VARIANTS_BCFTOOLS { @@ -29,12 +30,22 @@ workflow VARIANTS_BCFTOOLS { ) ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) + // + // Split multiallelic positions + // + BCFTOOLS_NORM ( + BCFTOOLS_MPILEUP.out.vcf, + fasta + ) + ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) + + // // Run downstream tools for variants QC // VARIANTS_QC ( bam, - BCFTOOLS_MPILEUP.out.vcf, + BCFTOOLS_NORM.out.vcf, BCFTOOLS_MPILEUP.out.stats, fasta, sizes, @@ -46,10 +57,12 @@ workflow VARIANTS_BCFTOOLS { ch_versions = ch_versions.mix(VARIANTS_QC.out.versions) emit: - vcf = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] + vcf_orig = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] + vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] + snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] snpeff_tbi = VARIANTS_QC.out.snpeff_tbi // channel: [ val(meta), [ tbi ] ] snpeff_stats = VARIANTS_QC.out.snpeff_stats // channel: [ val(meta), [ txt ] ] From c10136bda1b4ceea21994c42f7e265db3d565609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 12:22:23 +0100 Subject: [PATCH 172/238] change name create long table script --- bin/create_long_table.py | 154 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100755 bin/create_long_table.py diff --git a/bin/create_long_table.py b/bin/create_long_table.py new file mode 100755 index 00000000..b4c8539e --- /dev/null +++ b/bin/create_long_table.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python + +from matplotlib import table +import pandas as pd +import sys +import numpy as np +import re +import argparse +import glob, os +import io + +pd.set_option('display.max_columns', None) +pd.set_option('display.max_rows', None) + + +def parser_args(args=None): + Description = 'Create long/wide tables fo ivar/bcftools' + Epilog = """Example usage: python parser_ivar_bcftools.py """ + parser = argparse.ArgumentParser(description=Description, epilog=Epilog) + parser.add_argument('-sample', help="Input sample table files path." ) + parser.add_argument('-snpsift', help="Input snpsift txt files path.",required=True) + parser.add_argument('-pangolin', help="Input pangolin csv files path.",required=True) + parser.add_argument('-software', help="Input bcftools of ivar.",required=True) + + return parser.parse_args(args) + +def aa_three_to_one_letter(hgvs_three): + three_syntax_dict= {'Ala': 'A', 'Arg': 'R', 'Asn': 'N', 'Asp': 'D', 'Cys': 'C', + 'Gln': 'Q', 'Glu': 'E', 'Gly': 'G', 'His': 'H', 'Ile': 'I', + 'Leu': 'L', 'Lys': 'K', 'Met': 'M', 'Phe': 'F', 'Pro': 'P', + 'Pyl': 'O', 'Ser': 'S', 'Sec': 'U', 'Thr': 'T', 'Trp': 'W', + 'Tyr': 'Y', 'Val': 'V', 'Asx': 'B', 'Glx': 'Z', 'Xaa': 'X', + 'Xle': 'J', 'Ter': '*'} + hgvs_one=hgvs_three + for key in three_syntax_dict: + if key in hgvs_one: + hgvs_one = hgvs_one.replace(str(key),str(three_syntax_dict[key])) + + return hgvs_one + +def create_long(snp_file,snpsift_file,pangolin_file,software): + + ### format of sample table + snp_table=pd.read_table(snp_file, header='infer') + snp_table = snp_table.dropna(how = 'all', axis =1) + + if software=='bcftools': + snp_table.rename(columns={snp_table.columns[5]: "DP",snp_table.columns[6]: "AD"}, inplace=True) + new_column = snp_table + new_column[['REF_DP','ALT_DP']] = snp_table['AD'].str.split(',', expand=True) + snp_table = pd.merge(snp_table,new_column,how = 'left') + snp_table[["ALT_DP", "DP"]] = snp_table[["ALT_DP", "DP"]].apply(pd.to_numeric) + snp_table['AF']=snp_table['ALT_DP']/snp_table['DP'] + snp_table['AF'] = snp_table['AF'].round(2) + snp_table = snp_table.loc[:, ~snp_table.columns.str.contains('AD')] + + elif software=='ivar': + snp_table.rename(columns={snp_table.columns[0]: "CHROM",snp_table.columns[1]: "POS",snp_table.columns[2]: "REF",snp_table.columns[3]: "ALT",snp_table.columns[4]: "FILTER",snp_table.columns[5]: "DP",snp_table.columns[6]: "REF_DP", snp_table.columns[7]: "ALT_DP"}, inplace=True) + snp_table[["ALT_DP", "DP"]] = snp_table[["ALT_DP", "DP"]].apply(pd.to_numeric) + snp_table['AF']=snp_table['ALT_DP']/snp_table['DP'] + snp_table['AF'] = snp_table['AF'].round(2) + + ### format of snpsift table + snpsift_table = pd.read_csv(snpsift_file, sep="\t", header = "infer") + snpsift_table = snpsift_table.loc[:, ~snpsift_table.columns.str.contains('^Unnamed')] + colnames_snpsift = list(snpsift_table.columns) + colnames_snpsift = [i.replace('ANN[*].', '') for i in colnames_snpsift] + for i in range(len(colnames_snpsift)): + snpsift_table.rename(columns = {snpsift_table.columns[i]:colnames_snpsift[i]}, inplace = True) + snpsift_table = snpsift_table.loc[:, ['CHROM','POS','REF','ALT','GENE','EFFECT','HGVS_C','HGVS_P']] + snpsift_table_copy = snpsift_table.copy() + + for i in range(len(snpsift_table_copy)): + for j in range(3,8): + snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] + + oneletter_s = [] + for index,item in snpsift_table_copy["HGVS_P"].iteritems(): + hgvs_p_oneletter = aa_three_to_one_letter(str(item)) + oneletter_s.append(hgvs_p_oneletter) + + snpsift_table_copy["HGVS_P_1Letter"] = pd.Series(oneletter_s) + + #format of lineages + pangolin_table = pd.read_csv(pangolin_file, sep=",", header = "infer") + lineages = pangolin_table.loc[:,['taxon','lineage']] + + #table long one sample + tl_onesample = pd.DataFrame(data =snp_table) + if software=='bcftools': + tl_onesample["Sample"] = lineages.iloc[0,0] + elif software=='ivar': + tl_onesample["Sample"] = lineages.iloc[0,0].split('_')[1].split('.')[0] + tl_onesample["software"] = software + tl_onesample["Lineage"] = lineages.iloc[0,1] + + merged_table_long = pd.merge(tl_onesample,snpsift_table_copy,how = 'outer') + + return(merged_table_long) + +def same_len(list): + return len(set(list)) == 1 + +def concatenatetable(path): + all_filenames = [file for file in glob.glob(path + '/*.csv')] + + dataframe_list = [] + for file in all_filenames: + dataframe_list.append(pd.read_csv(file,sep=",")) + merged_df = pd.concat(dataframe_list) + return merged_df + +def main(args=None): + args = parser_args(args) + + # List vcf table files + table_list = [] + for file in glob.glob(args.sample + "/*_norm.table"): + table_list.append(file) + table_list.sort() + + #List snpsift files + snpsift_list = [] + for file in glob.glob(args.snpsift + "/*_norm.snpsift.txt"): + snpsift_list.append(file) + snpsift_list.sort() + + # List pangolin files + pangolin_list = [] + for file in glob.glob(args.pangolin + "/*.csv"): + pangolin_list.append(file) + pangolin_list.sort() + + if not same_len([len(table_list),len(snpsift_list),len(pangolin_list)]): + print("not same number of files for variants, snpsift and pangolin results ") + exit() + + sample_names = [os.path.basename(filename).split("_norm.table")[0] for filename in table_list] + + #create SampleTables folder in the folder where the script is running + if os.path.exists("SampleTables"): + "SampleTables already exists from previous run, please delete or rename the folder." + else: + os.mkdir("SampleTables") + + for sample_name,table,snpsift,pangolin in zip(sample_names,table_list,snpsift_list,pangolin_list): + long_table_sample = create_long(table,snpsift,pangolin,args.software) + long_table_sample.to_csv('./SampleTables/'+ sample_name + '.csv', header='infer', index=None, sep=',', mode='a') + + merged_df= concatenatetable("./SampleTables") + merged_df.to_csv("final_long_table.csv", index=False, encoding='utf-8-sig') + +if __name__ == '__main__': + sys.exit(main()) From e93fbef170c1892ad9c56c669b347ec3b3beebaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 16:15:14 +0100 Subject: [PATCH 173/238] Changed args name to create long table script, create module for create long table --- modules/local/create_long_table.nf | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 modules/local/create_long_table.nf diff --git a/modules/local/create_long_table.nf b/modules/local/create_long_table.nf new file mode 100644 index 00000000..a57d944c --- /dev/null +++ b/modules/local/create_long_table.nf @@ -0,0 +1,23 @@ +process CREATE_LONG_TABLE { + tag "$meta.id" + + conda (params.enable_conda ? "conda-forge::python=3.9.5 conda-forge::matplotlib=3.5.1 conda-forge::pandas=1.3.5 conda-forge::r-sys=3.4 conda-forge::regex=2021.11.10 conda-forge::scipy=1.7.3" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/mulled-v2-77320db00eefbbf8c599692102c3d387a37ef02a:08144a66f00dc7684fad061f1466033c0176e7ad-0' : + 'quay.io/biocontainers/mulled-v2-77320db00eefbbf8c599692102c3d387a37ef02a:08144a66f00dc7684fad061f1466033c0176e7ad-0' }" + + input: + path ('variants_table/*') + path ('snpsift/*') + path ('pangolin/*') + + output: + path "variants_long_table.csv", optional:true, emit: csv_variants + + script: + def args = task.ext.args ?: '' + """ + create_long_table.py --samples_path ./variants_table --snpsift_path ./snpsift --pangolin_path ./pangolin $args + + """ +} From 19977af7a6f050a037f2b5c954c871611a11a4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 16:15:28 +0100 Subject: [PATCH 174/238] Changed args name to create long table script, create module for create long table --- bin/create_long_table.py | 11 +++--- conf/modules_illumina.config | 25 ++++++++++++ subworkflows/local/long_table.nf | 63 +++++++++++++++++++++++++++++++ subworkflows/local/variants_qc.nf | 6 ++- 4 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 subworkflows/local/long_table.nf diff --git a/bin/create_long_table.py b/bin/create_long_table.py index b4c8539e..d1e1de81 100755 --- a/bin/create_long_table.py +++ b/bin/create_long_table.py @@ -17,10 +17,11 @@ def parser_args(args=None): Description = 'Create long/wide tables fo ivar/bcftools' Epilog = """Example usage: python parser_ivar_bcftools.py """ parser = argparse.ArgumentParser(description=Description, epilog=Epilog) - parser.add_argument('-sample', help="Input sample table files path." ) - parser.add_argument('-snpsift', help="Input snpsift txt files path.",required=True) - parser.add_argument('-pangolin', help="Input pangolin csv files path.",required=True) - parser.add_argument('-software', help="Input bcftools of ivar.",required=True) + parser.add_argument('--samples_path','-s',dest="sample", help="Input sample table files path.", required=True ) + parser.add_argument('--snpsift_path','-a',dest="snpsift",help="Input snpsift txt files path.",required=True) + parser.add_argument('--pangolin_path','-l',dest="pangolin",help="Input pangolin csv files path.",required=True) + parser.add_argument('--software','-p',dest="software",help="Input bcftools of ivar.",required=True) + parser.add_argument('--output','-o',dest="output",help="Output filename",required=True) return parser.parse_args(args) @@ -148,7 +149,7 @@ def main(args=None): long_table_sample.to_csv('./SampleTables/'+ sample_name + '.csv', header='infer', index=None, sep=',', mode='a') merged_df= concatenatetable("./SampleTables") - merged_df.to_csv("final_long_table.csv", index=False, encoding='utf-8-sig') + merged_df.to_csv(args.output, index=False, encoding='utf-8-sig') if __name__ == '__main__': sys.exit(main()) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index c70c48e4..2dd86ac6 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -399,6 +399,18 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } + if (!params.skip_long_table) { + withName: 'CREATE_LONG_TABLE' { + ext.args = "--output variants_long_table.csv --software ivar" + publishDir = [ + [ + path: { "${params.outdir}/variants" }, + mode: 'copy', + pattern: '*.csv' + ] + ] + } + } } } @@ -437,6 +449,18 @@ if (!params.skip_variants) { pattern: "*.norm.vcf.gz" ] } + if (!params.skip_long_table) { + withName: 'CREATE_LONG_TABLE' { + ext.args = "--output variants_long_table.csv --software ivar" + publishDir = [ + [ + path: { "${params.outdir}/variants" }, + mode: 'copy', + pattern: '*.csv' + ] + ] + } + } } } @@ -1020,6 +1044,7 @@ if (!params.skip_assembly) { } } + if (!params.skip_multiqc) { process { withName: 'MULTIQC' { diff --git a/subworkflows/local/long_table.nf b/subworkflows/local/long_table.nf new file mode 100644 index 00000000..05ebe2a5 --- /dev/null +++ b/subworkflows/local/long_table.nf @@ -0,0 +1,63 @@ +// +// Variant calling with BCFTools, downstream processing and QC +// + +include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' +include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm/main' +include { VARIANTS_QC } from './variants_qc' + +workflow LONG_TABLE { + take: + vcf // channel: [ val(meta), [ bam ] ] + pangolin // channel: /path/to/genome.fasta + snpsift // channel: /path/to/genome.sizes + fasta // channel: /path/to/genome.gff + + main: + + ch_versions = Channel.empty() + + // Split multiallelic positions + // + BCFTOOLS_NORM ( + BCFTOOLS_MPILEUP.out.vcf, + fasta + ) + ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) + + + // + // Run downstream tools for variants QC + // + VARIANTS_QC ( + bam, + BCFTOOLS_NORM.out.vcf, + BCFTOOLS_MPILEUP.out.stats, + fasta, + sizes, + gff, + bed, + snpeff_db, + snpeff_config + ) + ch_versions = ch_versions.mix(VARIANTS_QC.out.versions) + + emit: + vcf_orig = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] + + vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] + + snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] + snpeff_tbi = VARIANTS_QC.out.snpeff_tbi // channel: [ val(meta), [ tbi ] ] + snpeff_stats = VARIANTS_QC.out.snpeff_stats // channel: [ val(meta), [ txt ] ] + snpeff_csv = VARIANTS_QC.out.snpeff_csv // channel: [ val(meta), [ csv ] ] + snpeff_txt = VARIANTS_QC.out.snpeff_txt // channel: [ val(meta), [ txt ] ] + snpeff_html = VARIANTS_QC.out.snpeff_html // channel: [ val(meta), [ html ] ] + snpsift_txt = VARIANTS_QC.out.snpsift_txt // channel: [ val(meta), [ txt ] ] + + asciigenome_pdf = VARIANTS_QC.out.asciigenome_pdf // channel: [ val(meta), [ pdf ] ] + + versions = ch_versions // channel: [ versions.yml ] +} diff --git a/subworkflows/local/variants_qc.nf b/subworkflows/local/variants_qc.nf index cded165c..2eedd715 100644 --- a/subworkflows/local/variants_qc.nf +++ b/subworkflows/local/variants_qc.nf @@ -1,7 +1,7 @@ // // Variant calling QC // - +include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm' include { ASCIIGENOME } from '../../modules/local/asciigenome' include { SNPEFF_SNPSIFT } from './snpeff_snpsift' @@ -21,6 +21,10 @@ workflow VARIANTS_QC { ch_versions = Channel.empty() + // + // Bcftools norm for multiallelic variants in different lines + // + // // Annotate variants // From 2a44248d759a253ae2fd4e38add78d9a0db22fff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 16:16:48 +0100 Subject: [PATCH 175/238] installed bcftools query module --- modules.json | 3 + .../nf-core/modules/bcftools/query/main.nf | 41 ++++++++++++ .../nf-core/modules/bcftools/query/meta.yml | 62 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 modules/nf-core/modules/bcftools/query/main.nf create mode 100644 modules/nf-core/modules/bcftools/query/meta.yml diff --git a/modules.json b/modules.json index 715ad456..c6e50de9 100644 --- a/modules.json +++ b/modules.json @@ -27,6 +27,9 @@ "bcftools/norm": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, + "bcftools/query": { + "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + }, "bcftools/stats": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, diff --git a/modules/nf-core/modules/bcftools/query/main.nf b/modules/nf-core/modules/bcftools/query/main.nf new file mode 100644 index 00000000..5f4135f4 --- /dev/null +++ b/modules/nf-core/modules/bcftools/query/main.nf @@ -0,0 +1,41 @@ +process BCFTOOLS_QUERY { + tag "$meta.id" + label 'process_medium' + + conda (params.enable_conda ? 'bioconda::bcftools=1.14' : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bcftools:1.14--h88f3f91_0' : + 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" + + input: + tuple val(meta), path(vcf), path(index) + path(regions) + path(targets) + path(samples) + + output: + tuple val(meta), path("*.gz") , emit: vcf + path "versions.yml" , emit: versions + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def regions_file = regions ? "--regions-file ${regions}" : "" + def targets_file = targets ? "--targets-file ${targets}" : "" + def samples_file = samples ? "--samples-file ${samples}" : "" + + """ + bcftools query \\ + --output ${prefix}.vcf.gz \\ + ${regions_file} \\ + ${targets_file} \\ + ${samples_file} \\ + $args \\ + ${vcf} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + bcftools: \$(bcftools --version 2>&1 | head -n1 | sed 's/^.*bcftools //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/modules/nf-core/modules/bcftools/query/meta.yml b/modules/nf-core/modules/bcftools/query/meta.yml new file mode 100644 index 00000000..e450f73e --- /dev/null +++ b/modules/nf-core/modules/bcftools/query/meta.yml @@ -0,0 +1,62 @@ +name: bcftools_query +description: Extracts fields from VCF or BCF files and outputs them in user-defined format. +keywords: + - query + - variant calling + - bcftools + - VCF +tools: + - query: + description: | + Extracts fields from VCF or BCF files and outputs them in user-defined format. + homepage: http://samtools.github.io/bcftools/bcftools.html + documentation: http://www.htslib.org/doc/bcftools.html + doi: 10.1093/bioinformatics/btp352 + licence: ['MIT'] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: | + The vcf file to be qeuried. + e.g. 'file.vcf' + - index: + type: file + description: | + The tab index for the VCF file to be inspected. + e.g. 'file.tbi' + - regions: + type: file + description: | + Optionally, restrict the operation to regions listed in this file. + e.g. 'file.vcf' + - targets: + type: file + description: | + Optionally, restrict the operation to regions listed in this file (doesn't rely upon index files) + e.g. 'file.vcf' + - samples: + type: file + description: | + Optional, file of sample names to be included or excluded. + e.g. 'file.tsv' +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: VCF query output file + pattern: "*.{vcf.gz}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@abhi18av" From 0dd18db6567b72a1533dbd9ab071b4015b3050fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 16:51:48 +0100 Subject: [PATCH 176/238] add param in nextflowconfig --- nextflow.config | 1 + subworkflows/local/long_table.nf | 63 ------------------------------- subworkflows/local/variants_qc.nf | 6 +-- 3 files changed, 2 insertions(+), 68 deletions(-) delete mode 100644 subworkflows/local/long_table.nf diff --git a/nextflow.config b/nextflow.config index c28cc0a9..ef9d6a53 100644 --- a/nextflow.config +++ b/nextflow.config @@ -75,6 +75,7 @@ params { skip_consensus_plots = false skip_consensus = false skip_variants = false + skip_long_table = false // Illumina de novo assembly options assemblers = 'spades' diff --git a/subworkflows/local/long_table.nf b/subworkflows/local/long_table.nf deleted file mode 100644 index 05ebe2a5..00000000 --- a/subworkflows/local/long_table.nf +++ /dev/null @@ -1,63 +0,0 @@ -// -// Variant calling with BCFTools, downstream processing and QC -// - -include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' -include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm/main' -include { VARIANTS_QC } from './variants_qc' - -workflow LONG_TABLE { - take: - vcf // channel: [ val(meta), [ bam ] ] - pangolin // channel: /path/to/genome.fasta - snpsift // channel: /path/to/genome.sizes - fasta // channel: /path/to/genome.gff - - main: - - ch_versions = Channel.empty() - - // Split multiallelic positions - // - BCFTOOLS_NORM ( - BCFTOOLS_MPILEUP.out.vcf, - fasta - ) - ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) - - - // - // Run downstream tools for variants QC - // - VARIANTS_QC ( - bam, - BCFTOOLS_NORM.out.vcf, - BCFTOOLS_MPILEUP.out.stats, - fasta, - sizes, - gff, - bed, - snpeff_db, - snpeff_config - ) - ch_versions = ch_versions.mix(VARIANTS_QC.out.versions) - - emit: - vcf_orig = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] - stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] - - vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] - - snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] - snpeff_tbi = VARIANTS_QC.out.snpeff_tbi // channel: [ val(meta), [ tbi ] ] - snpeff_stats = VARIANTS_QC.out.snpeff_stats // channel: [ val(meta), [ txt ] ] - snpeff_csv = VARIANTS_QC.out.snpeff_csv // channel: [ val(meta), [ csv ] ] - snpeff_txt = VARIANTS_QC.out.snpeff_txt // channel: [ val(meta), [ txt ] ] - snpeff_html = VARIANTS_QC.out.snpeff_html // channel: [ val(meta), [ html ] ] - snpsift_txt = VARIANTS_QC.out.snpsift_txt // channel: [ val(meta), [ txt ] ] - - asciigenome_pdf = VARIANTS_QC.out.asciigenome_pdf // channel: [ val(meta), [ pdf ] ] - - versions = ch_versions // channel: [ versions.yml ] -} diff --git a/subworkflows/local/variants_qc.nf b/subworkflows/local/variants_qc.nf index 2eedd715..db91abeb 100644 --- a/subworkflows/local/variants_qc.nf +++ b/subworkflows/local/variants_qc.nf @@ -1,7 +1,7 @@ // // Variant calling QC // -include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm' +include { BCFTOOLS_QUERY } from '../../modules/nf-core/modules/bcftools/query/main' include { ASCIIGENOME } from '../../modules/local/asciigenome' include { SNPEFF_SNPSIFT } from './snpeff_snpsift' @@ -21,10 +21,6 @@ workflow VARIANTS_QC { ch_versions = Channel.empty() - // - // Bcftools norm for multiallelic variants in different lines - // - // // Annotate variants // From 59dedafc69aa4f14e6c3965438223764cb96a507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 17:41:44 +0100 Subject: [PATCH 177/238] added bgzip tabix module, emit tbi --- subworkflows/local/variants_bcftools.nf | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index 8ebd845e..ccd6ccd0 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -39,13 +39,17 @@ workflow VARIANTS_BCFTOOLS { ) ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) + VCF_BGZIP_TABIX_STATS ( + BCFTOOLS_NORM.out.vcf + ) + ch_versions = ch_versions.mix(VCF_BGZIP_TABIX_STATS.out.versions) // // Run downstream tools for variants QC // VARIANTS_QC ( bam, - BCFTOOLS_NORM.out.vcf, + VCF_BGZIP_TABIX_STATS.out.vcf, BCFTOOLS_MPILEUP.out.stats, fasta, sizes, @@ -58,10 +62,10 @@ workflow VARIANTS_BCFTOOLS { emit: vcf_orig = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] - vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] + vcf = VCF_BGZIP_TABIX_STATS.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = VCF_BGZIP_TABIX_STATS.out.tbi // channel: [ val(meta), [ vcf ] ] snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] snpeff_tbi = VARIANTS_QC.out.snpeff_tbi // channel: [ val(meta), [ tbi ] ] From 94071205b8cf722bf7f1b4ec392eb09118de833b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 17:46:32 +0100 Subject: [PATCH 178/238] Added call to new subworkflow long table --- subworkflows/local/variants_qc.nf | 2 -- workflows/illumina.nf | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/variants_qc.nf b/subworkflows/local/variants_qc.nf index db91abeb..111e983f 100644 --- a/subworkflows/local/variants_qc.nf +++ b/subworkflows/local/variants_qc.nf @@ -1,7 +1,6 @@ // // Variant calling QC // -include { BCFTOOLS_QUERY } from '../../modules/nf-core/modules/bcftools/query/main' include { ASCIIGENOME } from '../../modules/local/asciigenome' include { SNPEFF_SNPSIFT } from './snpeff_snpsift' @@ -48,7 +47,6 @@ workflow VARIANTS_QC { ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) } - // // Variant screenshots with ASCIIGenome // ch_asciigenome_pdf = Channel.empty() diff --git a/workflows/illumina.nf b/workflows/illumina.nf index c9ec299e..6a98065c 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -73,6 +73,7 @@ include { VARIANTS_IVAR } from '../subworkflows/local/variants_ivar' include { VARIANTS_BCFTOOLS } from '../subworkflows/local/variants_bcftools' include { CONSENSUS_IVAR } from '../subworkflows/local/consensus_ivar' include { CONSENSUS_BCFTOOLS } from '../subworkflows/local/consensus_bcftools' +include { LONG_TABLE } from '../subworkflows/local/long_table' include { ASSEMBLY_SPADES } from '../subworkflows/local/assembly_spades' include { ASSEMBLY_UNICYCLER } from '../subworkflows/local/assembly_unicycler' include { ASSEMBLY_MINIA } from '../subworkflows/local/assembly_minia' @@ -407,6 +408,7 @@ workflow ILLUMINA { ch_ivar_counts_multiqc = VARIANTS_IVAR.out.multiqc_tsv ch_ivar_stats_multiqc = VARIANTS_IVAR.out.stats ch_ivar_snpeff_multiqc = VARIANTS_IVAR.out.snpeff_csv + ch_snpsift_ltable = VARIANTS_IVAR.out.snpsift_txt ch_versions = ch_versions.mix(VARIANTS_IVAR.out.versions) } @@ -427,6 +429,7 @@ workflow ILLUMINA { ) ch_vcf = VARIANTS_BCFTOOLS.out.vcf ch_tbi = VARIANTS_BCFTOOLS.out.tbi + ch_snpsift_ltable = VARIANTS_BCFTOOLS.out.snpsift_txt ch_bcftools_stats_multiqc = VARIANTS_BCFTOOLS.out.stats ch_bcftools_snpeff_multiqc = VARIANTS_BCFTOOLS.out.snpeff_csv ch_versions = ch_versions.mix(VARIANTS_BCFTOOLS.out.versions) @@ -447,6 +450,7 @@ workflow ILLUMINA { ) ch_ivar_quast_multiqc = CONSENSUS_IVAR.out.quast_tsv ch_ivar_pangolin_multiqc = CONSENSUS_IVAR.out.pangolin_report + ch_pangolin_ltable = CONSENSUS_IVAR.out.pangolin_report ch_ivar_nextclade_report = CONSENSUS_IVAR.out.nextclade_report ch_versions = ch_versions.mix(CONSENSUS_IVAR.out.versions) @@ -484,6 +488,7 @@ workflow ILLUMINA { PREPARE_GENOME.out.nextclade_db ) ch_bcftools_quast_multiqc = CONSENSUS_BCFTOOLS.out.quast_tsv + ch_pangolin_ltable = CONSENSUS_BCFTOOLS.out.pangolin_report ch_bcftools_pangolin_multiqc = CONSENSUS_BCFTOOLS.out.pangolin_report ch_bcftools_nextclade_report = CONSENSUS_BCFTOOLS.out.nextclade_report ch_versions = ch_versions.mix(CONSENSUS_BCFTOOLS.out.versions) @@ -506,6 +511,21 @@ workflow ILLUMINA { .set { ch_bcftools_nextclade_multiqc } } + // + // SUBWORKFLOW: Create variants long table report + // + + if (!params.skip_consensus && !params.skip_long_table && variant_caller) { + LONG_TABLE ( + ch_vcf, + ch_tbi, + ch_snpsift_ltable, + ch_pangolin_ltable + ) + ch_versions = ch_versions.mix(LONG_TABLE.out.versions) + } + + // // MODULE: Primer trimming with Cutadapt // From 657e916512eba183a8f0f862abcff141e7eda2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 17:46:52 +0100 Subject: [PATCH 179/238] add logit to modules_illumina config --- conf/modules_illumina.config | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 2dd86ac6..6babf506 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -449,9 +449,26 @@ if (!params.skip_variants) { pattern: "*.norm.vcf.gz" ] } + + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:TABIX_BGZIP' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:.*:TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } if (!params.skip_long_table) { withName: 'CREATE_LONG_TABLE' { - ext.args = "--output variants_long_table.csv --software ivar" + ext.args = "--output variants_long_table.csv --software bcftools" publishDir = [ [ path: { "${params.outdir}/variants" }, From 5be074fc28eaa7d880e8a605c07b1892342e5150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 17:47:26 +0100 Subject: [PATCH 180/238] added new subworflow long table --- subworkflows/local/long_table.nf | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 subworkflows/local/long_table.nf diff --git a/subworkflows/local/long_table.nf b/subworkflows/local/long_table.nf new file mode 100644 index 00000000..5e648b56 --- /dev/null +++ b/subworkflows/local/long_table.nf @@ -0,0 +1,40 @@ +// +// Variant calling QC +// +include { BCFTOOLS_QUERY } from '../../modules/nf-core/modules/bcftools/query/main' +include { CREATE_LONG_TABLE } from '../../modules/local/create_long_table' + +workflow VARIANTS_QC { + take: + vcf // channel: [ val(meta), [ vcf ] ] + tbi // channel: [ val(meta), [ tbi ] ] + pangolin // channel: [ val(meta), [ ] ] + snpsift // channel: [ val(meta), [ ] ] + + main: + + ch_versions = Channel.empty() + + if (!params.skip_long_table) { + BCFTOOLS_QUERY ( + vcf.join(tbi, by: [0]), + "", + "", + "" + ) + ch_query_table = BCFTOOLS_QUERY.out.vcf + ch_versions = ch_versions.mix(BCFTOOLS_QUERY.out.versions) + + CREATE_LONG_TABLE ( + ch_query_table.collect[it[1]], + snpsift, + pangolin + ) + } + + emit: + longtable_csv = ch_long_table // channel: [ val(meta), [ vcf.gz ] ] + query_table = ch_query_table // channel: [ val(meta), [ txt ] ] + + versions = ch_versions // channel: [ versions.yml ] +} From 285651339a5748beda686461f2c811d896090456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 17:55:48 +0100 Subject: [PATCH 181/238] typo fixes --- modules/local/create_long_table.nf | 2 +- subworkflows/local/long_table.nf | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/local/create_long_table.nf b/modules/local/create_long_table.nf index a57d944c..05267a77 100644 --- a/modules/local/create_long_table.nf +++ b/modules/local/create_long_table.nf @@ -12,7 +12,7 @@ process CREATE_LONG_TABLE { path ('pangolin/*') output: - path "variants_long_table.csv", optional:true, emit: csv_variants + path "*.csv", optional:true, emit: csv_variants script: def args = task.ext.args ?: '' diff --git a/subworkflows/local/long_table.nf b/subworkflows/local/long_table.nf index 5e648b56..f9f590c4 100644 --- a/subworkflows/local/long_table.nf +++ b/subworkflows/local/long_table.nf @@ -4,7 +4,7 @@ include { BCFTOOLS_QUERY } from '../../modules/nf-core/modules/bcftools/query/main' include { CREATE_LONG_TABLE } from '../../modules/local/create_long_table' -workflow VARIANTS_QC { +workflow LONG_TABLE { take: vcf // channel: [ val(meta), [ vcf ] ] tbi // channel: [ val(meta), [ tbi ] ] @@ -26,10 +26,11 @@ workflow VARIANTS_QC { ch_versions = ch_versions.mix(BCFTOOLS_QUERY.out.versions) CREATE_LONG_TABLE ( - ch_query_table.collect[it[1]], - snpsift, - pangolin + ch_query_table.collect{it[1]}, + snpsift.collect{it[1]}, + pangolin.collect{it[1]} ) + ch_long_table = CREATE_LONG_TABLE.out.csv_variants } emit: From 0c262a6849ff5887bbdbd83f618366338a104a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 18:05:09 +0100 Subject: [PATCH 182/238] added bcftools query args to config --- conf/modules_illumina.config | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 6babf506..4007f127 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -400,6 +400,9 @@ if (!params.skip_variants) { ] } if (!params.skip_long_table) { + withName: 'BCFTOOLS_QUERY' { + ext.args = "--H -f '%CHROM\t%POS\t%REF\t%ALT\t%FILTER\t[%DP\t]\t[%REF_DP\t]\t[%ALT_DP\t]\n'" + } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software ivar" publishDir = [ @@ -467,6 +470,9 @@ if (!params.skip_variants) { ] } if (!params.skip_long_table) { + withName: 'BCFTOOLS_QUERY' { + ext.args = "-H -f '%CHROM\t%POS\t%REF\t%ALT\t%FILTER\t[%DP\t]\t[%AD\t]\n'" + } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software bcftools" publishDir = [ From 560c9d016970565b969255d5b9aa4f2116500de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Fri, 28 Jan 2022 19:05:52 +0100 Subject: [PATCH 183/238] add versions.yml to create long table module --- modules/local/create_long_table.nf | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/local/create_long_table.nf b/modules/local/create_long_table.nf index 05267a77..31aec66b 100644 --- a/modules/local/create_long_table.nf +++ b/modules/local/create_long_table.nf @@ -14,10 +14,17 @@ process CREATE_LONG_TABLE { output: path "*.csv", optional:true, emit: csv_variants - script: + path "versions.yml" , emit: versions + + script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ def args = task.ext.args ?: '' + """ create_long_table.py --samples_path ./variants_table --snpsift_path ./snpsift --pangolin_path ./pangolin $args + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS """ } From d619b83c9c7f2aeae2d6beee281b4df1cf8266de Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 28 Jan 2022 21:10:32 +0000 Subject: [PATCH 184/238] Update container for make_bed_mask --- modules/local/make_bed_mask.nf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/local/make_bed_mask.nf b/modules/local/make_bed_mask.nf index 18d060cc..81546c4b 100644 --- a/modules/local/make_bed_mask.nf +++ b/modules/local/make_bed_mask.nf @@ -1,10 +1,10 @@ process MAKE_BED_MASK { tag "$meta.id" - conda (params.enable_conda ? "conda-forge::python=3.9.5 conda-forge::gawk=5.1.0 bioconda::samtools=1.12" : null) + conda (params.enable_conda ? "conda-forge::python=3.9.5 bioconda::samtools=1.14" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/plasmidid:1.6.5--hdfd78af_0' : - 'quay.io/biocontainers/plasmidid:1.6.5--hdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/mulled-v2-1a35167f7a491c7086c13835aaa74b39f1f43979:6b5cffa1187cfccf2dc983ed3b5359d49b999eb0-0' : + 'quay.io/biocontainers/mulled-v2-1a35167f7a491c7086c13835aaa74b39f1f43979:6b5cffa1187cfccf2dc983ed3b5359d49b999eb0-0' }" input: tuple val(meta), path(bam), path(vcf) From af58aff59d079be150768f21b5b2948c29d45b4c Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 28 Jan 2022 21:10:46 +0000 Subject: [PATCH 185/238] Add new consensus route to CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 577c355c..88ea5359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [[2.3](https://github.com/nf-core/viralrecon/releases/tag/2.3)] - 2022-01-28 +### :warning: Major enhancements + +* When using `--protocol amplicon`, in the previous release, iVar was used for both the variant calling and consensus sequence generation. The pipeline will now perform the variant calling and consensus sequence generation with iVar and BCFTools/BEDTools, respectively. +* Bump minimum Nextflow version from `21.04.0` -> `21.10.3` + ### Enhancements & fixes * Port pipeline to the updated Nextflow DSL2 syntax adopted on nf-core/modules -* Bump minimum Nextflow version from `21.04.0` -> `21.10.3` * Updated pipeline template to [nf-core/tools 2.2](https://github.com/nf-core/tools/releases/tag/2.2) * [[#209](https://github.com/nf-core/viralrecon/issues/209)] - Check that contig in primer BED and genome fasta match * [[#218](https://github.com/nf-core/viralrecon/issues/218)] - Support for compressed FastQ files for Nanopore data From f7c2aaf9f833386b27815b2af75e5dcac9079747 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Fri, 28 Jan 2022 21:10:57 +0000 Subject: [PATCH 186/238] Split out consensus sequence section --- docs/output.md | 90 +++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/docs/output.md b/docs/output.md index afa08c3b..afa67ba4 100644 --- a/docs/output.md +++ b/docs/output.md @@ -295,12 +295,13 @@ An example MultiQC report generated from a full-sized dataset can be viewed on t * [picard MarkDuplicates](#picard-markduplicates) - Duplicate read marking and removal * [picard CollectMultipleMetrics](#picard-collectmultiplemetrics) - Alignment metrics * [mosdepth](#mosdepth) - Whole-genome and amplicon coverage metrics - * [iVar variants and iVar consensus](#ivar-variants-and-ivar-consensus) *||* [BCFTools and BEDTools](#bcftools-and-bedtools) - Variant calling and consensus sequence generation + * [iVar variants](#ivar-variants) *||* [BCFTools call](#bcftools-call) - Variant calling * [SnpEff and SnpSift](#snpeff-and-snpsift) - Genetic variant annotation and functional effect prediction + * [ASCIIGenome](#asciigenome) - Individual variant screenshots with annotation tracks + * [iVar consensus](#ivar-consensus) *||* [BCFTools and BEDTools](#bcftools-and-bedtools) - Consensus sequence generation * [QUAST](#quast) - Consensus assessment report * [Pangolin](#pangolin) - Lineage analysis * [Nextclade](#nextclade) - Clade assignment, mutation calling and sequence quality checks - * [ASCIIGenome](#asciigenome) - Individual variant screenshots with annotation tracks * [De novo assembly](#illumina-de-novo-assembly) * [Cutadapt](#cutadapt) - Primer trimming for amplicon data * [SPAdes](#spades) *||* [Unicycler](#unicycler) *||* [minia](#minia) - Viral genome assembly @@ -503,7 +504,7 @@ Unless you are using [UMIs](https://emea.illumina.com/science/sequencing-method-

R - Sample per-amplicon coverage plot

-### iVar variants and iVar consensus +### iVar variants
Output files @@ -516,25 +517,16 @@ Unless you are using [UMIs](https://emea.illumina.com/science/sequencing-method- * `*.variant_counts.log`: Counts for type of variants called by iVar. * `variants/ivar/bcftools_stats/` * `*.bcftools_stats.txt`: Statistics and counts obtained from iVar variants VCF file. -* `variants//consensus/ivar/` - * `*.consensus.fa`: Consensus Fasta file generated by iVar. - * `*.consensus.qual.txt`: File with the average quality of each base in the consensus sequence. -* `variants//consensus/ivar/base_qc/` - * `*.ACTG_density.pdf`: Plot showing density of ACGT bases within the consensus sequence. - * `*.base_counts.pdf`: Plot showing frequency and percentages of all bases in consensus sequence. - * `*.base_counts.tsv`: File containing frequency and percentages of all bases in consensus sequence. - * `*.N_density.pdf`: Plot showing density of N bases within the consensus sequence. - * `*.N_run.tsv`: File containing start positions and width of N bases in consensus sequence.
-[iVar](https://github.com/andersen-lab/ivar/blob/master/docs/MANUAL.md) is a computational package that contains functions broadly useful for viral amplicon-based sequencing. We use iVar in this pipeline to [trim primer sequences](#ivar-trim) for amplicon input data as well as to call variants and for consensus sequence generation. +[iVar](https://github.com/andersen-lab/ivar/blob/master/docs/MANUAL.md) is a computational package that contains functions broadly useful for viral amplicon-based sequencing. We use iVar in this pipeline to [trim primer sequences](#ivar-trim) for amplicon input data as well as to call variants. -iVar outputs a tsv format, which is not compatible with downstream analysis such as annotation using snpeff. Moreover some issues need to be addressed such as [strand-bias filtering](https://github.com/andersen-lab/ivar/issues/5) and [consecutive variants belonging to the same codon](https://github.com/andersen-lab/ivar/issues/92). Viralrecon uses a custom python script [ivar_variants_to_vcf.py](https://github.com/nf-core/viralrecon/blob/master/bin/ivar_variants_to_vcf.py) to convert ivar default output to vcf addressing both issues. +iVar outputs a tsv format which is not compatible with downstream analysis such as annotation using SnpEff. Moreover some issues need to be addressed such as [strand-bias filtering](https://github.com/andersen-lab/ivar/issues/5) and [the consecutive reporting of variants belonging to the same codon](https://github.com/andersen-lab/ivar/issues/92). This pipeline uses a custom Python script [ivar_variants_to_vcf.py](https://github.com/nf-core/viralrecon/blob/master/bin/ivar_variants_to_vcf.py) to convert the default iVar output to VCF whilst also addressing both of these issues. ![MultiQC - iVar variants called plot](images/mqc_ivar_variants_plot.png) -### BCFTools and BEDTools +### BCFTools call
Output files @@ -544,24 +536,10 @@ iVar outputs a tsv format, which is not compatible with downstream analysis such * `*.vcf.gz.tbi`: Variants VCF index file. * `variants/bcftools/bcftools_stats/` * `*.bcftools_stats.txt`: Statistics and counts obtained from VCF file. -* `variants//consensus/bcftools/` - * `*.consensus.fa`: Consensus Fasta file generated by integrating the variants called by BCFTools into the reference genome. -* `variants//consensus/bcftools/base_qc/` - * `*.ACTG_density.pdf`: Plot showing density of ACGT bases within the consensus sequence. - * `*.base_counts.pdf`: Plot showing frequency and percentages of all bases in consensus sequence. - * `*.base_counts.tsv`: File containing frequency and percentages of all bases in consensus sequence. - * `*.N_density.pdf`: Plot showing density of N bases within the consensus sequence. - * `*.N_run.tsv`: File containing start positions and width of N bases in consensus sequence. - -**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic').
-[BCFtools](http://samtools.github.io/bcftools/bcftools.html) can be used to call variants directly from BAM alignment files. The functionality to call variants with BCFTools in this pipeline was inspired by work carried out by [Conor Walker](https://github.com/conorwalker/covid19/blob/3cb26ec399417bedb7e60487415c78a405f517d6/scripts/call_variants.sh). - -[BCFtools](http://samtools.github.io/bcftools/bcftools.html) is a set of utilities that manipulate variant calls in [VCF](https://vcftools.github.io/specs.html) and its binary counterpart BCF format. BCFTools is used in the variant calling and *de novo* assembly steps of this pipeline to obtain basic statistics from the VCF output. It can also used be used to generate a consensus sequence by integrating variant calls into the reference genome. - -[BEDTools](https://bedtools.readthedocs.io/en/latest/) is a swiss-army knife of tools for a wide-range of genomics analysis tasks. In this pipeline we use `bedtools genomecov` to compute the per-base mapped read coverage in bedGraph format, and `bedtools maskfasta` to mask sequences in a Fasta file based on intervals defined in a feature file. This may be useful for creating your own masked genome file based on custom annotations or for masking all but your target regions when aligning sequence data from a targeted capture experiment. +[BCFtools](http://samtools.github.io/bcftools/bcftools.html) can be used to call variants directly from BAM alignment files. It is a set of utilities that manipulate variant calls in [VCF](https://vcftools.github.io/specs.html) and its binary counterpart BCF format. BCFTools is used in the variant calling and *de novo* assembly steps of this pipeline to obtain basic statistics from the VCF output. ![MultiQC - BCFTools variant counts](images/mqc_bcftools_stats_plot.png) @@ -606,6 +584,47 @@ As described in the documentation, [ASCIIGenome](https://asciigenome.readthedocs

ASCIIGenome screenshot

+### iVar consensus + +
+Output files + +* `variants//consensus/ivar/` + * `*.consensus.fa`: Consensus Fasta file generated by iVar. + * `*.consensus.qual.txt`: File with the average quality of each base in the consensus sequence. +* `variants//consensus/ivar/base_qc/` + * `*.ACTG_density.pdf`: Plot showing density of ACGT bases within the consensus sequence. + * `*.base_counts.pdf`: Plot showing frequency and percentages of all bases in consensus sequence. + * `*.base_counts.tsv`: File containing frequency and percentages of all bases in consensus sequence. + * `*.N_density.pdf`: Plot showing density of N bases within the consensus sequence. + * `*.N_run.tsv`: File containing start positions and width of N bases in consensus sequence. + +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). + +
+ +As described in the [iVar variants](#ivar-variants) section, iVar can be used in this pipeline to call variants and for the consensus sequence generation. + +### BCFTools and BEDTools + +
+Output files + +* `variants//consensus/bcftools/` + * `*.consensus.fa`: Consensus Fasta file generated by integrating the variants called by BCFTools into the reference genome. +* `variants//consensus/bcftools/base_qc/` + * `*.ACTG_density.pdf`: Plot showing density of ACGT bases within the consensus sequence. + * `*.base_counts.pdf`: Plot showing frequency and percentages of all bases in consensus sequence. + * `*.base_counts.tsv`: File containing frequency and percentages of all bases in consensus sequence. + * `*.N_density.pdf`: Plot showing density of N bases within the consensus sequence. + * `*.N_run.tsv`: File containing start positions and width of N bases in consensus sequence. + +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). + +
+ + [BCFTools](http://samtools.github.io/bcftools/bcftools.html) is used in the variant calling and *de novo* assembly steps of this pipeline to obtain basic statistics from the VCF output. It can also used be used to generate a consensus sequence by integrating variant calls into the reference genome. In this pipeline, we use `samtools mpileup` to create a mask using low coverage positions, and `bedtools maskfasta` to mask the genome sequences based on these intervals. Finally, `bcftools consensus` is used to generate the consensus by projecting the high allele frequency variants onto the masked genome reference sequence. + ### QUAST
@@ -614,7 +633,8 @@ As described in the documentation, [ASCIIGenome](https://asciigenome.readthedocs * `variants//consensus//quast/` * `report.html`: Results report in HTML format. Also available in various other file formats i.e. `report.pdf`, `report.tex`, `report.tsv` and `report.txt`. -**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--consensus_caller` parameter (Default: 'bcftools' for both '--protocol amplicon' and '--protocol metagenomic').
@@ -628,12 +648,13 @@ As described in the documentation, [ASCIIGenome](https://asciigenome.readthedocs * `variants//consensus//pangolin/` * `*.pangolin.csv`: Lineage analysis results from Pangolin. +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--consensus_caller` parameter (Default: 'bcftools' for both '--protocol amplicon' and '--protocol metagenomic'). + Phylogenetic Assignment of Named Global Outbreak LINeages ([Pangolin](https://github.com/cov-lineages/pangolin)) has been used extensively during the COVID-19 pandemic in order to to assign lineages to SARS-CoV-2 genome sequenced samples. A [web application](https://pangolin.cog-uk.io/) also exists that allows users to upload genome sequences via a web browser to assign lineages to genome sequences of SARS-CoV-2, view descriptive characteristics of the assigned lineage(s), view the placement of the lineage in a phylogeny of global samples, and view the temporal and geographic distribution of the assigned lineage(s). -**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). - ### Nextclade
@@ -642,7 +663,8 @@ Phylogenetic Assignment of Named Global Outbreak LINeages ([Pangolin](https://gi * `variants//consensus//nextclade/` * `*.csv`: Analysis results from Nextlade containing genome clade assignment, mutation calling and sequence quality checks. -**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). +**NB:** The value of `` in the output directory name above is determined by the `--consensus_caller` parameter (Default: 'bcftools' for both '--protocol amplicon' and '--protocol metagenomic').
From c46e8ec2f21d8dcab7ef202ed824f4a89f6d3be8 Mon Sep 17 00:00:00 2001 From: svarona Date: Sat, 29 Jan 2022 11:59:11 +0100 Subject: [PATCH 187/238] Added rename header of fasta consensus config --- conf/modules_illumina.config | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 09b68e03..50597b94 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -616,6 +616,14 @@ if (!params.skip_variants) { withName: 'BCFTOOLS_CONSENSUS' { ext.prefix = { "${meta.id}.consensus" } + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, + enabled: false + ] + } + + withName: 'RENAME_CONSENSUS' { + ext.prefix = { "${meta.id}.AF0.75_consensus" } publishDir = [ path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, mode: 'copy', From a5319cedb76150c6852fb8179443c4f4e80f56ee Mon Sep 17 00:00:00 2001 From: svarona Date: Sat, 29 Jan 2022 11:59:34 +0100 Subject: [PATCH 188/238] Added consensus rename header after consensus --- subworkflows/local/consensus_bcftools.nf | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf index 084c6d12..61b0578e 100644 --- a/subworkflows/local/consensus_bcftools.nf +++ b/subworkflows/local/consensus_bcftools.nf @@ -8,6 +8,7 @@ include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merg include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' include { MAKE_BED_MASK } from '../../modules/local/make_bed_mask' +include { RENAME_CONSENSUS } from '../../modules/local/rename_consensus' include { CONSENSUS_QC } from './consensus_qc' workflow CONSENSUS_BCFTOOLS { @@ -72,11 +73,20 @@ workflow CONSENSUS_BCFTOOLS { ) ch_versions = ch_versions.mix(BCFTOOLS_CONSENSUS.out.versions.first()) + // + // Rename consensus header adding sample name + // + RENAME_CONSENSUS ( + BCFTOOLS_CONSENSUS.out.fasta + ) + //ch_versions = ch_versions.mix(RENAME_CONSENSUS.out.versions.first()) + + // // Consensus sequence QC // CONSENSUS_QC ( - BCFTOOLS_CONSENSUS.out.fasta, + RENAME_CONSENSUS.out.consensus, fasta, gff, nextclade_db @@ -84,7 +94,7 @@ workflow CONSENSUS_BCFTOOLS { ch_versions = ch_versions.mix(CONSENSUS_QC.out.versions.first()) emit: - consensus = BCFTOOLS_CONSENSUS.out.fasta // channel: [ val(meta), [ fasta ] ] + consensus = RENAME_CONSENSUS.out.consensus // channel: [ val(meta), [ fasta ] ] quast_results = CONSENSUS_QC.out.quast_results // channel: [ val(meta), [ results ] ] quast_tsv = CONSENSUS_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] From 84cc4e360b283f7f1b24c3027557a12c970a1261 Mon Sep 17 00:00:00 2001 From: svarona Date: Sat, 29 Jan 2022 11:59:48 +0100 Subject: [PATCH 189/238] Created rename consensus header module --- modules/local/rename_consensus.nf | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 modules/local/rename_consensus.nf diff --git a/modules/local/rename_consensus.nf b/modules/local/rename_consensus.nf new file mode 100644 index 00000000..bc7e6edf --- /dev/null +++ b/modules/local/rename_consensus.nf @@ -0,0 +1,29 @@ +process RENAME_CONSENSUS { + tag "$meta.id" + + conda (params.enable_conda ? "conda-forge::python=3.9.5" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/python:3.9--1' : + 'quay.io/biocontainers/python:3.9--1' }" + + input: + tuple val(meta), path(fasta) + + output: + tuple val(meta), path("*.fa"), emit: consensus + + script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ + def prefix = task.ext.prefix ?: "${meta.id}" + + + """ + sed "s/>/>${meta.id} /g" $fasta > ${prefix}.fa + + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: \$(python --version | sed 's/Python //g') + END_VERSIONS + """ + +} From 347c1acb804d87e24847cdb379a4cc036c77ec68 Mon Sep 17 00:00:00 2001 From: svarona Date: Sat, 29 Jan 2022 12:31:58 +0100 Subject: [PATCH 190/238] Fixed bcftools norm version --- subworkflows/local/variants_bcftools.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index 8ebd845e..97884479 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -37,7 +37,7 @@ workflow VARIANTS_BCFTOOLS { BCFTOOLS_MPILEUP.out.vcf, fasta ) - ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) + ch_versions = ch_versions.mix(BCFTOOLS_NORM.out.versions.first()) // From f628ed1aa8e8be0c2ad561ce83d4c8b167152aea Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 31 Jan 2022 11:04:54 +0000 Subject: [PATCH 191/238] Initial fixes for PR #264 --- bin/ivar_variants_to_vcf.py | 8 +-- conf/modules_illumina.config | 81 +++++++++++------------- modules/local/rename_consensus.nf | 29 --------- modules/local/rename_fasta_header.nf | 26 ++++++++ subworkflows/local/consensus_bcftools.nf | 30 ++++----- subworkflows/local/variants_bcftools.nf | 15 +++-- 6 files changed, 93 insertions(+), 96 deletions(-) delete mode 100644 modules/local/rename_consensus.nf create mode 100644 modules/local/rename_fasta_header.nf diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index b21b5e12..72ce0032 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -271,10 +271,10 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= # dict_lines has a maximum size of three. ## Always fill dict_lines until size 2. - if len(dict_lines["POS"]) ==0 or len(dict_lines["POS"]) ==1 : - for i,j in enumerate(dict_lines): - dict_lines.setdefault(j, []).append(param_list[i]) - writeLine=False + if len(dict_lines["POS"]) ==0 or len(dict_lines["POS"]) == 1: + for i,j in enumerate(dict_lines): + dict_lines.setdefault(j, []).append(param_list[i]) + writeLine=False # If queue has size 2, we include the third line elif len(dict_lines["POS"]) == 2: diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 50597b94..24327f68 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -421,33 +421,38 @@ if (!params.skip_variants) { ext.args = '--ignore-overlaps --count-orphans --no-BAQ --max-depth 0 --min-BQ 20 --annotate FORMAT/AD,FORMAT/ADF,FORMAT/ADR,FORMAT/DP,FORMAT/SP,INFO/AD,INFO/ADF,INFO/ADR' ext.args2 = '--ploidy 1 --keep-alts --keep-masked-ref --multiallelic-caller --variants-only' ext.args3 = "--include 'INFO/DP>=10'" + ext.prefix = { "${meta.id}.orig" } publishDir = [ - [ - path: { "${params.outdir}/variants/bcftools" }, - mode: 'copy', - pattern: '*.{gz,tbi}' - ], - [ - path: { "${params.outdir}/variants/bcftools" }, - mode: 'copy', - pattern: '*.mpileup', - enabled: params.save_mpileup - ], - [ - path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, - mode: 'copy', - pattern: '*stats.txt' - ] + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + pattern: '*.mpileup', + enabled: params.save_mpileup ] } withName: 'BCFTOOLS_NORM' { ext.args = '--do-not-normalize --output-type z --multiallelics -any' - ext.prefix = { "${meta.id}.norm" } publishDir = [ path: { "${params.outdir}/variants/bcftools" }, mode: 'copy', - pattern: "*.norm.vcf.gz" + pattern: "*.vcf.gz" + ] + } + + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:TABIX_TABIX' { + ext.args = '-p vcf -f' + publishDir = [ + path: { "${params.outdir}/variants/bcftools" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:BCFTOOLS_STATS' { + publishDir = [ + path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } @@ -553,28 +558,18 @@ if (!params.skip_variants) { if (!params.skip_consensus && params.consensus_caller == 'bcftools') { process { - if (params.variant_caller == 'bcftools') { - withName: 'BCFTOOLS_FILTER' { - ext.args = "--output-type z --include 'FORMAT/AD[:1] / FORMAT/DP >= 0.75'" - ext.prefix = { "${meta.id}.AF0.75" } - publishDir = [ - path: { "${params.outdir}/variants/${variant_caller}" }, - mode: 'copy', - pattern: "*.AF0.75.vcf.gz", - ] - } - } - - else if (params.variant_caller == 'ivar') { - withName: 'BCFTOOLS_FILTER' { - ext.args = "--output-type z --include 'FORMAT/ALT_FREQ >= 0.75'" - ext.prefix = { "${meta.id}.AF0.75" } - publishDir = [ - path: { "${params.outdir}/variants/${variant_caller}" }, - mode: 'copy', - pattern: "*.AF0.75.vcf.gz", - ] - } + withName: 'BCFTOOLS_FILTER' { + ext.args = [ + '--output-type z', + params.variant_caller == 'ivar' ? "--include 'FORMAT/ALT_FREQ >= 0.75'" : '', + params.variant_caller == 'bcftools' ? "--include 'FORMAT/AD[:1] / FORMAT/DP >= 0.75'" : '', + ].join(' ').trim() + ext.prefix = { "${meta.id}.AF0.75" } + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}" }, + mode: 'copy', + pattern: "*vcf.gz", + ] } withName: '.*:.*:CONSENSUS_BCFTOOLS:TABIX_TABIX' { @@ -615,15 +610,15 @@ if (!params.skip_variants) { } withName: 'BCFTOOLS_CONSENSUS' { - ext.prefix = { "${meta.id}.consensus" } + ext.prefix = { "${meta.id}" } publishDir = [ path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, enabled: false ] } - withName: 'RENAME_CONSENSUS' { - ext.prefix = { "${meta.id}.AF0.75_consensus" } + withName: 'RENAME_FASTA_HEADER' { + ext.prefix = { "${meta.id}.consensus" } publishDir = [ path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, mode: 'copy', diff --git a/modules/local/rename_consensus.nf b/modules/local/rename_consensus.nf deleted file mode 100644 index bc7e6edf..00000000 --- a/modules/local/rename_consensus.nf +++ /dev/null @@ -1,29 +0,0 @@ -process RENAME_CONSENSUS { - tag "$meta.id" - - conda (params.enable_conda ? "conda-forge::python=3.9.5" : null) - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/python:3.9--1' : - 'quay.io/biocontainers/python:3.9--1' }" - - input: - tuple val(meta), path(fasta) - - output: - tuple val(meta), path("*.fa"), emit: consensus - - script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ - def prefix = task.ext.prefix ?: "${meta.id}" - - - """ - sed "s/>/>${meta.id} /g" $fasta > ${prefix}.fa - - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - python: \$(python --version | sed 's/Python //g') - END_VERSIONS - """ - -} diff --git a/modules/local/rename_fasta_header.nf b/modules/local/rename_fasta_header.nf new file mode 100644 index 00000000..64c45a73 --- /dev/null +++ b/modules/local/rename_fasta_header.nf @@ -0,0 +1,26 @@ +process RENAME_FASTA_HEADER { + tag "$meta.id" + + conda (params.enable_conda ? "conda-forge::sed=4.7" : null) + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://containers.biocontainers.pro/s3/SingImgsRepo/biocontainers/v1.2.0_cv1/biocontainers_v1.2.0_cv1.img' : + 'biocontainers/biocontainers:v1.2.0_cv1' }" + + input: + tuple val(meta), path(fasta) + + output: + tuple val(meta), path("*.fa"), emit: fasta + path "versions.yml" , emit: versions + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + sed "s/>/>${meta.id} /g" $fasta > ${prefix}.fa + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + sed: \$(echo \$(sed --version 2>&1) | sed 's/^.*GNU sed) //; s/ .*\$//') + END_VERSIONS + """ +} diff --git a/subworkflows/local/consensus_bcftools.nf b/subworkflows/local/consensus_bcftools.nf index 61b0578e..3f82151e 100644 --- a/subworkflows/local/consensus_bcftools.nf +++ b/subworkflows/local/consensus_bcftools.nf @@ -2,14 +2,14 @@ // Consensus calling with BCFTools and downstream processing QC // -include { BCFTOOLS_FILTER } from '../../modules/nf-core/modules/bcftools/filter/main' -include { TABIX_TABIX } from '../../modules/nf-core/modules/tabix/tabix/main' -include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' -include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' -include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' -include { MAKE_BED_MASK } from '../../modules/local/make_bed_mask' -include { RENAME_CONSENSUS } from '../../modules/local/rename_consensus' -include { CONSENSUS_QC } from './consensus_qc' +include { BCFTOOLS_FILTER } from '../../modules/nf-core/modules/bcftools/filter/main' +include { TABIX_TABIX } from '../../modules/nf-core/modules/tabix/tabix/main' +include { BEDTOOLS_MERGE } from '../../modules/nf-core/modules/bedtools/merge/main' +include { BEDTOOLS_MASKFASTA } from '../../modules/nf-core/modules/bedtools/maskfasta/main' +include { BCFTOOLS_CONSENSUS } from '../../modules/nf-core/modules/bcftools/consensus/main' +include { MAKE_BED_MASK } from '../../modules/local/make_bed_mask' +include { RENAME_FASTA_HEADER } from '../../modules/local/rename_fasta_header' +include { CONSENSUS_QC } from './consensus_qc' workflow CONSENSUS_BCFTOOLS { take: @@ -24,16 +24,15 @@ workflow CONSENSUS_BCFTOOLS { ch_versions = Channel.empty() - // // Filter variants by allele frequency, zip and index // - BCFTOOLS_FILTER( + BCFTOOLS_FILTER ( vcf ) ch_versions = ch_versions.mix(BCFTOOLS_FILTER.out.versions.first()) - TABIX_TABIX( + TABIX_TABIX ( BCFTOOLS_FILTER.out.vcf ) ch_versions = ch_versions.mix(TABIX_TABIX.out.versions.first()) @@ -76,17 +75,16 @@ workflow CONSENSUS_BCFTOOLS { // // Rename consensus header adding sample name // - RENAME_CONSENSUS ( + RENAME_FASTA_HEADER ( BCFTOOLS_CONSENSUS.out.fasta ) - //ch_versions = ch_versions.mix(RENAME_CONSENSUS.out.versions.first()) - + ch_versions = ch_versions.mix(RENAME_FASTA_HEADER.out.versions.first()) // // Consensus sequence QC // CONSENSUS_QC ( - RENAME_CONSENSUS.out.consensus, + RENAME_FASTA_HEADER.out.fasta, fasta, gff, nextclade_db @@ -94,7 +92,7 @@ workflow CONSENSUS_BCFTOOLS { ch_versions = ch_versions.mix(CONSENSUS_QC.out.versions.first()) emit: - consensus = RENAME_CONSENSUS.out.consensus // channel: [ val(meta), [ fasta ] ] + consensus = RENAME_FASTA_HEADER.out.fasta // channel: [ val(meta), [ fasta ] ] quast_results = CONSENSUS_QC.out.quast_results // channel: [ val(meta), [ results ] ] quast_tsv = CONSENSUS_QC.out.quast_tsv // channel: [ val(meta), [ tsv ] ] diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index 97884479..c250e04f 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -4,6 +4,7 @@ include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm/main' +include { VCF_TABIX_STATS } from '../nf-core/vcf_tabix_stats' include { VARIANTS_QC } from './variants_qc' workflow VARIANTS_BCFTOOLS { @@ -31,7 +32,7 @@ workflow VARIANTS_BCFTOOLS { ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) // - // Split multiallelic positions + // Split multi-allelic positions // BCFTOOLS_NORM ( BCFTOOLS_MPILEUP.out.vcf, @@ -39,6 +40,10 @@ workflow VARIANTS_BCFTOOLS { ) ch_versions = ch_versions.mix(BCFTOOLS_NORM.out.versions.first()) + VCF_TABIX_STATS ( + BCFTOOLS_NORM.out.vcf + ) + ch_versions = ch_versions.mix(VCF_TABIX_STATS.out.versions) // // Run downstream tools for variants QC @@ -46,7 +51,7 @@ workflow VARIANTS_BCFTOOLS { VARIANTS_QC ( bam, BCFTOOLS_NORM.out.vcf, - BCFTOOLS_MPILEUP.out.stats, + VCF_TABIX_STATS.out.stats, fasta, sizes, gff, @@ -58,10 +63,12 @@ workflow VARIANTS_BCFTOOLS { emit: vcf_orig = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] - stats = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] + tbi_orig = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] + stats_orig = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = VCF_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] + stats = VCF_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] snpeff_tbi = VARIANTS_QC.out.snpeff_tbi // channel: [ val(meta), [ tbi ] ] From e9080c7c358c22f7b2d693f97ce504f65559fd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 13:08:38 +0100 Subject: [PATCH 192/238] bcftools query empty args as empty array --- subworkflows/local/long_table.nf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subworkflows/local/long_table.nf b/subworkflows/local/long_table.nf index f9f590c4..2ecdab6e 100644 --- a/subworkflows/local/long_table.nf +++ b/subworkflows/local/long_table.nf @@ -1,5 +1,5 @@ // -// Variant calling QC +// LONG TABLE VARIANTS // include { BCFTOOLS_QUERY } from '../../modules/nf-core/modules/bcftools/query/main' include { CREATE_LONG_TABLE } from '../../modules/local/create_long_table' @@ -18,9 +18,9 @@ workflow LONG_TABLE { if (!params.skip_long_table) { BCFTOOLS_QUERY ( vcf.join(tbi, by: [0]), - "", - "", - "" + [], + [], + [] ) ch_query_table = BCFTOOLS_QUERY.out.vcf ch_versions = ch_versions.mix(BCFTOOLS_QUERY.out.versions) From 712958b4403f2e359c45174d96ebfe181dcc1e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 13:13:37 +0100 Subject: [PATCH 193/238] Fixed typo in bcftools query config --- conf/modules_illumina.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 4007f127..5adc3a5e 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -401,7 +401,7 @@ if (!params.skip_variants) { } if (!params.skip_long_table) { withName: 'BCFTOOLS_QUERY' { - ext.args = "--H -f '%CHROM\t%POS\t%REF\t%ALT\t%FILTER\t[%DP\t]\t[%REF_DP\t]\t[%ALT_DP\t]\n'" + ext.args = "-H -f '%CHROM\t%POS\t%REF\t%ALT\t%FILTER\t[%DP\t]\t[%REF_DP\t]\t[%ALT_DP\t]\n'" } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software ivar" From 91174bb5cdeec467e228e722627751a5860e62fb Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 31 Jan 2022 13:19:50 +0000 Subject: [PATCH 194/238] Final tweaks --- conf/modules_illumina.config | 8 ++++---- docs/output.md | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 24327f68..0102ab16 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -564,18 +564,18 @@ if (!params.skip_variants) { params.variant_caller == 'ivar' ? "--include 'FORMAT/ALT_FREQ >= 0.75'" : '', params.variant_caller == 'bcftools' ? "--include 'FORMAT/AD[:1] / FORMAT/DP >= 0.75'" : '', ].join(' ').trim() - ext.prefix = { "${meta.id}.AF0.75" } + ext.prefix = { "${meta.id}.filtered" } publishDir = [ - path: { "${params.outdir}/variants/${variant_caller}" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, mode: 'copy', - pattern: "*vcf.gz", + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } withName: '.*:.*:CONSENSUS_BCFTOOLS:TABIX_TABIX' { ext.args = '-p vcf -f' publishDir = [ - path: { "${params.outdir}/variants/${variant_caller}" }, + path: { "${params.outdir}/variants/${variant_caller}/consensus/bcftools" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] diff --git a/docs/output.md b/docs/output.md index afa67ba4..4da7b7a4 100644 --- a/docs/output.md +++ b/docs/output.md @@ -512,7 +512,7 @@ Unless you are using [UMIs](https://emea.illumina.com/science/sequencing-method- * `variants/ivar/` * `*.tsv`: Original iVar variants in TSV format. * `*.vcf.gz`: iVar variants in VCF format. Converted using custom `ivar_variants_to_vcf.py` python script. - * `*.vcf.gz.tbi`: iVar variants in VCF index file. + * `*.vcf.gz.tbi`: iVar variants VCF index file. * `variants/ivar/log/` * `*.variant_counts.log`: Counts for type of variants called by iVar. * `variants/ivar/bcftools_stats/` @@ -612,6 +612,8 @@ As described in the [iVar variants](#ivar-variants) section, iVar can be used in * `variants//consensus/bcftools/` * `*.consensus.fa`: Consensus Fasta file generated by integrating the variants called by BCFTools into the reference genome. + * `*.filtered.vcf.gz`: VCF file containing high allele-frequency variants (default: `>= 0.75`) that were integrated into the consensus sequence. + * `*.filtered.vcf.gz.tbi`: Variants VCF index file for high allele frequency variants. * `variants//consensus/bcftools/base_qc/` * `*.ACTG_density.pdf`: Plot showing density of ACGT bases within the consensus sequence. * `*.base_counts.pdf`: Plot showing frequency and percentages of all bases in consensus sequence. From 0193023bf58f355d0ad3a5511fb7bd730eab822b Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 31 Jan 2022 13:24:40 +0000 Subject: [PATCH 195/238] Fix ECLint --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index 4da7b7a4..81c304f6 100644 --- a/docs/output.md +++ b/docs/output.md @@ -625,7 +625,7 @@ As described in the [iVar variants](#ivar-variants) section, iVar can be used in - [BCFTools](http://samtools.github.io/bcftools/bcftools.html) is used in the variant calling and *de novo* assembly steps of this pipeline to obtain basic statistics from the VCF output. It can also used be used to generate a consensus sequence by integrating variant calls into the reference genome. In this pipeline, we use `samtools mpileup` to create a mask using low coverage positions, and `bedtools maskfasta` to mask the genome sequences based on these intervals. Finally, `bcftools consensus` is used to generate the consensus by projecting the high allele frequency variants onto the masked genome reference sequence. +[BCFTools](http://samtools.github.io/bcftools/bcftools.html) is used in the variant calling and *de novo* assembly steps of this pipeline to obtain basic statistics from the VCF output. It can also used be used to generate a consensus sequence by integrating variant calls into the reference genome. In this pipeline, we use `samtools mpileup` to create a mask using low coverage positions, and `bedtools maskfasta` to mask the genome sequences based on these intervals. Finally, `bcftools consensus` is used to generate the consensus by projecting the high allele frequency variants onto the masked genome reference sequence. ### QUAST From e48bef735e857cb8d7a3756330cea30a263cc3e0 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Mon, 31 Jan 2022 15:52:55 +0000 Subject: [PATCH 196/238] Update docs --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index 81c304f6..5f4b6888 100644 --- a/docs/output.md +++ b/docs/output.md @@ -611,7 +611,7 @@ As described in the [iVar variants](#ivar-variants) section, iVar can be used in Output files * `variants//consensus/bcftools/` - * `*.consensus.fa`: Consensus Fasta file generated by integrating the variants called by BCFTools into the reference genome. + * `*.consensus.fa`: Consensus fasta file generated by integrating the high allele-frequency variants called by iVar/BCFTools into the reference genome. * `*.filtered.vcf.gz`: VCF file containing high allele-frequency variants (default: `>= 0.75`) that were integrated into the consensus sequence. * `*.filtered.vcf.gz.tbi`: Variants VCF index file for high allele frequency variants. * `variants//consensus/bcftools/base_qc/` From 11ed8c1266e8f19eddf786385d24243af98bec63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 17:14:50 +0100 Subject: [PATCH 197/238] fixed conflicts 2 --- conf/modules_illumina.config | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index a69bf8d7..1eec0e04 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -488,25 +488,6 @@ if (!params.skip_variants) { } } - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:TABIX_TABIX' { - ext.args = '-p vcf -f' - publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:BCFTOOLS_STATS' { - publishDir = [ - path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } - if (!params.skip_asciigenome) { process { withName: 'ASCIIGENOME' { From 2afe1ae6419a4850b42deb94c482eb2057e45bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 17:24:50 +0100 Subject: [PATCH 198/238] fix tabs in bcftoosl query config --- conf/modules_illumina.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 1eec0e04..e4e481af 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -414,7 +414,7 @@ if (!params.skip_variants) { } if (!params.skip_long_table) { withName: 'BCFTOOLS_QUERY' { - ext.args = "-H -f '%CHROM\t%POS\t%REF\t%ALT\t%FILTER\t[%DP\t]\t[%REF_DP\t]\t[%ALT_DP\t]\n'" + ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\n'" } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software ivar" @@ -472,7 +472,7 @@ if (!params.skip_variants) { } if (!params.skip_long_table) { withName: 'BCFTOOLS_QUERY' { - ext.args = "-H -f '%CHROM\t%POS\t%REF\t%ALT\t%FILTER\t[%DP\t]\t[%AD\t]\n'" + ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\n'" } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software bcftools" From 01cc9228b1cd92eaf33c4af115499ffe8bc9b002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 18:31:06 +0100 Subject: [PATCH 199/238] added prefix to bcftools query config, removed meta.id from create_long_table module --- conf/modules_illumina.config | 6 ++++-- modules/local/create_long_table.nf | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index e4e481af..620382b2 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -414,7 +414,8 @@ if (!params.skip_variants) { } if (!params.skip_long_table) { withName: 'BCFTOOLS_QUERY' { - ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\n'" + ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\\n'" + ext.prefix = { "${meta.id}_table" } } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software ivar" @@ -472,7 +473,8 @@ if (!params.skip_variants) { } if (!params.skip_long_table) { withName: 'BCFTOOLS_QUERY' { - ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\n'" + ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" + ext.prefix = { "${meta.id}_table" } } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software bcftools" diff --git a/modules/local/create_long_table.nf b/modules/local/create_long_table.nf index 31aec66b..2b8f2e46 100644 --- a/modules/local/create_long_table.nf +++ b/modules/local/create_long_table.nf @@ -1,5 +1,4 @@ process CREATE_LONG_TABLE { - tag "$meta.id" conda (params.enable_conda ? "conda-forge::python=3.9.5 conda-forge::matplotlib=3.5.1 conda-forge::pandas=1.3.5 conda-forge::r-sys=3.4 conda-forge::regex=2021.11.10 conda-forge::scipy=1.7.3" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? From 6624f50a0a9913c2ed1627ab9c394c27010dddab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 18:43:05 +0100 Subject: [PATCH 200/238] remove glob for create long table script --- bin/create_long_table.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/create_long_table.py b/bin/create_long_table.py index d1e1de81..6a8183c7 100755 --- a/bin/create_long_table.py +++ b/bin/create_long_table.py @@ -116,19 +116,19 @@ def main(args=None): # List vcf table files table_list = [] - for file in glob.glob(args.sample + "/*_norm.table"): + for file in glob.glob(args.sample + "/*"): table_list.append(file) table_list.sort() #List snpsift files snpsift_list = [] - for file in glob.glob(args.snpsift + "/*_norm.snpsift.txt"): + for file in glob.glob(args.snpsift + "/*"): snpsift_list.append(file) snpsift_list.sort() # List pangolin files pangolin_list = [] - for file in glob.glob(args.pangolin + "/*.csv"): + for file in glob.glob(args.pangolin + "/*"): pangolin_list.append(file) pangolin_list.sort() From 294d817d7821060dece75361b76fc6cacd49871a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 18:46:43 +0100 Subject: [PATCH 201/238] change head for cat and added comment in ordering vcf in ivar_variants_to_vcf module --- modules/local/ivar_variants_to_vcf.nf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/local/ivar_variants_to_vcf.nf b/modules/local/ivar_variants_to_vcf.nf index 3c911e19..a27b6dda 100644 --- a/modules/local/ivar_variants_to_vcf.nf +++ b/modules/local/ivar_variants_to_vcf.nf @@ -26,7 +26,8 @@ process IVAR_VARIANTS_TO_VCF { $args \\ > ${prefix}.variant_counts.log - head -1000 unsorted.txt | grep "^#" > ${prefix}.vcf; cat unsorted.txt | grep -v "^#" | sort -k1,1d -k2,2n >> ${prefix}.vcf + ## Order vcf by coordinates + cat unsorted.txt | grep "^#" > ${prefix}.vcf; cat unsorted.txt | grep -v "^#" | sort -k1,1d -k2,2n >> ${prefix}.vcf cat $header ${prefix}.variant_counts.log > ${prefix}.variant_counts_mqc.tsv From a280a037447ff3ea583fb16a1819610f135c6045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 19:02:08 +0100 Subject: [PATCH 202/238] temporary fix bcftools query module, change prefix for bcftools query in config --- conf/modules_illumina.config | 4 ++-- modules/nf-core/modules/bcftools/query/main.nf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 620382b2..daba9f6e 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -415,7 +415,7 @@ if (!params.skip_variants) { if (!params.skip_long_table) { withName: 'BCFTOOLS_QUERY' { ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\\n'" - ext.prefix = { "${meta.id}_table" } + ext.prefix = { "${meta.id}" } } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software ivar" @@ -474,7 +474,7 @@ if (!params.skip_variants) { if (!params.skip_long_table) { withName: 'BCFTOOLS_QUERY' { ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" - ext.prefix = { "${meta.id}_table" } + ext.prefix = { "${meta.id}" } } withName: 'CREATE_LONG_TABLE' { ext.args = "--output variants_long_table.csv --software bcftools" diff --git a/modules/nf-core/modules/bcftools/query/main.nf b/modules/nf-core/modules/bcftools/query/main.nf index 5f4135f4..ecb31160 100644 --- a/modules/nf-core/modules/bcftools/query/main.nf +++ b/modules/nf-core/modules/bcftools/query/main.nf @@ -26,7 +26,7 @@ process BCFTOOLS_QUERY { """ bcftools query \\ - --output ${prefix}.vcf.gz \\ + --output ${prefix}.table \\ ${regions_file} \\ ${targets_file} \\ ${samples_file} \\ From e6510766b1f53bfdcfc370730a06ede768425b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 19:09:46 +0100 Subject: [PATCH 203/238] temporary fix bcftools query nf-core module and fix in args order for submodule long table --- modules/nf-core/modules/bcftools/query/main.nf | 2 +- subworkflows/local/long_table.nf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/nf-core/modules/bcftools/query/main.nf b/modules/nf-core/modules/bcftools/query/main.nf index ecb31160..49a73cd1 100644 --- a/modules/nf-core/modules/bcftools/query/main.nf +++ b/modules/nf-core/modules/bcftools/query/main.nf @@ -14,7 +14,7 @@ process BCFTOOLS_QUERY { path(samples) output: - tuple val(meta), path("*.gz") , emit: vcf + tuple val(meta), path("*.table") , emit: vcf path "versions.yml" , emit: versions script: diff --git a/subworkflows/local/long_table.nf b/subworkflows/local/long_table.nf index 2ecdab6e..74589d01 100644 --- a/subworkflows/local/long_table.nf +++ b/subworkflows/local/long_table.nf @@ -8,8 +8,8 @@ workflow LONG_TABLE { take: vcf // channel: [ val(meta), [ vcf ] ] tbi // channel: [ val(meta), [ tbi ] ] - pangolin // channel: [ val(meta), [ ] ] - snpsift // channel: [ val(meta), [ ] ] + snpsift // channel: [ val(meta), [ txt ] ] + pangolin // channel: [ val(meta), [ csv ] ] main: From 9e74572ad3199c8c53dd6e290a315d528a549132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 19:14:55 +0100 Subject: [PATCH 204/238] fix lintin --- bin/create_long_table.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/create_long_table.py b/bin/create_long_table.py index 6a8183c7..ad95b0bc 100755 --- a/bin/create_long_table.py +++ b/bin/create_long_table.py @@ -67,13 +67,13 @@ def create_long(snp_file,snpsift_file,pangolin_file,software): colnames_snpsift = list(snpsift_table.columns) colnames_snpsift = [i.replace('ANN[*].', '') for i in colnames_snpsift] for i in range(len(colnames_snpsift)): - snpsift_table.rename(columns = {snpsift_table.columns[i]:colnames_snpsift[i]}, inplace = True) + snpsift_table.rename(columns = {snpsift_table.columns[i]:colnames_snpsift[i]}, inplace = True) snpsift_table = snpsift_table.loc[:, ['CHROM','POS','REF','ALT','GENE','EFFECT','HGVS_C','HGVS_P']] snpsift_table_copy = snpsift_table.copy() for i in range(len(snpsift_table_copy)): for j in range(3,8): - snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] + snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] oneletter_s = [] for index,item in snpsift_table_copy["HGVS_P"].iteritems(): From 6e3d8dfdad0f4ca18d328cad195be3c23b8e557d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 19:19:32 +0100 Subject: [PATCH 205/238] add skip_long_table to schema --- nextflow_schema.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 34154e20..88495e57 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -469,6 +469,11 @@ "type": "boolean", "fa_icon": "fas fa-fast-forward", "description": "Specify this parameter to skip all of the variant calling and mapping steps in the pipeline." + }, + "skip_long_table": { + "type": "boolean", + "fa_icon": "fas fa-fast-forward", + "description": "Specify this parameter to skip variants long table report generation." } }, "fa_icon": "fas fa-dna" @@ -730,4 +735,4 @@ "$ref": "#/definitions/institutional_config_options" } ] -} \ No newline at end of file +} From ddcbcd1e9de8a498abe3727141f721078cb70667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 19:21:33 +0100 Subject: [PATCH 206/238] removed all renamed script --- bin/parser_ivar_bcftools.py | 154 ------------------------------------ 1 file changed, 154 deletions(-) delete mode 100755 bin/parser_ivar_bcftools.py diff --git a/bin/parser_ivar_bcftools.py b/bin/parser_ivar_bcftools.py deleted file mode 100755 index b4c8539e..00000000 --- a/bin/parser_ivar_bcftools.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python - -from matplotlib import table -import pandas as pd -import sys -import numpy as np -import re -import argparse -import glob, os -import io - -pd.set_option('display.max_columns', None) -pd.set_option('display.max_rows', None) - - -def parser_args(args=None): - Description = 'Create long/wide tables fo ivar/bcftools' - Epilog = """Example usage: python parser_ivar_bcftools.py """ - parser = argparse.ArgumentParser(description=Description, epilog=Epilog) - parser.add_argument('-sample', help="Input sample table files path." ) - parser.add_argument('-snpsift', help="Input snpsift txt files path.",required=True) - parser.add_argument('-pangolin', help="Input pangolin csv files path.",required=True) - parser.add_argument('-software', help="Input bcftools of ivar.",required=True) - - return parser.parse_args(args) - -def aa_three_to_one_letter(hgvs_three): - three_syntax_dict= {'Ala': 'A', 'Arg': 'R', 'Asn': 'N', 'Asp': 'D', 'Cys': 'C', - 'Gln': 'Q', 'Glu': 'E', 'Gly': 'G', 'His': 'H', 'Ile': 'I', - 'Leu': 'L', 'Lys': 'K', 'Met': 'M', 'Phe': 'F', 'Pro': 'P', - 'Pyl': 'O', 'Ser': 'S', 'Sec': 'U', 'Thr': 'T', 'Trp': 'W', - 'Tyr': 'Y', 'Val': 'V', 'Asx': 'B', 'Glx': 'Z', 'Xaa': 'X', - 'Xle': 'J', 'Ter': '*'} - hgvs_one=hgvs_three - for key in three_syntax_dict: - if key in hgvs_one: - hgvs_one = hgvs_one.replace(str(key),str(three_syntax_dict[key])) - - return hgvs_one - -def create_long(snp_file,snpsift_file,pangolin_file,software): - - ### format of sample table - snp_table=pd.read_table(snp_file, header='infer') - snp_table = snp_table.dropna(how = 'all', axis =1) - - if software=='bcftools': - snp_table.rename(columns={snp_table.columns[5]: "DP",snp_table.columns[6]: "AD"}, inplace=True) - new_column = snp_table - new_column[['REF_DP','ALT_DP']] = snp_table['AD'].str.split(',', expand=True) - snp_table = pd.merge(snp_table,new_column,how = 'left') - snp_table[["ALT_DP", "DP"]] = snp_table[["ALT_DP", "DP"]].apply(pd.to_numeric) - snp_table['AF']=snp_table['ALT_DP']/snp_table['DP'] - snp_table['AF'] = snp_table['AF'].round(2) - snp_table = snp_table.loc[:, ~snp_table.columns.str.contains('AD')] - - elif software=='ivar': - snp_table.rename(columns={snp_table.columns[0]: "CHROM",snp_table.columns[1]: "POS",snp_table.columns[2]: "REF",snp_table.columns[3]: "ALT",snp_table.columns[4]: "FILTER",snp_table.columns[5]: "DP",snp_table.columns[6]: "REF_DP", snp_table.columns[7]: "ALT_DP"}, inplace=True) - snp_table[["ALT_DP", "DP"]] = snp_table[["ALT_DP", "DP"]].apply(pd.to_numeric) - snp_table['AF']=snp_table['ALT_DP']/snp_table['DP'] - snp_table['AF'] = snp_table['AF'].round(2) - - ### format of snpsift table - snpsift_table = pd.read_csv(snpsift_file, sep="\t", header = "infer") - snpsift_table = snpsift_table.loc[:, ~snpsift_table.columns.str.contains('^Unnamed')] - colnames_snpsift = list(snpsift_table.columns) - colnames_snpsift = [i.replace('ANN[*].', '') for i in colnames_snpsift] - for i in range(len(colnames_snpsift)): - snpsift_table.rename(columns = {snpsift_table.columns[i]:colnames_snpsift[i]}, inplace = True) - snpsift_table = snpsift_table.loc[:, ['CHROM','POS','REF','ALT','GENE','EFFECT','HGVS_C','HGVS_P']] - snpsift_table_copy = snpsift_table.copy() - - for i in range(len(snpsift_table_copy)): - for j in range(3,8): - snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] - - oneletter_s = [] - for index,item in snpsift_table_copy["HGVS_P"].iteritems(): - hgvs_p_oneletter = aa_three_to_one_letter(str(item)) - oneletter_s.append(hgvs_p_oneletter) - - snpsift_table_copy["HGVS_P_1Letter"] = pd.Series(oneletter_s) - - #format of lineages - pangolin_table = pd.read_csv(pangolin_file, sep=",", header = "infer") - lineages = pangolin_table.loc[:,['taxon','lineage']] - - #table long one sample - tl_onesample = pd.DataFrame(data =snp_table) - if software=='bcftools': - tl_onesample["Sample"] = lineages.iloc[0,0] - elif software=='ivar': - tl_onesample["Sample"] = lineages.iloc[0,0].split('_')[1].split('.')[0] - tl_onesample["software"] = software - tl_onesample["Lineage"] = lineages.iloc[0,1] - - merged_table_long = pd.merge(tl_onesample,snpsift_table_copy,how = 'outer') - - return(merged_table_long) - -def same_len(list): - return len(set(list)) == 1 - -def concatenatetable(path): - all_filenames = [file for file in glob.glob(path + '/*.csv')] - - dataframe_list = [] - for file in all_filenames: - dataframe_list.append(pd.read_csv(file,sep=",")) - merged_df = pd.concat(dataframe_list) - return merged_df - -def main(args=None): - args = parser_args(args) - - # List vcf table files - table_list = [] - for file in glob.glob(args.sample + "/*_norm.table"): - table_list.append(file) - table_list.sort() - - #List snpsift files - snpsift_list = [] - for file in glob.glob(args.snpsift + "/*_norm.snpsift.txt"): - snpsift_list.append(file) - snpsift_list.sort() - - # List pangolin files - pangolin_list = [] - for file in glob.glob(args.pangolin + "/*.csv"): - pangolin_list.append(file) - pangolin_list.sort() - - if not same_len([len(table_list),len(snpsift_list),len(pangolin_list)]): - print("not same number of files for variants, snpsift and pangolin results ") - exit() - - sample_names = [os.path.basename(filename).split("_norm.table")[0] for filename in table_list] - - #create SampleTables folder in the folder where the script is running - if os.path.exists("SampleTables"): - "SampleTables already exists from previous run, please delete or rename the folder." - else: - os.mkdir("SampleTables") - - for sample_name,table,snpsift,pangolin in zip(sample_names,table_list,snpsift_list,pangolin_list): - long_table_sample = create_long(table,snpsift,pangolin,args.software) - long_table_sample.to_csv('./SampleTables/'+ sample_name + '.csv', header='infer', index=None, sep=',', mode='a') - - merged_df= concatenatetable("./SampleTables") - merged_df.to_csv("final_long_table.csv", index=False, encoding='utf-8-sig') - -if __name__ == '__main__': - sys.exit(main()) From 5ef2c528fe884e9d6dc502c7064acb98a556b34f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Mon, 31 Jan 2022 19:32:34 +0100 Subject: [PATCH 207/238] fix bug when skip variants --- workflows/illumina.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/illumina.nf b/workflows/illumina.nf index e0fd8416..fdd5ba1a 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -530,7 +530,7 @@ workflow ILLUMINA { // SUBWORKFLOW: Create variants long table report // - if (!params.skip_consensus && !params.skip_long_table && variant_caller) { + if (!params.skip_variants && !params.skip_consensus && !params.skip_long_table && variant_caller) { LONG_TABLE ( ch_vcf, ch_tbi, From 90e4a78136b8659d05396ffe0a08d75f214a7cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 1 Feb 2022 12:12:20 +0100 Subject: [PATCH 208/238] fix sara stupid bugs in variants_bcftools --- subworkflows/local/variants_bcftools.nf | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index 964ade82..c72b3862 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -2,10 +2,10 @@ // Variant calling with BCFTools, downstream processing and QC // -include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' -include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm/main' -include { VCF_TABIX_STATS } from '../nf-core/vcf_tabix_stats' -include { VARIANTS_QC } from './variants_qc' +include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' +include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm/main' +include { VCF_TABIX_STATS } from '../nf-core/vcf_tabix_stats' +include { VARIANTS_QC } from './variants_qc' workflow VARIANTS_BCFTOOLS { take: @@ -40,10 +40,10 @@ workflow VARIANTS_BCFTOOLS { ) ch_versions = ch_versions.mix(BCFTOOLS_NORM.out.versions.first()) - VCF_BGZIP_TABIX_STATS ( + VCF_TABIX_STATS ( BCFTOOLS_NORM.out.vcf ) - ch_versions = ch_versions.mix(VCF_BGZIP_TABIX_STATS.out.versions) + ch_versions = ch_versions.mix(VCF_TABIX_STATS.out.versions) // @@ -51,8 +51,8 @@ workflow VARIANTS_BCFTOOLS { // VARIANTS_QC ( bam, - VCF_BGZIP_TABIX_STATS.out.vcf, - BCFTOOLS_MPILEUP.out.stats, + BCFTOOLS_NORM.out.vcf, + VCF_TABIX_STATS.out.stats, fasta, sizes, gff, @@ -66,9 +66,9 @@ workflow VARIANTS_BCFTOOLS { vcf_orig = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] stats_orig = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] - vcf = VCF_BGZIP_TABIX_STATS.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = VCF_BGZIP_TABIX_STATS.out.tbi // channel: [ val(meta), [ vcf ] ] - stats = VCF_BGZIP_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] + vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = VCF_TABIX_STATS.out.tbi // channel: [ val(meta), [ vcf ] ] + stats = VCF_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] snpeff_tbi = VARIANTS_QC.out.snpeff_tbi // channel: [ val(meta), [ tbi ] ] From 30aee9b676a22c16873bb52ba2a313c7cacaacec Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 1 Feb 2022 11:47:29 +0000 Subject: [PATCH 209/238] Fix MultiQC report for new --variant_caller options --- assets/multiqc_config_illumina.yaml | 95 +++++++---------------- bin/multiqc_to_custom_csv.py | 43 +++-------- conf/modules_illumina.config | 21 ++--- modules/local/multiqc_illumina.nf | 22 ++---- workflows/illumina.nf | 115 +++++++++++----------------- 5 files changed, 96 insertions(+), 200 deletions(-) diff --git a/assets/multiqc_config_illumina.yaml b/assets/multiqc_config_illumina.yaml index 48bf55e8..3cff53aa 100644 --- a/assets/multiqc_config_illumina.yaml +++ b/assets/multiqc_config_illumina.yaml @@ -24,7 +24,6 @@ run_modules: module_order: - fastqc: name: "PREPROCESS: FastQC (raw reads)" - anchor: "fastqc_raw" info: "This section of the report shows FastQC results for the raw reads before adapter trimming." path_filters: - "./fastqc/*.zip" @@ -59,53 +58,26 @@ module_order: name: "VARIANTS: mosdepth" info: "This section of the report shows genome-wide coverage metrics generated by mosdepth." - pangolin: - name: "VARIANTS: Pangolin (iVar)" - anchor: "pangolin_ivar" - info: "This section of the report shows Pangolin lineage analysis results for variants called by iVar." + name: "VARIANTS: Pangolin" + info: "This section of the report shows Pangolin lineage analysis results for the called variants." path_filters: - - "./variants_ivar/*.pangolin.csv" + - "./variants/*.pangolin.csv" - bcftools: - name: "VARIANTS: BCFTools (iVar)" - anchor: "bcftools_ivar" - info: "This section of the report shows BCFTools stats results for variants called by iVar." + name: "VARIANTS: BCFTools" + info: "This section of the report shows BCFTools stats results for the called variants." path_filters: - - "./variants_ivar/*.txt" + - "./variants/*.txt" - snpeff: - name: "VARIANTS: SnpEff (iVar)" - anchor: "snpeff_ivar" - info: "This section of the report shows SnpEff results for variants called by iVar." + name: "VARIANTS: SnpEff" + info: "This section of the report shows SnpEff results for the called variants." path_filters: - - "./variants_ivar/*.csv" + - "./variants/*.csv" - quast: - name: "VARIANTS: QUAST (iVar)" - anchor: "quast_ivar" - info: "This section of the report shows QUAST results for consensus sequences generated from variants with iVar." + name: "VARIANTS: QUAST" + anchor: "quast_variants" + info: "This section of the report shows QUAST QC results for the consensus sequence." path_filters: - - "./variants_ivar/*.tsv" - - pangolin: - name: "VARIANTS: Pangolin (BCFTools)" - anchor: "pangolin_bcftools" - info: "This section of the report shows Pangolin lineage analysis results for variants called by BCFTools." - path_filters: - - "./variants_bcftools/*.pangolin.csv" - - bcftools: - name: "VARIANTS: BCFTools (BCFTools)" - anchor: "bcftools_bcftools" - info: "This section of the report shows BCFTools stats results for variants called by BCFTools." - path_filters: - - "./variants_bcftools/*.txt" - - snpeff: - name: "VARIANTS: SnpEff (BCFTools)" - anchor: "snpeff_bcftools" - info: "This section of the report shows SnpEff results for variants called by BCFTools." - path_filters: - - "./variants_bcftools/*.csv" - - quast: - name: "VARIANTS: QUAST (BCFTools)" - anchor: "quast_bcftools" - info: "This section of the report shows QUAST results for consensus sequence generated from BCFTools variants." - path_filters: - - "./variants_bcftools/*.tsv" + - "./variants/*.tsv" - cutadapt: name: "ASSEMBLY: Cutadapt (primer trimming)" info: "This section of the report shows Cutadapt results for reads after primer sequence trimming." @@ -210,38 +182,22 @@ custom_data: "% Coverage > 10x": description: "Coverage > 10x calculated by mosdepth" format: "{:,.2f}" - "# SNPs (iVar)": - description: "Total number of SNPs called by iVar" - format: "{:,.0f}" - "# INDELs (iVar)": - description: "Total number of INDELs called by iVar" - format: "{:,.0f}" - "# Missense variants (iVar)": - description: "Total number of variants called by iVar and identified as missense mutations with SnpEff" - format: "{:,.0f}" - "# Ns per 100kb consensus (iVar)": - description: "Number of N bases per 100kb in consensus sequence generated by iVar" - format: "{:,.2f}" - "Pangolin lineage (iVar)": - description: "Pangolin lineage inferred from the consensus sequence generated by iVar" - "Nextclade clade (iVar)": - description: "Nextclade clade inferred from the consensus sequence generated by iVar" - "# SNPs (BCFTools)": - description: "Total number of SNPs called by BCFTools" + "# SNPs": + description: "Total number of SNPs" format: "{:,.0f}" - "# INDELs (BCFTools)": - description: "Total number of INDELs called by BCFTools" + "# INDELs": + description: "Total number of INDELs" format: "{:,.0f}" - "# Missense variants (BCFTools)": - description: "Total number of variants called by BCFTools and identified as missense mutations with SnpEff" + "# Missense variants": + description: "Total number of variants identified as missense mutations with SnpEff" format: "{:,.0f}" - "# Ns per 100kb consensus (BCFTools)": - description: "Number of N bases per 100kb in consensus sequence generated by BCFTools" + "# Ns per 100kb consensus": + description: "Number of N bases per 100kb in consensus sequence" format: "{:,.2f}" - "Pangolin lineage (BCFTools)": - description: "Pangolin lineage inferred from the consensus sequence generated by BCFTools" - "Nextclade clade (BCFTools)": - description: "Nextclade clade inferred from the consensus sequence generated by BCFTools" + "Pangolin lineage": + description: "Pangolin lineage inferred from the consensus sequence" + "Nextclade clade": + description: "Nextclade clade inferred from the consensus sequence" pconfig: id: "summary_variants_metrics_plot" table_title: "Variant calling metrics" @@ -326,6 +282,7 @@ custom_data: extra_fn_clean_exts: - ".markduplicates" - ".unclassified" + - "_MN908947.3" extra_fn_clean_trim: - "Consensus_" diff --git a/bin/multiqc_to_custom_csv.py b/bin/multiqc_to_custom_csv.py index 34e54348..dac8b7ae 100755 --- a/bin/multiqc_to_custom_csv.py +++ b/bin/multiqc_to_custom_csv.py @@ -221,48 +221,25 @@ def main(args=None): ], ), ( - "multiqc_bcftools_stats_bcftools_ivar.yaml", - [ - ("# SNPs (iVar)", ["number_of_SNPs"]), - ("# INDELs (iVar)", ["number_of_indels"]), - ], - ), - ( - "multiqc_snpeff_snpeff_ivar.yaml", - [("# Missense variants (iVar)", ["MISSENSE"])], - ), - ( - "multiqc_quast_quast_ivar.yaml", - [("# Ns per 100kb consensus (iVar)", ["# N's per 100 kbp"])], - ), - ( - "multiqc_pangolin_pangolin_ivar.yaml", - [("Pangolin lineage (iVar)", ["lineage"])], - ), - ("multiqc_ivar_nextclade_clade.yaml", [("Nextclade clade (iVar)", ["clade"])]), - ( - "multiqc_bcftools_stats_bcftools_bcftools.yaml", + "multiqc_bcftools_stats.yaml", [ - ("# SNPs (BCFTools)", ["number_of_SNPs"]), - ("# INDELs (BCFTools)", ["number_of_indels"]), + ("# SNPs", ["number_of_SNPs"]), + ("# INDELs", ["number_of_indels"]), ], ), ( - "multiqc_snpeff_snpeff_bcftools.yaml", - [("# Missense variants (BCFTools)", ["MISSENSE"])], + "multiqc_snpeff.yaml", + [("# Missense variants", ["MISSENSE"])], ), ( - "multiqc_quast_quast_bcftools.yaml", - [("# Ns per 100kb consensus (BCFTools)", ["# N's per 100 kbp"])], + "multiqc_quast_quast_variants.yaml", + [("# Ns per 100kb consensus", ["# N's per 100 kbp"])], ), ( - "multiqc_pangolin_pangolin_bcftools.yaml", - [("Pangolin lineage (BCFTools)", ["lineage"])], - ), - ( - "multiqc_bcftools_nextclade_clade.yaml", - [("Nextclade clade (BCFTools)", ["clade"])], + "multiqc_pangolin.yaml", + [("Pangolin lineage", ["lineage"])], ), + ("multiqc_nextclade_clade.yaml", [("Nextclade clade", ["clade"])]), ] illumina_assembly_files = [ diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 0102ab16..4ca92470 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -546,13 +546,6 @@ if (!params.skip_variants) { ] ] } - - withName: 'MULTIQC_TSV_IVAR_NEXTCLADE' { - publishDir = [ - path: { "${params.outdir}/multiqc" }, - enabled: false - ] - } } } @@ -625,13 +618,6 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - - withName: 'MULTIQC_TSV_BCFTOOLS_NEXTCLADE' { - publishDir = [ - path: { "${params.outdir}/multiqc" }, - enabled: false - ] - } } } @@ -657,6 +643,13 @@ if (!params.skip_variants) { pattern: "*.csv" ] } + + withName: 'MULTIQC_TSV_NEXTCLADE' { + publishDir = [ + path: { "${params.outdir}/multiqc" }, + enabled: false + ] + } } } diff --git a/modules/local/multiqc_illumina.nf b/modules/local/multiqc_illumina.nf index 2a4a9be2..eb3a12b9 100644 --- a/modules/local/multiqc_illumina.nf +++ b/modules/local/multiqc_illumina.nf @@ -22,17 +22,12 @@ process MULTIQC { path ('ivar_trim/*') path ('picard_markduplicates/*') path ('mosdepth/*') - path ('variants_ivar/*') - path ('variants_ivar/*') - path ('variants_ivar/*') - path ('variants_ivar/*') - path ('variants_ivar/*') - path ('variants_ivar/*') - path ('variants_bcftools/*') - path ('variants_bcftools/*') - path ('variants_bcftools/*') - path ('variants_bcftools/*') - path ('variants_bcftools/*') + path ('variants/*') + path ('variants/*') + path ('variants/*') + path ('variants/*') + path ('variants/*') + path ('variants/*') path ('cutadapt/*') path ('assembly_spades/*') path ('assembly_unicycler/*') @@ -65,11 +60,10 @@ process MULTIQC { rm -f *variants_metrics_mqc.csv fi - rm -f variants_ivar/report.tsv - rm -f variants_bcftools/report.tsv + rm -f variants/report.tsv ## Run MultiQC a second time - multiqc -f $args -e general_stats --ignore *nextclade_clade_mqc.tsv $custom_config . + multiqc -f $args -e general_stats --ignore nextclade_clade_mqc.tsv $custom_config . cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/workflows/illumina.nf b/workflows/illumina.nf index e7298005..96b8e4a9 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -61,8 +61,7 @@ include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_GENOME } from '../mod include { PLOT_MOSDEPTH_REGIONS as PLOT_MOSDEPTH_REGIONS_AMPLICON } from '../modules/local/plot_mosdepth_regions' include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_FAIL_READS } from '../modules/local/multiqc_tsv_from_list' include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_FAIL_MAPPED } from '../modules/local/multiqc_tsv_from_list' -include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_IVAR_NEXTCLADE } from '../modules/local/multiqc_tsv_from_list' -include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_BCFTOOLS_NEXTCLADE } from '../modules/local/multiqc_tsv_from_list' +include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NEXTCLADE } from '../modules/local/multiqc_tsv_from_list' // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules @@ -401,11 +400,11 @@ workflow ILLUMINA { // // SUBWORKFLOW: Call variants with IVar // - ch_vcf = Channel.empty() - ch_tbi = Channel.empty() - ch_ivar_counts_multiqc = Channel.empty() - ch_ivar_stats_multiqc = Channel.empty() - ch_ivar_snpeff_multiqc = Channel.empty() + ch_vcf = Channel.empty() + ch_tbi = Channel.empty() + ch_ivar_counts_multiqc = Channel.empty() + ch_bcftools_stats_multiqc = Channel.empty() + ch_snpeff_multiqc = Channel.empty() if (!params.skip_variants && variant_caller == 'ivar') { VARIANTS_IVAR ( ch_bam, @@ -417,19 +416,17 @@ workflow ILLUMINA { PREPARE_GENOME.out.snpeff_config, ch_ivar_variants_header_mqc ) - ch_vcf = VARIANTS_IVAR.out.vcf - ch_tbi = VARIANTS_IVAR.out.tbi - ch_ivar_counts_multiqc = VARIANTS_IVAR.out.multiqc_tsv - ch_ivar_stats_multiqc = VARIANTS_IVAR.out.stats - ch_ivar_snpeff_multiqc = VARIANTS_IVAR.out.snpeff_csv - ch_versions = ch_versions.mix(VARIANTS_IVAR.out.versions) + ch_vcf = VARIANTS_IVAR.out.vcf + ch_tbi = VARIANTS_IVAR.out.tbi + ch_ivar_counts_multiqc = VARIANTS_IVAR.out.multiqc_tsv + ch_bcftools_stats_multiqc = VARIANTS_IVAR.out.stats + ch_snpeff_multiqc = VARIANTS_IVAR.out.snpeff_csv + ch_versions = ch_versions.mix(VARIANTS_IVAR.out.versions) } // // SUBWORKFLOW: Call variants with BCFTools // - ch_bcftools_stats_multiqc = Channel.empty() - ch_bcftools_snpeff_multiqc = Channel.empty() if (!params.skip_variants && variant_caller == 'bcftools') { VARIANTS_BCFTOOLS ( ch_bam, @@ -440,19 +437,19 @@ workflow ILLUMINA { PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config ) - ch_vcf = VARIANTS_BCFTOOLS.out.vcf - ch_tbi = VARIANTS_BCFTOOLS.out.tbi - ch_bcftools_stats_multiqc = VARIANTS_BCFTOOLS.out.stats - ch_bcftools_snpeff_multiqc = VARIANTS_BCFTOOLS.out.snpeff_csv - ch_versions = ch_versions.mix(VARIANTS_BCFTOOLS.out.versions) + ch_vcf = VARIANTS_BCFTOOLS.out.vcf + ch_tbi = VARIANTS_BCFTOOLS.out.tbi + ch_bcftools_stats_multiqc = VARIANTS_BCFTOOLS.out.stats + ch_snpeff_multiqc = VARIANTS_BCFTOOLS.out.snpeff_csv + ch_versions = ch_versions.mix(VARIANTS_BCFTOOLS.out.versions) } // // SUBWORKFLOW: Call consensus with iVar and downstream QC // - ch_ivar_quast_multiqc = Channel.empty() - ch_ivar_pangolin_multiqc = Channel.empty() - ch_ivar_nextclade_multiqc = Channel.empty() + ch_quast_multiqc = Channel.empty() + ch_pangolin_multiqc = Channel.empty() + ch_nextclade_report = Channel.empty() if (!params.skip_consensus && params.consensus_caller == 'ivar') { CONSENSUS_IVAR ( ch_bam, @@ -460,35 +457,15 @@ workflow ILLUMINA { PREPARE_GENOME.out.gff, PREPARE_GENOME.out.nextclade_db ) - ch_ivar_quast_multiqc = CONSENSUS_IVAR.out.quast_tsv - ch_ivar_pangolin_multiqc = CONSENSUS_IVAR.out.pangolin_report - ch_ivar_nextclade_report = CONSENSUS_IVAR.out.nextclade_report - ch_versions = ch_versions.mix(CONSENSUS_IVAR.out.versions) - - // - // MODULE: Get Nextclade clade information for MultiQC report - // - ch_ivar_nextclade_report - .map { meta, csv -> - def clade = WorkflowCommons.getNextcladeFieldMapFromCsv(csv)['clade'] - return [ "$meta.id\t$clade" ] - } - .set { ch_ivar_nextclade_multiqc } - - MULTIQC_TSV_IVAR_NEXTCLADE ( - ch_ivar_nextclade_multiqc.collect(), - ['Sample', 'clade'], - 'ivar_nextclade_clade' - ) - .set { ch_ivar_nextclade_multiqc } + ch_quast_multiqc = CONSENSUS_IVAR.out.quast_tsv + ch_pangolin_multiqc = CONSENSUS_IVAR.out.pangolin_report + ch_nextclade_report = CONSENSUS_IVAR.out.nextclade_report + ch_versions = ch_versions.mix(CONSENSUS_IVAR.out.versions) } // // SUBWORKFLOW: Call consensus with BCFTools // - ch_bcftools_quast_multiqc = Channel.empty() - ch_bcftools_pangolin_multiqc = Channel.empty() - ch_bcftools_nextclade_multiqc = Channel.empty() if (!params.skip_consensus && params.consensus_caller == 'bcftools' && variant_caller) { CONSENSUS_BCFTOOLS ( ch_bam, @@ -498,27 +475,30 @@ workflow ILLUMINA { PREPARE_GENOME.out.gff, PREPARE_GENOME.out.nextclade_db ) - ch_bcftools_quast_multiqc = CONSENSUS_BCFTOOLS.out.quast_tsv - ch_bcftools_pangolin_multiqc = CONSENSUS_BCFTOOLS.out.pangolin_report - ch_bcftools_nextclade_report = CONSENSUS_BCFTOOLS.out.nextclade_report - ch_versions = ch_versions.mix(CONSENSUS_BCFTOOLS.out.versions) - - // - // MODULE: Get Nextclade clade information for MultiQC report - // - ch_bcftools_nextclade_report + ch_quast_multiqc = CONSENSUS_BCFTOOLS.out.quast_tsv + ch_pangolin_multiqc = CONSENSUS_BCFTOOLS.out.pangolin_report + ch_nextclade_report = CONSENSUS_BCFTOOLS.out.nextclade_report + ch_versions = ch_versions.mix(CONSENSUS_BCFTOOLS.out.versions) + } + + // + // MODULE: Get Nextclade clade information for MultiQC report + // + ch_nextclade_multiqc = Channel.empty() + if (!params.skip_nextclade) { + ch_nextclade_report .map { meta, csv -> def clade = WorkflowCommons.getNextcladeFieldMapFromCsv(csv)['clade'] return [ "$meta.id\t$clade" ] } - .set { ch_bcftools_nextclade_multiqc } + .set { ch_nextclade_multiqc } - MULTIQC_TSV_BCFTOOLS_NEXTCLADE ( - ch_bcftools_nextclade_multiqc.collect(), + MULTIQC_TSV_NEXTCLADE ( + ch_nextclade_multiqc.collect(), ['Sample', 'clade'], - 'bcftools_nextclade_clade' + 'nextclade_clade' ) - .set { ch_bcftools_nextclade_multiqc } + .set { ch_nextclade_multiqc } } // @@ -623,16 +603,11 @@ workflow ILLUMINA { ch_markduplicates_flagstat_multiqc.collect{it[1]}.ifEmpty([]), ch_mosdepth_multiqc.collect{it[1]}.ifEmpty([]), ch_ivar_counts_multiqc.collect{it[1]}.ifEmpty([]), - ch_ivar_stats_multiqc.collect{it[1]}.ifEmpty([]), - ch_ivar_snpeff_multiqc.collect{it[1]}.ifEmpty([]), - ch_ivar_quast_multiqc.collect().ifEmpty([]), - ch_ivar_pangolin_multiqc.collect{it[1]}.ifEmpty([]), - ch_ivar_nextclade_multiqc.collect().ifEmpty([]), ch_bcftools_stats_multiqc.collect{it[1]}.ifEmpty([]), - ch_bcftools_snpeff_multiqc.collect{it[1]}.ifEmpty([]), - ch_bcftools_quast_multiqc.collect().ifEmpty([]), - ch_bcftools_pangolin_multiqc.collect{it[1]}.ifEmpty([]), - ch_bcftools_nextclade_multiqc.collect().ifEmpty([]), + ch_snpeff_multiqc.collect{it[1]}.ifEmpty([]), + ch_quast_multiqc.collect().ifEmpty([]), + ch_pangolin_multiqc.collect{it[1]}.ifEmpty([]), + ch_nextclade_multiqc.collect().ifEmpty([]), ch_cutadapt_multiqc.collect{it[1]}.ifEmpty([]), ch_spades_quast_multiqc.collect().ifEmpty([]), ch_unicycler_quast_multiqc.collect().ifEmpty([]), From 4eada1598d59a57e963e536f22cbfb76948c631d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 1 Feb 2022 15:09:22 +0100 Subject: [PATCH 210/238] fixed col names in create long table for bcftools --- bin/create_long_table.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/create_long_table.py b/bin/create_long_table.py index ad95b0bc..99cb9398 100755 --- a/bin/create_long_table.py +++ b/bin/create_long_table.py @@ -46,7 +46,7 @@ def create_long(snp_file,snpsift_file,pangolin_file,software): snp_table = snp_table.dropna(how = 'all', axis =1) if software=='bcftools': - snp_table.rename(columns={snp_table.columns[5]: "DP",snp_table.columns[6]: "AD"}, inplace=True) + snp_table.rename(columns={snp_table.columns[0]: "CHROM",snp_table.columns[1]: "POS",snp_table.columns[2]: "REF",snp_table.columns[3]: "ALT",snp_table.columns[4]: "FILTER", snp_table.columns[5]: "DP",snp_table.columns[6]: "AD"}, inplace=True) new_column = snp_table new_column[['REF_DP','ALT_DP']] = snp_table['AD'].str.split(',', expand=True) snp_table = pd.merge(snp_table,new_column,how = 'left') @@ -89,12 +89,11 @@ def create_long(snp_file,snpsift_file,pangolin_file,software): #table long one sample tl_onesample = pd.DataFrame(data =snp_table) if software=='bcftools': - tl_onesample["Sample"] = lineages.iloc[0,0] + tl_onesample["Sample"] = lineages.iloc[0,0].split('_')[1].split('.')[0] elif software=='ivar': tl_onesample["Sample"] = lineages.iloc[0,0].split('_')[1].split('.')[0] tl_onesample["software"] = software tl_onesample["Lineage"] = lineages.iloc[0,1] - merged_table_long = pd.merge(tl_onesample,snpsift_table_copy,how = 'outer') return(merged_table_long) From 7826396564d0e979fac1df782cdb020560e0eff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 1 Feb 2022 15:15:45 +0100 Subject: [PATCH 211/238] fixed help in create long table script --- bin/create_long_table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/create_long_table.py b/bin/create_long_table.py index 99cb9398..3c5ebb71 100755 --- a/bin/create_long_table.py +++ b/bin/create_long_table.py @@ -15,7 +15,7 @@ def parser_args(args=None): Description = 'Create long/wide tables fo ivar/bcftools' - Epilog = """Example usage: python parser_ivar_bcftools.py """ + Epilog = """Example usage: python create_long_table.py --samples_path --snpsift_path --pangolin_path --software """ parser = argparse.ArgumentParser(description=Description, epilog=Epilog) parser.add_argument('--samples_path','-s',dest="sample", help="Input sample table files path.", required=True ) parser.add_argument('--snpsift_path','-a',dest="snpsift",help="Input snpsift txt files path.",required=True) From 03a31b6f9b9c80e9683654f94a68d9f4a5ffc495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Monz=C3=B3n?= Date: Tue, 1 Feb 2022 16:21:48 +0100 Subject: [PATCH 212/238] fix trailing spaces --- workflows/illumina.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/illumina.nf b/workflows/illumina.nf index 82fc2334..2b843134 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -440,7 +440,7 @@ workflow ILLUMINA { PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config ) - + ch_vcf = VARIANTS_BCFTOOLS.out.vcf ch_tbi = VARIANTS_BCFTOOLS.out.tbi ch_bcftools_stats_multiqc = VARIANTS_BCFTOOLS.out.stats From 4269a2f49d0ebc1564ff18c730165538587d74a9 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Tue, 1 Feb 2022 18:04:33 +0000 Subject: [PATCH 213/238] First pass updates for PR #266 --- CHANGELOG.md | 4 +- ...g_table.py => make_variants_long_table.py} | 4 +- conf/modules_illumina.config | 69 ++++++++----------- ...g_table.nf => make_variants_long_table.nf} | 16 +++-- nextflow.config | 2 +- nextflow_schema.json | 10 +-- subworkflows/local/long_table.nf | 41 ----------- subworkflows/local/variants_bcftools.nf | 14 ++-- subworkflows/local/variants_long_table.nf | 41 +++++++++++ subworkflows/local/variants_qc.nf | 2 + workflows/illumina.nf | 42 +++++------ 11 files changed, 118 insertions(+), 127 deletions(-) rename bin/{create_long_table.py => make_variants_long_table.py} (98%) rename modules/local/{create_long_table.nf => make_variants_long_table.nf} (75%) delete mode 100644 subworkflows/local/long_table.nf create mode 100644 subworkflows/local/variants_long_table.nf diff --git a/CHANGELOG.md b/CHANGELOG.md index 88ea5359..ff333d65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [[2.3](https://github.com/nf-core/viralrecon/releases/tag/2.3)] - 2022-01-28 +## [[2.3](https://github.com/nf-core/viralrecon/releases/tag/2.3)] - 2022-02-04 ### :warning: Major enhancements @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [[#245](https://github.com/nf-core/viralrecon/issues/245)] - Mpileup file as output * [[#246](https://github.com/nf-core/viralrecon/issues/246)] - Option to generate consensus with BCFTools / BEDTools using iVar variants * [[#247](https://github.com/nf-core/viralrecon/issues/247)] - Add strand-bias filtering option and codon fix in consecutive positions in ivar tsv conversion to vcf +* [[#248](https://github.com/nf-core/viralrecon/issues/248)] - New variants reporting table ### Parameters @@ -32,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | | `--nextclade_dataset_reference` | | | `--nextclade_dataset_tag` | | | `--skip_consensus_plots` | +| | `--skip_variants_long_table` | | | `--consensus_caller` | | `--callers` | `--variant_caller` | diff --git a/bin/create_long_table.py b/bin/make_variants_long_table.py similarity index 98% rename from bin/create_long_table.py rename to bin/make_variants_long_table.py index 3c5ebb71..fac73246 100755 --- a/bin/create_long_table.py +++ b/bin/make_variants_long_table.py @@ -14,13 +14,13 @@ def parser_args(args=None): - Description = 'Create long/wide tables fo ivar/bcftools' + Description = 'Create long/wide tables for ivar/bcftools' Epilog = """Example usage: python create_long_table.py --samples_path --snpsift_path --pangolin_path --software """ parser = argparse.ArgumentParser(description=Description, epilog=Epilog) parser.add_argument('--samples_path','-s',dest="sample", help="Input sample table files path.", required=True ) parser.add_argument('--snpsift_path','-a',dest="snpsift",help="Input snpsift txt files path.",required=True) parser.add_argument('--pangolin_path','-l',dest="pangolin",help="Input pangolin csv files path.",required=True) - parser.add_argument('--software','-p',dest="software",help="Input bcftools of ivar.",required=True) + parser.add_argument('--software','-p',dest="software",help="Input bcftools or ivar.",required=True) parser.add_argument('--output','-o',dest="output",help="Output filename",required=True) return parser.parse_args(args) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 46a289f3..7f7360b2 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -412,22 +412,6 @@ if (!params.skip_variants) { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - if (!params.skip_long_table) { - withName: 'BCFTOOLS_QUERY' { - ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\\n'" - ext.prefix = { "${meta.id}" } - } - withName: 'CREATE_LONG_TABLE' { - ext.args = "--output variants_long_table.csv --software ivar" - publishDir = [ - [ - path: { "${params.outdir}/variants" }, - mode: 'copy', - pattern: '*.csv' - ] - ] - } - } } } @@ -455,7 +439,8 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:TABIX_BGZIP' { + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:TABIX_TABIX' { + ext.args = '-p vcf -f' publishDir = [ path: { "${params.outdir}/variants/bcftools" }, mode: 'copy', @@ -463,30 +448,13 @@ if (!params.skip_variants) { ] } - withName: '.*:.*:VARIANTS_BCFTOOLS:.*:.*:TABIX_TABIX' { - ext.args = '-p vcf -f' + withName: '.*:.*:VARIANTS_BCFTOOLS:.*:BCFTOOLS_STATS' { publishDir = [ - path: { "${params.outdir}/variants/bcftools" }, + path: { "${params.outdir}/variants/bcftools/bcftools_stats" }, mode: 'copy', saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - if (!params.skip_long_table) { - withName: 'BCFTOOLS_QUERY' { - ext.args = "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" - ext.prefix = { "${meta.id}" } - } - withName: 'CREATE_LONG_TABLE' { - ext.args = "--output variants_long_table.csv --software bcftools" - publishDir = [ - [ - path: { "${params.outdir}/variants" }, - mode: 'copy', - pattern: '*.csv' - ] - ] - } - } } } @@ -558,6 +526,30 @@ if (!params.skip_variants) { } } + if (!params.skip_variants_long_table) { + process { + withName: 'BCFTOOLS_QUERY' { + ext.args = [ + variant_caller == 'ivar' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\\n'" : '', + variant_caller == 'bcftools' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '', + ].join(' ').trim() + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}" }, + enabled: false + ] + } + + withName: 'MAKE_VARIANTS_LONG_TABLE' { + ext.args = "--output variants_long_table.csv --software ${variant_caller}" + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}" }, + mode: 'copy', + pattern: "*.csv" + ] + } + } + } + if (!params.skip_consensus && params.consensus_caller == 'ivar') { process { withName: 'IVAR_CONSENSUS' { @@ -586,8 +578,8 @@ if (!params.skip_variants) { withName: 'BCFTOOLS_FILTER' { ext.args = [ '--output-type z', - params.variant_caller == 'ivar' ? "--include 'FORMAT/ALT_FREQ >= 0.75'" : '', - params.variant_caller == 'bcftools' ? "--include 'FORMAT/AD[:1] / FORMAT/DP >= 0.75'" : '', + variant_caller == 'ivar' ? "--include 'FORMAT/ALT_FREQ >= 0.75'" : '', + variant_caller == 'bcftools' ? "--include 'FORMAT/AD[:1] / FORMAT/DP >= 0.75'" : '', ].join(' ').trim() ext.prefix = { "${meta.id}.filtered" } publishDir = [ @@ -1052,7 +1044,6 @@ if (!params.skip_assembly) { } } - if (!params.skip_multiqc) { process { withName: 'MULTIQC' { diff --git a/modules/local/create_long_table.nf b/modules/local/make_variants_long_table.nf similarity index 75% rename from modules/local/create_long_table.nf rename to modules/local/make_variants_long_table.nf index 2b8f2e46..f891c4e1 100644 --- a/modules/local/create_long_table.nf +++ b/modules/local/make_variants_long_table.nf @@ -1,4 +1,4 @@ -process CREATE_LONG_TABLE { +process MAKE_VARIANTS_LONG_TABLE { conda (params.enable_conda ? "conda-forge::python=3.9.5 conda-forge::matplotlib=3.5.1 conda-forge::pandas=1.3.5 conda-forge::r-sys=3.4 conda-forge::regex=2021.11.10 conda-forge::scipy=1.7.3" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? @@ -6,20 +6,22 @@ process CREATE_LONG_TABLE { 'quay.io/biocontainers/mulled-v2-77320db00eefbbf8c599692102c3d387a37ef02a:08144a66f00dc7684fad061f1466033c0176e7ad-0' }" input: - path ('variants_table/*') + path ('bcftools_query/*') path ('snpsift/*') path ('pangolin/*') output: - path "*.csv", optional:true, emit: csv_variants - - path "versions.yml" , emit: versions + path "*.csv" , emit: csv + path "versions.yml", emit: versions script: // This script is bundled with the pipeline, in nf-core/viralrecon/bin/ def args = task.ext.args ?: '' - """ - create_long_table.py --samples_path ./variants_table --snpsift_path ./snpsift --pangolin_path ./pangolin $args + make_variants_long_table.py \\ + --samples_path ./bcftools_query \\ + --snpsift_path ./snpsift \\ + --pangolin_path ./pangolin \\ + $args cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/nextflow.config b/nextflow.config index ef9d6a53..1674fdef 100644 --- a/nextflow.config +++ b/nextflow.config @@ -73,9 +73,9 @@ params { skip_markduplicates = true skip_picard_metrics = false skip_consensus_plots = false + skip_variants_long_table = false skip_consensus = false skip_variants = false - skip_long_table = false // Illumina de novo assembly options assemblers = 'spades' diff --git a/nextflow_schema.json b/nextflow_schema.json index 88495e57..c472de48 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -460,6 +460,11 @@ "fa_icon": "fas fa-fast-forward", "description": "Skip creation of consensus base density plots." }, + "skip_variants_long_table": { + "type": "boolean", + "fa_icon": "fas fa-fast-forward", + "description": "Skip long table generation for reporting variants." + }, "skip_consensus": { "type": "boolean", "fa_icon": "fas fa-fast-forward", @@ -469,11 +474,6 @@ "type": "boolean", "fa_icon": "fas fa-fast-forward", "description": "Specify this parameter to skip all of the variant calling and mapping steps in the pipeline." - }, - "skip_long_table": { - "type": "boolean", - "fa_icon": "fas fa-fast-forward", - "description": "Specify this parameter to skip variants long table report generation." } }, "fa_icon": "fas fa-dna" diff --git a/subworkflows/local/long_table.nf b/subworkflows/local/long_table.nf deleted file mode 100644 index 74589d01..00000000 --- a/subworkflows/local/long_table.nf +++ /dev/null @@ -1,41 +0,0 @@ -// -// LONG TABLE VARIANTS -// -include { BCFTOOLS_QUERY } from '../../modules/nf-core/modules/bcftools/query/main' -include { CREATE_LONG_TABLE } from '../../modules/local/create_long_table' - -workflow LONG_TABLE { - take: - vcf // channel: [ val(meta), [ vcf ] ] - tbi // channel: [ val(meta), [ tbi ] ] - snpsift // channel: [ val(meta), [ txt ] ] - pangolin // channel: [ val(meta), [ csv ] ] - - main: - - ch_versions = Channel.empty() - - if (!params.skip_long_table) { - BCFTOOLS_QUERY ( - vcf.join(tbi, by: [0]), - [], - [], - [] - ) - ch_query_table = BCFTOOLS_QUERY.out.vcf - ch_versions = ch_versions.mix(BCFTOOLS_QUERY.out.versions) - - CREATE_LONG_TABLE ( - ch_query_table.collect{it[1]}, - snpsift.collect{it[1]}, - pangolin.collect{it[1]} - ) - ch_long_table = CREATE_LONG_TABLE.out.csv_variants - } - - emit: - longtable_csv = ch_long_table // channel: [ val(meta), [ vcf.gz ] ] - query_table = ch_query_table // channel: [ val(meta), [ txt ] ] - - versions = ch_versions // channel: [ versions.yml ] -} diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index c72b3862..c250e04f 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -2,10 +2,10 @@ // Variant calling with BCFTools, downstream processing and QC // -include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' -include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm/main' -include { VCF_TABIX_STATS } from '../nf-core/vcf_tabix_stats' -include { VARIANTS_QC } from './variants_qc' +include { BCFTOOLS_MPILEUP } from '../../modules/nf-core/modules/bcftools/mpileup/main' +include { BCFTOOLS_NORM } from '../../modules/nf-core/modules/bcftools/norm/main' +include { VCF_TABIX_STATS } from '../nf-core/vcf_tabix_stats' +include { VARIANTS_QC } from './variants_qc' workflow VARIANTS_BCFTOOLS { take: @@ -45,7 +45,6 @@ workflow VARIANTS_BCFTOOLS { ) ch_versions = ch_versions.mix(VCF_TABIX_STATS.out.versions) - // // Run downstream tools for variants QC // @@ -64,10 +63,11 @@ workflow VARIANTS_BCFTOOLS { emit: vcf_orig = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi_orig = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] stats_orig = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] - vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi = VCF_TABIX_STATS.out.tbi // channel: [ val(meta), [ vcf ] ] + vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] + tbi = VCF_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] stats = VCF_TABIX_STATS.out.stats // channel: [ val(meta), [ txt ] ] snpeff_vcf = VARIANTS_QC.out.snpeff_vcf // channel: [ val(meta), [ vcf.gz ] ] diff --git a/subworkflows/local/variants_long_table.nf b/subworkflows/local/variants_long_table.nf new file mode 100644 index 00000000..4d31b44c --- /dev/null +++ b/subworkflows/local/variants_long_table.nf @@ -0,0 +1,41 @@ +// +// Create a long table with variant information including AA changes and lineage info +// + +include { BCFTOOLS_QUERY } from '../../modules/nf-core/modules/bcftools/query/main' +include { MAKE_VARIANTS_LONG_TABLE } from '../../modules/local/make_variants_long_table' + +workflow VARIANTS_LONG_TABLE { + take: + vcf // channel: [ val(meta), [ vcf ] ] + tbi // channel: [ val(meta), [ tbi ] ] + snpsift // channel: [ val(meta), [ txt ] ] + pangolin // channel: [ val(meta), [ csv ] ] + + main: + + ch_versions = Channel.empty() + + BCFTOOLS_QUERY ( + vcf.join(tbi, by: [0]), + [], + [], + [] + ) + ch_query_table = BCFTOOLS_QUERY.out.vcf + ch_versions = ch_versions.mix(BCFTOOLS_QUERY.out.versions.first()) + + MAKE_VARIANTS_LONG_TABLE ( + ch_query_table.collect{it[1]}, + snpsift.collect{it[1]}.ifEmpty([]), + pangolin.collect{it[1]}.ifEmpty([]) + ) + ch_long_table = MAKE_VARIANTS_LONG_TABLE.out.csv + ch_versions = ch_versions.mix(MAKE_VARIANTS_LONG_TABLE.out.versions) + + emit: + query_table = ch_query_table // channel: [ val(meta), [ txt ] ] + long_table = ch_long_table // channel: [ val(meta), [ csv ] ] + + versions = ch_versions // channel: [ versions.yml ] +} diff --git a/subworkflows/local/variants_qc.nf b/subworkflows/local/variants_qc.nf index 111e983f..cded165c 100644 --- a/subworkflows/local/variants_qc.nf +++ b/subworkflows/local/variants_qc.nf @@ -1,6 +1,7 @@ // // Variant calling QC // + include { ASCIIGENOME } from '../../modules/local/asciigenome' include { SNPEFF_SNPSIFT } from './snpeff_snpsift' @@ -47,6 +48,7 @@ workflow VARIANTS_QC { ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) } + // // Variant screenshots with ASCIIGenome // ch_asciigenome_pdf = Channel.empty() diff --git a/workflows/illumina.nf b/workflows/illumina.nf index 2b843134..849b1687 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -66,16 +66,16 @@ include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NEXTCLADE } from '../mod // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' -include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_illumina' -include { VARIANTS_IVAR } from '../subworkflows/local/variants_ivar' -include { VARIANTS_BCFTOOLS } from '../subworkflows/local/variants_bcftools' -include { CONSENSUS_IVAR } from '../subworkflows/local/consensus_ivar' -include { CONSENSUS_BCFTOOLS } from '../subworkflows/local/consensus_bcftools' -include { LONG_TABLE } from '../subworkflows/local/long_table' -include { ASSEMBLY_SPADES } from '../subworkflows/local/assembly_spades' -include { ASSEMBLY_UNICYCLER } from '../subworkflows/local/assembly_unicycler' -include { ASSEMBLY_MINIA } from '../subworkflows/local/assembly_minia' +include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_illumina' +include { VARIANTS_IVAR } from '../subworkflows/local/variants_ivar' +include { VARIANTS_BCFTOOLS } from '../subworkflows/local/variants_bcftools' +include { CONSENSUS_IVAR } from '../subworkflows/local/consensus_ivar' +include { CONSENSUS_BCFTOOLS } from '../subworkflows/local/consensus_bcftools' +include { VARIANTS_LONG_TABLE } from '../subworkflows/local/variants_long_table' +include { ASSEMBLY_SPADES } from '../subworkflows/local/assembly_spades' +include { ASSEMBLY_UNICYCLER } from '../subworkflows/local/assembly_unicycler' +include { ASSEMBLY_MINIA } from '../subworkflows/local/assembly_minia' /* ======================================================================================== @@ -405,6 +405,7 @@ workflow ILLUMINA { ch_tbi = Channel.empty() ch_ivar_counts_multiqc = Channel.empty() ch_bcftools_stats_multiqc = Channel.empty() + ch_snpsift_txt = Channel.empty() ch_snpeff_multiqc = Channel.empty() if (!params.skip_variants && variant_caller == 'ivar') { VARIANTS_IVAR ( @@ -417,13 +418,12 @@ workflow ILLUMINA { PREPARE_GENOME.out.snpeff_config, ch_ivar_variants_header_mqc ) - ch_vcf = VARIANTS_IVAR.out.vcf ch_tbi = VARIANTS_IVAR.out.tbi ch_ivar_counts_multiqc = VARIANTS_IVAR.out.multiqc_tsv ch_bcftools_stats_multiqc = VARIANTS_IVAR.out.stats - ch_snpsift_ltable = VARIANTS_IVAR.out.snpsift_txt ch_snpeff_multiqc = VARIANTS_IVAR.out.snpeff_csv + ch_snpsift_txt = VARIANTS_IVAR.out.snpsift_txt ch_versions = ch_versions.mix(VARIANTS_IVAR.out.versions) } @@ -440,12 +440,11 @@ workflow ILLUMINA { PREPARE_GENOME.out.snpeff_db, PREPARE_GENOME.out.snpeff_config ) - ch_vcf = VARIANTS_BCFTOOLS.out.vcf ch_tbi = VARIANTS_BCFTOOLS.out.tbi ch_bcftools_stats_multiqc = VARIANTS_BCFTOOLS.out.stats - ch_snpsift_ltable = VARIANTS_BCFTOOLS.out.snpsift_txt ch_snpeff_multiqc = VARIANTS_BCFTOOLS.out.snpeff_csv + ch_snpsift_txt = VARIANTS_BCFTOOLS.out.snpsift_txt ch_versions = ch_versions.mix(VARIANTS_BCFTOOLS.out.versions) } @@ -464,11 +463,9 @@ workflow ILLUMINA { ) ch_quast_multiqc = CONSENSUS_IVAR.out.quast_tsv - ch_pangolin_ltable = CONSENSUS_IVAR.out.pangolin_report ch_pangolin_multiqc = CONSENSUS_IVAR.out.pangolin_report ch_nextclade_report = CONSENSUS_IVAR.out.nextclade_report ch_versions = ch_versions.mix(CONSENSUS_IVAR.out.versions) - } // @@ -485,7 +482,6 @@ workflow ILLUMINA { ) ch_quast_multiqc = CONSENSUS_BCFTOOLS.out.quast_tsv - ch_pangolin_ltable = CONSENSUS_BCFTOOLS.out.pangolin_report ch_pangolin_multiqc = CONSENSUS_BCFTOOLS.out.pangolin_report ch_nextclade_report = CONSENSUS_BCFTOOLS.out.nextclade_report ch_versions = ch_versions.mix(CONSENSUS_BCFTOOLS.out.versions) @@ -514,18 +510,16 @@ workflow ILLUMINA { // // SUBWORKFLOW: Create variants long table report // - - if (!params.skip_variants && !params.skip_consensus && !params.skip_long_table && variant_caller) { - LONG_TABLE ( + if (!params.skip_variants && !params.skip_variants_long_table) { + VARIANTS_LONG_TABLE ( ch_vcf, ch_tbi, - ch_snpsift_ltable, - ch_pangolin_ltable + ch_snpsift_txt, + ch_pangolin_multiqc ) - ch_versions = ch_versions.mix(LONG_TABLE.out.versions) + ch_versions = ch_versions.mix(VARIANTS_LONG_TABLE.out.versions) } - // // MODULE: Primer trimming with Cutadapt // From 220bc0ac7ed41ee233385c97d8ee0c96d4f4c231 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 00:05:35 +0000 Subject: [PATCH 214/238] Re-write variants long script --- bin/make_variants_long_table.py | 274 ++++++++++++---------- conf/modules_illumina.config | 4 +- modules/local/make_variants_long_table.nf | 6 +- 3 files changed, 153 insertions(+), 131 deletions(-) diff --git a/bin/make_variants_long_table.py b/bin/make_variants_long_table.py index fac73246..6a6faba7 100755 --- a/bin/make_variants_long_table.py +++ b/bin/make_variants_long_table.py @@ -1,154 +1,176 @@ #!/usr/bin/env python -from matplotlib import table -import pandas as pd +import os import sys -import numpy as np -import re +import glob +import errno +import shutil +import logging import argparse -import glob, os -import io +import pandas as pd +from matplotlib import table + + +logger = logging.getLogger() + pd.set_option('display.max_columns', None) pd.set_option('display.max_rows', None) def parser_args(args=None): - Description = 'Create long/wide tables for ivar/bcftools' - Epilog = """Example usage: python create_long_table.py --samples_path --snpsift_path --pangolin_path --software """ + Description = 'Create long/wide tables containing variant information.' + Epilog = """Example usage: python make_variants_long_table.py --bcftools_query_dir ./bcftools_query/ --snpsift_dir ./snpsift/ --pangolin_dir ./pangolin/""" parser = argparse.ArgumentParser(description=Description, epilog=Epilog) - parser.add_argument('--samples_path','-s',dest="sample", help="Input sample table files path.", required=True ) - parser.add_argument('--snpsift_path','-a',dest="snpsift",help="Input snpsift txt files path.",required=True) - parser.add_argument('--pangolin_path','-l',dest="pangolin",help="Input pangolin csv files path.",required=True) - parser.add_argument('--software','-p',dest="software",help="Input bcftools or ivar.",required=True) - parser.add_argument('--output','-o',dest="output",help="Output filename",required=True) - + parser.add_argument("-bd", "--bcftools_query_dir" , type=str, default="./bcftools_query" , help="Directory containing output of BCFTools query for each sample (default: './bcftools_query').") + parser.add_argument("-sd", "--snpsift_dir" , type=str, default="./snpsift" , help="Directory containing output of SnpSift for each sample (default: './snpsift').") + parser.add_argument("-pd", "--pangolin_dir" , type=str, default="./pangolin" , help="Directory containing output of Pangolin for each sample (default: './pangolin').") + parser.add_argument("-bs", "--bcftools_file_suffix", type=str, default=".table" , help="Suffix to trim off BCFTools query file name to obtain sample name (default: '.table').") + parser.add_argument("-ss", "--snpsift_file_suffix" , type=str, default=".snpsift.txt" , help="Suffix to trim off SnpSift file name to obtain sample name (default: '.snpsift.txt').") + parser.add_argument("-ps", "--pangolin_file_suffix", type=str, default=".pangolin.csv" , help="Suffix to trim off Pangolin file name to obtain sample name (default: '.pangolin.csv').") + parser.add_argument("-of", "--output_file" , type=str, default="variants_long_table.csv", help="Full path to output file (default: 'variants_long_table.csv').") + parser.add_argument("-vc", "--variant_caller" , type=str, default="ivar" , help="Tool used to call the variants (default: 'ivar').") return parser.parse_args(args) -def aa_three_to_one_letter(hgvs_three): - three_syntax_dict= {'Ala': 'A', 'Arg': 'R', 'Asn': 'N', 'Asp': 'D', 'Cys': 'C', - 'Gln': 'Q', 'Glu': 'E', 'Gly': 'G', 'His': 'H', 'Ile': 'I', - 'Leu': 'L', 'Lys': 'K', 'Met': 'M', 'Phe': 'F', 'Pro': 'P', - 'Pyl': 'O', 'Ser': 'S', 'Sec': 'U', 'Thr': 'T', 'Trp': 'W', - 'Tyr': 'Y', 'Val': 'V', 'Asx': 'B', 'Glx': 'Z', 'Xaa': 'X', - 'Xle': 'J', 'Ter': '*'} - hgvs_one=hgvs_three - for key in three_syntax_dict: - if key in hgvs_one: - hgvs_one = hgvs_one.replace(str(key),str(three_syntax_dict[key])) +def make_dir(path): + if not len(path) == 0: + try: + os.makedirs(path) + except OSError as exception: + if exception.errno != errno.EEXIST: + raise + + +def get_file_dict(file_dir, file_suffix): + files = glob.glob(os.path.join(file_dir, f'*{file_suffix}')) + samples = [os.path.basename(x).rstrip(f'{file_suffix}') for x in files] + return dict(zip(samples, files)) + + +def three_letter_aa_to_one(hgvs_three): + aa_dict= { + 'Ala': 'A', 'Arg': 'R', 'Asn': 'N', 'Asp': 'D', 'Cys': 'C', + 'Gln': 'Q', 'Glu': 'E', 'Gly': 'G', 'His': 'H', 'Ile': 'I', + 'Leu': 'L', 'Lys': 'K', 'Met': 'M', 'Phe': 'F', 'Pro': 'P', + 'Pyl': 'O', 'Ser': 'S', 'Sec': 'U', 'Thr': 'T', 'Trp': 'W', + 'Tyr': 'Y', 'Val': 'V', 'Asx': 'B', 'Glx': 'Z', 'Xaa': 'X', + 'Xle': 'J', 'Ter': '*' + } + hgvs_one = hgvs_three + for key in aa_dict: + if key in hgvs_one: + hgvs_one = hgvs_one.replace(str(key),str(aa_dict[key])) return hgvs_one -def create_long(snp_file,snpsift_file,pangolin_file,software): - - ### format of sample table - snp_table=pd.read_table(snp_file, header='infer') - snp_table = snp_table.dropna(how = 'all', axis =1) - - if software=='bcftools': - snp_table.rename(columns={snp_table.columns[0]: "CHROM",snp_table.columns[1]: "POS",snp_table.columns[2]: "REF",snp_table.columns[3]: "ALT",snp_table.columns[4]: "FILTER", snp_table.columns[5]: "DP",snp_table.columns[6]: "AD"}, inplace=True) - new_column = snp_table - new_column[['REF_DP','ALT_DP']] = snp_table['AD'].str.split(',', expand=True) - snp_table = pd.merge(snp_table,new_column,how = 'left') - snp_table[["ALT_DP", "DP"]] = snp_table[["ALT_DP", "DP"]].apply(pd.to_numeric) - snp_table['AF']=snp_table['ALT_DP']/snp_table['DP'] - snp_table['AF'] = snp_table['AF'].round(2) - snp_table = snp_table.loc[:, ~snp_table.columns.str.contains('AD')] - - elif software=='ivar': - snp_table.rename(columns={snp_table.columns[0]: "CHROM",snp_table.columns[1]: "POS",snp_table.columns[2]: "REF",snp_table.columns[3]: "ALT",snp_table.columns[4]: "FILTER",snp_table.columns[5]: "DP",snp_table.columns[6]: "REF_DP", snp_table.columns[7]: "ALT_DP"}, inplace=True) - snp_table[["ALT_DP", "DP"]] = snp_table[["ALT_DP", "DP"]].apply(pd.to_numeric) - snp_table['AF']=snp_table['ALT_DP']/snp_table['DP'] - snp_table['AF'] = snp_table['AF'].round(2) - - ### format of snpsift table - snpsift_table = pd.read_csv(snpsift_file, sep="\t", header = "infer") - snpsift_table = snpsift_table.loc[:, ~snpsift_table.columns.str.contains('^Unnamed')] - colnames_snpsift = list(snpsift_table.columns) - colnames_snpsift = [i.replace('ANN[*].', '') for i in colnames_snpsift] - for i in range(len(colnames_snpsift)): - snpsift_table.rename(columns = {snpsift_table.columns[i]:colnames_snpsift[i]}, inplace = True) - snpsift_table = snpsift_table.loc[:, ['CHROM','POS','REF','ALT','GENE','EFFECT','HGVS_C','HGVS_P']] - snpsift_table_copy = snpsift_table.copy() - - for i in range(len(snpsift_table_copy)): - for j in range(3,8): - snpsift_table_copy.iloc[i,j]= str(snpsift_table.iloc[i,j]).split(",")[0] - - oneletter_s = [] - for index,item in snpsift_table_copy["HGVS_P"].iteritems(): - hgvs_p_oneletter = aa_three_to_one_letter(str(item)) - oneletter_s.append(hgvs_p_oneletter) - - snpsift_table_copy["HGVS_P_1Letter"] = pd.Series(oneletter_s) - - #format of lineages - pangolin_table = pd.read_csv(pangolin_file, sep=",", header = "infer") - lineages = pangolin_table.loc[:,['taxon','lineage']] - - #table long one sample - tl_onesample = pd.DataFrame(data =snp_table) - if software=='bcftools': - tl_onesample["Sample"] = lineages.iloc[0,0].split('_')[1].split('.')[0] - elif software=='ivar': - tl_onesample["Sample"] = lineages.iloc[0,0].split('_')[1].split('.')[0] - tl_onesample["software"] = software - tl_onesample["Lineage"] = lineages.iloc[0,1] - merged_table_long = pd.merge(tl_onesample,snpsift_table_copy,how = 'outer') - return(merged_table_long) - -def same_len(list): - return len(set(list)) == 1 +## Returns a pandas dataframe in the format: + # CHROM POS REF ALT FILTER DP REF_DP ALT_DP AF + # 0 MN908947.3 241 C T PASS 642 375 266 0.41 + # 1 MN908947.3 1875 C T PASS 99 63 34 0.34 +def ivar_bcftools_query_to_table(bcftools_query_file): + table = pd.read_table(bcftools_query_file, header='infer') + table = table.dropna(how='all', axis=1) + old_colnames = list(table.columns) + new_colnames = [x.split(']')[-1].split(':')[-1] for x in old_colnames] + table.rename(columns=dict(zip(old_colnames, new_colnames)), inplace=True) + table[["ALT_DP", "DP"]] = table[["ALT_DP", "DP"]].apply(pd.to_numeric) + table['AF'] = table['ALT_DP'] / table['DP'] + table['AF'] = table['AF'].round(2) + return table + + +## Returns a pandas dataframe in the format: + # CHROM POS REF ALT FILTER DP REF_DP ALT_DP AF + # 0 MN908947.3 241 C T . 24 8 16 0.67 + # 1 MN908947.3 3037 C T . 17 5 12 0.71 +def bcftools_bcftools_query_to_table(bcftools_query_file): + table = pd.read_table(bcftools_query_file, header='infer') + table = table.dropna(how='all', axis=1) + old_colnames = list(table.columns) + new_colnames = [x.split(']')[-1].split(':')[-1] for x in old_colnames] + table.rename(columns=dict(zip(old_colnames, new_colnames)), inplace=True) + table[['REF_DP','ALT_DP']] = table['AD'].str.split(',', expand=True) + table[["ALT_DP", "DP"]] = table[["ALT_DP", "DP"]].apply(pd.to_numeric) + table['AF'] = table['ALT_DP'] / table['DP'] + table['AF'] = table['AF'].round(2) + table.drop('AD', axis=1, inplace=True) + return table + + +def get_pangolin_lineage(pangolin_file): + table = pd.read_csv(pangolin_file, sep=",", header="infer") + return table['lineage'][0] + + +def snpsift_to_table(snpsift_file): + table = pd.read_table(snpsift_file, sep="\t", header='infer') + table = table.loc[:, ~table.columns.str.contains('^Unnamed')] + old_colnames = list(table.columns) + new_colnames = [x.replace('ANN[*].', '') for x in old_colnames] + table.rename(columns=dict(zip(old_colnames, new_colnames)), inplace=True) + table = table.loc[:, ['CHROM', 'POS', 'REF', 'ALT', 'GENE', 'EFFECT', 'HGVS_C', 'HGVS_P']] + + ## Split by comma and get first value in cols = ['ALT','GENE','EFFECT','HGVS_C','HGVS_P'] + for i in range(len(table)): + for j in range(3,8): + table.iloc[i,j] = str(table.iloc[i,j]).split(",")[0] -def concatenatetable(path): - all_filenames = [file for file in glob.glob(path + '/*.csv')] + ## Amino acid substitution + aa = [] + for index,item in table["HGVS_P"].iteritems(): + hgvs_p = three_letter_aa_to_one(str(item)) + aa.append(hgvs_p) + table["HGVS_P_1LETTER"] = pd.Series(aa) + return table - dataframe_list = [] - for file in all_filenames: - dataframe_list.append(pd.read_csv(file,sep=",")) - merged_df = pd.concat(dataframe_list) - return merged_df def main(args=None): args = parser_args(args) - # List vcf table files - table_list = [] - for file in glob.glob(args.sample + "/*"): - table_list.append(file) - table_list.sort() - - #List snpsift files - snpsift_list = [] - for file in glob.glob(args.snpsift + "/*"): - snpsift_list.append(file) - snpsift_list.sort() - - # List pangolin files - pangolin_list = [] - for file in glob.glob(args.pangolin + "/*"): - pangolin_list.append(file) - pangolin_list.sort() - - if not same_len([len(table_list),len(snpsift_list),len(pangolin_list)]): - print("not same number of files for variants, snpsift and pangolin results ") - exit() - - sample_names = [os.path.basename(filename).split("_norm.table")[0] for filename in table_list] - - #create SampleTables folder in the folder where the script is running - if os.path.exists("SampleTables"): - "SampleTables already exists from previous run, please delete or rename the folder." - else: - os.mkdir("SampleTables") + ## Find files and create a dictionary {'sample': '/path/to/file'} + bcftools_files = get_file_dict(args.bcftools_query_dir, args.bcftools_file_suffix) + snpsift_files = get_file_dict(args.snpsift_dir, args.snpsift_file_suffix) + pangolin_files = get_file_dict(args.pangolin_dir, args.pangolin_file_suffix) - for sample_name,table,snpsift,pangolin in zip(sample_names,table_list,snpsift_list,pangolin_list): - long_table_sample = create_long(table,snpsift,pangolin,args.software) - long_table_sample.to_csv('./SampleTables/'+ sample_name + '.csv', header='infer', index=None, sep=',', mode='a') + ## Check all files are provided for each sample + if set(bcftools_files) != set(snpsift_files): + logger.error(f"Number of BCFTools ({len(bcftools_files)}) and SnpSift ({len(snpsift_files)}) files do not match!") + sys.exit(1) + else: + if pangolin_files: + if set(bcftools_files) != set(pangolin_files): + logger.error(f"Number of BCFTools ({len(bcftools_files)}) and Pangolin ({len(pangolin_files)}) files do not match!") + sys.exit(1) + + ## Create per-sample table and write to file + sample_tables = [] + for sample in sorted(bcftools_files): + + ## Read in BCFTools query file + bcftools_table = None + if args.variant_caller == 'ivar': + bcftools_table = ivar_bcftools_query_to_table(bcftools_files[sample]) + elif args.variant_caller == 'bcftools': + bcftools_table = bcftools_bcftools_query_to_table(bcftools_files[sample]) + + ## Read in SnpSift file + snpsift_table = snpsift_to_table(snpsift_files[sample]) + + ## Read in Pangolin lineage file + pangolin_lineage = get_pangolin_lineage(pangolin_files[sample]) + + merged_table = pd.DataFrame(data = bcftools_table) + merged_table.insert(0,'SAMPLE', sample) + merged_table = pd.merge(merged_table, snpsift_table, how='outer') + merged_table['LINEAGE'] = pangolin_lineage + merged_table['CALLER'] = args.variant_caller + sample_tables.append(merged_table) + + ## Merge table across samples + merged_tables = pd.concat(sample_tables) + merged_tables.to_csv(args.output_file, index=False, encoding='utf-8-sig') - merged_df= concatenatetable("./SampleTables") - merged_df.to_csv(args.output, index=False, encoding='utf-8-sig') if __name__ == '__main__': sys.exit(main()) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 7f7360b2..6d2854c3 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -540,11 +540,11 @@ if (!params.skip_variants) { } withName: 'MAKE_VARIANTS_LONG_TABLE' { - ext.args = "--output variants_long_table.csv --software ${variant_caller}" + ext.args = "--variant_caller ${variant_caller}" publishDir = [ path: { "${params.outdir}/variants/${variant_caller}" }, mode: 'copy', - pattern: "*.csv" + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } } diff --git a/modules/local/make_variants_long_table.nf b/modules/local/make_variants_long_table.nf index f891c4e1..c4f9996a 100644 --- a/modules/local/make_variants_long_table.nf +++ b/modules/local/make_variants_long_table.nf @@ -18,9 +18,9 @@ process MAKE_VARIANTS_LONG_TABLE { def args = task.ext.args ?: '' """ make_variants_long_table.py \\ - --samples_path ./bcftools_query \\ - --snpsift_path ./snpsift \\ - --pangolin_path ./pangolin \\ + --bcftools_query_dir ./bcftools_query \\ + --snpsift_dir ./snpsift \\ + --pangolin_dir ./pangolin \\ $args cat <<-END_VERSIONS > versions.yml From b8480eb9ab3cce9cf61750498630837a610d89ae Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 09:59:15 +0000 Subject: [PATCH 215/238] Dont create long table if using --skip_snpeff --- conf/modules_illumina.config | 40 ++++++++++++++++++------------------ nextflow.config | 2 +- nextflow_schema.json | 10 ++++----- workflows/illumina.nf | 2 +- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index 6d2854c3..aeedea5d 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -524,28 +524,28 @@ if (!params.skip_variants) { ] } } - } - if (!params.skip_variants_long_table) { - process { - withName: 'BCFTOOLS_QUERY' { - ext.args = [ - variant_caller == 'ivar' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\\n'" : '', - variant_caller == 'bcftools' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '', - ].join(' ').trim() - publishDir = [ - path: { "${params.outdir}/variants/${variant_caller}" }, - enabled: false - ] - } + if (!params.skip_variants_long_table) { + process { + withName: 'BCFTOOLS_QUERY' { + ext.args = [ + variant_caller == 'ivar' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\\n'" : '', + variant_caller == 'bcftools' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '', + ].join(' ').trim() + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}" }, + enabled: false + ] + } - withName: 'MAKE_VARIANTS_LONG_TABLE' { - ext.args = "--variant_caller ${variant_caller}" - publishDir = [ - path: { "${params.outdir}/variants/${variant_caller}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] + withName: 'MAKE_VARIANTS_LONG_TABLE' { + ext.args = "--variant_caller ${variant_caller}" + publishDir = [ + path: { "${params.outdir}/variants/${variant_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } } } } diff --git a/nextflow.config b/nextflow.config index 1674fdef..e15b78d1 100644 --- a/nextflow.config +++ b/nextflow.config @@ -47,6 +47,7 @@ params { skip_variants_quast = false skip_snpeff = false skip_asciigenome = false + skip_variants_long_table = false skip_multiqc = false // Illumina QC, read trimming and filtering options @@ -73,7 +74,6 @@ params { skip_markduplicates = true skip_picard_metrics = false skip_consensus_plots = false - skip_variants_long_table = false skip_consensus = false skip_variants = false diff --git a/nextflow_schema.json b/nextflow_schema.json index c472de48..553f338c 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -310,6 +310,11 @@ "fa_icon": "fas fa-fast-forward", "description": "Skip generation of QUAST aggregated report for consensus sequences." }, + "skip_variants_long_table": { + "type": "boolean", + "fa_icon": "fas fa-fast-forward", + "description": "Skip long table generation for reporting variants." + }, "skip_multiqc": { "type": "boolean", "fa_icon": "fas fa-fast-forward", @@ -460,11 +465,6 @@ "fa_icon": "fas fa-fast-forward", "description": "Skip creation of consensus base density plots." }, - "skip_variants_long_table": { - "type": "boolean", - "fa_icon": "fas fa-fast-forward", - "description": "Skip long table generation for reporting variants." - }, "skip_consensus": { "type": "boolean", "fa_icon": "fas fa-fast-forward", diff --git a/workflows/illumina.nf b/workflows/illumina.nf index 849b1687..cfd47d77 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -510,7 +510,7 @@ workflow ILLUMINA { // // SUBWORKFLOW: Create variants long table report // - if (!params.skip_variants && !params.skip_variants_long_table) { + if (!params.skip_variants && !params.skip_variants_long_table && !params.skip_snpeff) { VARIANTS_LONG_TABLE ( ch_vcf, ch_tbi, From 1bdebea7a6890cd1b6f6288170787c432febdd59 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 10:31:11 +0000 Subject: [PATCH 216/238] Add Erika to Credits section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98e5dc50..ba09fb9b 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ The nf-core/viralrecon pipeline comes with documentation about the pipeline [usa ## Credits -These scripts were originally written by [Sarai Varona](https://github.com/svarona), [Miguel Juliá](https://github.com/MiguelJulia) and [Sara Monzon](https://github.com/saramonzon) from [BU-ISCIII](https://github.com/BU-ISCIII) and co-ordinated by Isabel Cuesta for the [Institute of Health Carlos III](https://eng.isciii.es/eng.isciii.es/Paginas/Inicio.html), Spain. Through collaboration with the nf-core community the pipeline has now been updated substantially to include additional processing steps, to standardise inputs/outputs and to improve pipeline reporting; implemented and maintained primarily by Harshil Patel ([@drpatelh](https://github.com/drpatelh)) from [Seqera Labs, Spain](https://seqera.io/). +These scripts were originally written by [Sarai Varona](https://github.com/svarona), [Miguel Juliá](https://github.com/MiguelJulia), [Erika Kvalem](https://github.com/ErikaKvalem) and [Sara Monzon](https://github.com/saramonzon) from [BU-ISCIII](https://github.com/BU-ISCIII) and co-ordinated by Isabel Cuesta for the [Institute of Health Carlos III](https://eng.isciii.es/eng.isciii.es/Paginas/Inicio.html), Spain. Through collaboration with the nf-core community the pipeline has now been updated substantially to include additional processing steps, to standardise inputs/outputs and to improve pipeline reporting; implemented and maintained primarily by Harshil Patel ([@drpatelh](https://github.com/drpatelh)) from [Seqera Labs, Spain](https://seqera.io/). The key steps in the Nanopore implementation of the pipeline are carried out using the [ARTIC Network's field bioinformatics pipeline](https://github.com/artic-network/fieldbioinformatics) and were inspired by the amazing work carried out by contributors to the [connor-lab/ncov2019-artic-nf pipeline](https://github.com/connor-lab/ncov2019-artic-nf) originally written by [Matt Bull](https://github.com/m-bull) for use by the [COG-UK](https://github.com/COG-UK) project. Thank you for all of your incredible efforts during this pandemic! From 8b81e1744e61144785fb3234d0e58a13548e8b08 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 10:52:25 +0000 Subject: [PATCH 217/238] Small fixes and error catching --- bin/make_variants_long_table.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/bin/make_variants_long_table.py b/bin/make_variants_long_table.py index 6a6faba7..9a01a962 100755 --- a/bin/make_variants_long_table.py +++ b/bin/make_variants_long_table.py @@ -128,6 +128,16 @@ def snpsift_to_table(snpsift_file): def main(args=None): args = parser_args(args) + ## Create output directory if it doesn't exist + out_dir = os.path.dirname(args.output_file) + make_dir(out_dir) + + ## Check correct variant caller has been provided + variant_callers = ['ivar', 'bcftools', 'nanopolish', 'medaka'] + if args.variant_caller not in variant_callers: + logger.error(f"Invalid option '--variant caller {args.variant_caller}'. Valid options: " + ', '.join(variant_callers)) + sys.exit(1) + ## Find files and create a dictionary {'sample': '/path/to/file'} bcftools_files = get_file_dict(args.bcftools_query_dir, args.bcftools_file_suffix) snpsift_files = get_file_dict(args.snpsift_dir, args.snpsift_file_suffix) @@ -157,14 +167,15 @@ def main(args=None): ## Read in SnpSift file snpsift_table = snpsift_to_table(snpsift_files[sample]) - ## Read in Pangolin lineage file - pangolin_lineage = get_pangolin_lineage(pangolin_files[sample]) - merged_table = pd.DataFrame(data = bcftools_table) merged_table.insert(0,'SAMPLE', sample) merged_table = pd.merge(merged_table, snpsift_table, how='outer') - merged_table['LINEAGE'] = pangolin_lineage merged_table['CALLER'] = args.variant_caller + + ## Read in Pangolin lineage file + if pangolin_files: + merged_table['LINEAGE'] = get_pangolin_lineage(pangolin_files[sample]) + sample_tables.append(merged_table) ## Merge table across samples From a42a0b79afb5dad0ab1fc2e0c9fbaf7d5fa41657 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 10:52:49 +0000 Subject: [PATCH 218/238] Update CI tests --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 249864d9..2c999500 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,9 +55,9 @@ jobs: parameters: - "--consensus_caller ivar" - "--variant_caller bcftools --consensus_caller ivar" - - "--skip_fastp" + - "--skip_fastp --skip_pangolin" - "--skip_variants" - - "--skip_cutadapt" + - "--skip_cutadapt --skip_snpeff" - "--skip_kraken2" - "--skip_assembly" - "--spades_mode corona" From d71311bcadde948d136821bd3a8a6616e5cff4a3 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 11:36:54 +0000 Subject: [PATCH 219/238] Add ONT implementation --- conf/modules_nanopore.config | 24 ++++++++++++++++++++++++ workflows/illumina.nf | 2 +- workflows/nanopore.nf | 26 +++++++++++++++++++++----- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 2dd14426..8f3a0743 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -319,6 +319,30 @@ if (!params.skip_snpeff) { ] } } + + if (!params.skip_variants_long_table) { + process { + withName: 'BCFTOOLS_QUERY' { + ext.args = [ + params.artic_minion_caller == 'nanopolish' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '', + params.artic_minion_caller == 'medaka' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '' + ].join(' ').trim() + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + enabled: false + ] + } + + withName: 'MAKE_VARIANTS_LONG_TABLE' { + ext.args = "--variant_caller ${params.artic_minion_caller}" + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } } if (!params.skip_asciigenome) { diff --git a/workflows/illumina.nf b/workflows/illumina.nf index cfd47d77..c25c3313 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -510,7 +510,7 @@ workflow ILLUMINA { // // SUBWORKFLOW: Create variants long table report // - if (!params.skip_variants && !params.skip_variants_long_table && !params.skip_snpeff) { + if (!params.skip_variants && !params.skip_variants_long_table && params.gff & !params.skip_snpeff) { VARIANTS_LONG_TABLE ( ch_vcf, ch_tbi, diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 6faf2b79..84478f14 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -63,9 +63,10 @@ include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NEXTCLADE } from '../mod // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' -include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_nanopore' -include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' +include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_nanopore' +include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' +include { VARIANTS_LONG_TABLE } from '../subworkflows/local/variants_long_table' /* ======================================================================================== @@ -386,8 +387,8 @@ workflow NANOPORE { PANGOLIN ( ARTIC_MINION.out.fasta ) - ch_pangolin_multiqc = PANGOLIN.out.report - ch_versions = ch_versions.mix(PANGOLIN.out.versions.first().ifEmpty(null)) + ch_pangolin_multiqc = PANGOLIN.out.report + ch_versions = ch_versions.mix(PANGOLIN.out.versions.first().ifEmpty(null)) } // @@ -441,6 +442,7 @@ workflow NANOPORE { // SUBWORKFLOW: Annotate variants with snpEff // ch_snpeff_multiqc = Channel.empty() + ch_snpsift_txt = Channel.empty() if (params.gff && !params.skip_snpeff) { SNPEFF_SNPSIFT ( VCFLIB_VCFUNIQ.out.vcf, @@ -449,6 +451,7 @@ workflow NANOPORE { PREPARE_GENOME.out.fasta ) ch_snpeff_multiqc = SNPEFF_SNPSIFT.out.csv + ch_snpsift_txt = SNPEFF_SNPSIFT.out.snpsift_txt ch_versions = ch_versions.mix(SNPEFF_SNPSIFT.out.versions) } @@ -480,6 +483,19 @@ workflow NANOPORE { ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first().ifEmpty(null)) } + // + // SUBWORKFLOW: Create variants long table report + // + if (!params.skip_variants_long_table && params.gff && !params.skip_snpeff) { + VARIANTS_LONG_TABLE ( + VCFLIB_VCFUNIQ.out.vcf, + TABIX_TABIX.out.tbi, + ch_snpsift_txt, + ch_pangolin_multiqc + ) + ch_versions = ch_versions.mix(VARIANTS_LONG_TABLE.out.versions) + } + // // MODULE: Pipeline reporting // From 9b65eedef598ac339c36f46ace44e6f0661876e5 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 12:41:33 +0000 Subject: [PATCH 220/238] Add last bits for Illumina implementation --- README.md | 1 + docs/output.md | 25 +++++++++++++++++++++++++ workflows/illumina.nf | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ba09fb9b..c520954d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and * Consensus assessment report ([`QUAST`](http://quast.sourceforge.net/quast)) * Lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)) * Clade assignment, mutation calling and sequence quality checks ([`Nextclade`](https://github.com/nextstrain/nextclade)) + 9. Create variants long format table collating per-sample information for individual variants ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html)), functional effect prediction ([`SnpSift`](http://snpeff.sourceforge.net/SnpSift.html)) and lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)). 6. _De novo_ assembly 1. Primer trimming ([`Cutadapt`](https://cutadapt.readthedocs.io/en/stable/guide.html); *amplicon data only*) 2. Choice of multiple assembly tools ([`SPAdes`](http://cab.spbu.ru/software/spades/) *||* [`Unicycler`](https://github.com/rrwick/Unicycler) *||* [`minia`](https://github.com/GATB/minia)) diff --git a/docs/output.md b/docs/output.md index 5f4b6888..f0feec22 100644 --- a/docs/output.md +++ b/docs/output.md @@ -302,6 +302,7 @@ An example MultiQC report generated from a full-sized dataset can be viewed on t * [QUAST](#quast) - Consensus assessment report * [Pangolin](#pangolin) - Lineage analysis * [Nextclade](#nextclade) - Clade assignment, mutation calling and sequence quality checks + * [Variants long table](#variants-long-table) - Collate per-sample information for individual variants, functional effect prediction and lineage analysis * [De novo assembly](#illumina-de-novo-assembly) * [Cutadapt](#cutadapt) - Primer trimming for amplicon data * [SPAdes](#spades) *||* [Unicycler](#unicycler) *||* [minia](#minia) - Viral genome assembly @@ -672,6 +673,30 @@ Phylogenetic Assignment of Named Global Outbreak LINeages ([Pangolin](https://gi [Nextclade](https://github.com/nextstrain/nextclade) performs viral genome clade assignment, mutation calling and sequence quality checks for the consensus sequences generated in this pipeline. Similar to Pangolin, it has been used extensively during the COVID-19 pandemic. A [web application](https://clades.nextstrain.org/) also exists that allows users to upload genome sequences via a web browser. +### Variants long table + +
+Output files + +* `variants//` + * `variants_long_table.csv`: Long format table collating per-sample information for individual variants, functional effect prediction and lineage analysis. + +**NB:** The value of `` in the output directory name above is determined by the `--variant_caller` parameter (Default: 'ivar' for '--protocol amplicon' and 'bcftools' for '--protocol metagenomic'). + +
+ +Create variants long format table collating per-sample information for individual variants ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html)), functional effect prediction ([`SnpSift`](http://snpeff.sourceforge.net/SnpSift.html)) and lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)). + +The more pertinent information is summarised in this table to make it easier for researchers to assess the impact of variants found amongst the sequenced sample(s). An example of the fields included in the table are shown below: + +``` +SAMPLE,CHROM,POS,REF,ALT,FILTER,DP,REF_DP,ALT_DP,AF,GENE,EFFECT,HGVS_C,HGVS_P,HGVS_P_1LETTER,CALLER,LINEAGE +SAMPLE1_PE,MN908947.3,241,C,T,PASS,489,4,483,0.99,orf1ab,upstream_gene_variant,c.-25C>T,.,.,ivar,B.1 +SAMPLE1_PE,MN908947.3,1875,C,T,PASS,92,62,29,0.32,orf1ab,missense_variant,c.1610C>T,p.Ala537Val,p.A537V,ivar,B.1 +SAMPLE1_PE,MN908947.3,3037,C,T,PASS,213,0,213,1.0,orf1ab,synonymous_variant,c.2772C>T,p.Phe924Phe,p.F924F,ivar,B.1 +SAMPLE1_PE,MN908947.3,11719,G,A,PASS,195,9,186,0.95,orf1ab,synonymous_variant,c.11454G>A,p.Gln3818Gln,p.Q3818Q,ivar,B.1 +``` + ## Illumina: De novo assembly A file called `summary_assembly_metrics_mqc.csv` containing a selection of read alignment and *de novo* assembly related metrics will be saved in the `multiqc/` results directory. The same metrics will also be added to the top of the MultiQC report. diff --git a/workflows/illumina.nf b/workflows/illumina.nf index cfd47d77..b8bc5484 100644 --- a/workflows/illumina.nf +++ b/workflows/illumina.nf @@ -510,7 +510,7 @@ workflow ILLUMINA { // // SUBWORKFLOW: Create variants long table report // - if (!params.skip_variants && !params.skip_variants_long_table && !params.skip_snpeff) { + if (!params.skip_variants && !params.skip_variants_long_table && params.gff && !params.skip_snpeff) { VARIANTS_LONG_TABLE ( ch_vcf, ch_tbi, From e2c3be894241be37489d7b66a768166956819701 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 12:50:56 +0000 Subject: [PATCH 221/238] Revert ONT changes --- conf/modules_nanopore.config | 24 ------------------------ docs/output.md | 2 +- workflows/nanopore.nf | 20 +++----------------- 3 files changed, 4 insertions(+), 42 deletions(-) diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 8f3a0743..2dd14426 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -319,30 +319,6 @@ if (!params.skip_snpeff) { ] } } - - if (!params.skip_variants_long_table) { - process { - withName: 'BCFTOOLS_QUERY' { - ext.args = [ - params.artic_minion_caller == 'nanopolish' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '', - params.artic_minion_caller == 'medaka' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '' - ].join(' ').trim() - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}" }, - enabled: false - ] - } - - withName: 'MAKE_VARIANTS_LONG_TABLE' { - ext.args = "--variant_caller ${params.artic_minion_caller}" - publishDir = [ - path: { "${params.outdir}/${params.artic_minion_caller}" }, - mode: 'copy', - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - } - } } if (!params.skip_asciigenome) { diff --git a/docs/output.md b/docs/output.md index f0feec22..c66e9abc 100644 --- a/docs/output.md +++ b/docs/output.md @@ -687,7 +687,7 @@ Phylogenetic Assignment of Named Global Outbreak LINeages ([Pangolin](https://gi Create variants long format table collating per-sample information for individual variants ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html)), functional effect prediction ([`SnpSift`](http://snpeff.sourceforge.net/SnpSift.html)) and lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)). -The more pertinent information is summarised in this table to make it easier for researchers to assess the impact of variants found amongst the sequenced sample(s). An example of the fields included in the table are shown below: +The more pertinent variant information is summarised in this table to make it easier for researchers to assess the impact of variants found amongst the sequenced sample(s). An example of the fields included in the table are shown below: ``` SAMPLE,CHROM,POS,REF,ALT,FILTER,DP,REF_DP,ALT_DP,AF,GENE,EFFECT,HGVS_C,HGVS_P,HGVS_P_1LETTER,CALLER,LINEAGE diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index 84478f14..a1bf36d9 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -63,10 +63,9 @@ include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NEXTCLADE } from '../mod // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' -include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_nanopore' -include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' -include { VARIANTS_LONG_TABLE } from '../subworkflows/local/variants_long_table' +include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_nanopore' +include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' /* ======================================================================================== @@ -483,19 +482,6 @@ workflow NANOPORE { ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first().ifEmpty(null)) } - // - // SUBWORKFLOW: Create variants long table report - // - if (!params.skip_variants_long_table && params.gff && !params.skip_snpeff) { - VARIANTS_LONG_TABLE ( - VCFLIB_VCFUNIQ.out.vcf, - TABIX_TABIX.out.tbi, - ch_snpsift_txt, - ch_pangolin_multiqc - ) - ch_versions = ch_versions.mix(VARIANTS_LONG_TABLE.out.versions) - } - // // MODULE: Pipeline reporting // From 9b670f915238c5a6669fd41b237e9a8238d145a7 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 12:52:38 +0000 Subject: [PATCH 222/238] Fix markdown --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index c66e9abc..8bffd875 100644 --- a/docs/output.md +++ b/docs/output.md @@ -689,7 +689,7 @@ Create variants long format table collating per-sample information for individua The more pertinent variant information is summarised in this table to make it easier for researchers to assess the impact of variants found amongst the sequenced sample(s). An example of the fields included in the table are shown below: -``` +```bash SAMPLE,CHROM,POS,REF,ALT,FILTER,DP,REF_DP,ALT_DP,AF,GENE,EFFECT,HGVS_C,HGVS_P,HGVS_P_1LETTER,CALLER,LINEAGE SAMPLE1_PE,MN908947.3,241,C,T,PASS,489,4,483,0.99,orf1ab,upstream_gene_variant,c.-25C>T,.,.,ivar,B.1 SAMPLE1_PE,MN908947.3,1875,C,T,PASS,92,62,29,0.32,orf1ab,missense_variant,c.1610C>T,p.Ala537Val,p.A537V,ivar,B.1 From 5ad7b924c531a66501d0bab7202d2208c688d61c Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 13:12:38 +0000 Subject: [PATCH 223/238] Add long format table to ONT output --- README.md | 3 ++- conf/modules_nanopore.config | 24 ++++++++++++++++++++++++ docs/output.md | 25 +++++++++++++++++++++++++ workflows/nanopore.nf | 20 +++++++++++++++++--- 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c520954d..57dbce79 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and * Consensus assessment report ([`QUAST`](http://quast.sourceforge.net/quast)) * Lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)) * Clade assignment, mutation calling and sequence quality checks ([`Nextclade`](https://github.com/nextstrain/nextclade)) - 9. Create variants long format table collating per-sample information for individual variants ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html)), functional effect prediction ([`SnpSift`](http://snpeff.sourceforge.net/SnpSift.html)) and lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)). + 9. Create variants long format table collating per-sample information for individual variants ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html)), functional effect prediction ([`SnpSift`](http://snpeff.sourceforge.net/SnpSift.html)) and lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)) 6. _De novo_ assembly 1. Primer trimming ([`Cutadapt`](https://cutadapt.readthedocs.io/en/stable/guide.html); *amplicon data only*) 2. Choice of multiple assembly tools ([`SPAdes`](http://cab.spbu.ru/software/spades/) *||* [`Unicycler`](https://github.com/rrwick/Unicycler) *||* [`minia`](https://github.com/GATB/minia)) @@ -73,6 +73,7 @@ The SRA download functionality has been removed from the pipeline (`>=2.1`) and * Lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)) * Clade assignment, mutation calling and sequence quality checks ([`Nextclade`](https://github.com/nextstrain/nextclade)) * Individual variant screenshots with annotation tracks ([`ASCIIGenome`](https://asciigenome.readthedocs.io/en/latest/)) + * Create variants long format table collating per-sample information for individual variants ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html)), functional effect prediction ([`SnpSift`](http://snpeff.sourceforge.net/SnpSift.html)) and lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)) 8. Present QC, visualisation and custom reporting for sequencing, raw reads, alignment and variant calling results ([`MultiQC`](http://multiqc.info/)) ## Quick Start diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 2dd14426..8f3a0743 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -319,6 +319,30 @@ if (!params.skip_snpeff) { ] } } + + if (!params.skip_variants_long_table) { + process { + withName: 'BCFTOOLS_QUERY' { + ext.args = [ + params.artic_minion_caller == 'nanopolish' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '', + params.artic_minion_caller == 'medaka' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '' + ].join(' ').trim() + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + enabled: false + ] + } + + withName: 'MAKE_VARIANTS_LONG_TABLE' { + ext.args = "--variant_caller ${params.artic_minion_caller}" + publishDir = [ + path: { "${params.outdir}/${params.artic_minion_caller}" }, + mode: 'copy', + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } + } } if (!params.skip_asciigenome) { diff --git a/docs/output.md b/docs/output.md index 8bffd875..8c8e8d8b 100644 --- a/docs/output.md +++ b/docs/output.md @@ -22,6 +22,7 @@ The directories listed below will be created in the results directory after the * [Pangolin](#nanopore-pangolin) - Lineage analysis * [Nextclade](#nanopore-nextclade) - Clade assignment, mutation calling and sequence quality checks * [ASCIIGenome](#nanopore-asciigenome) - Individual variant screenshots with annotation tracks + * [Variants long table](#nanopore-variants-long-table) - Collate per-sample information for individual variants, functional effect prediction and lineage analysis * [Workflow reporting](#nanopore-workflow-reporting) * [MultiQC](#nanopore-multiqc) - Present QC, visualisation and custom reporting for sequencing, raw reads, alignment and variant calling results @@ -257,6 +258,30 @@ As described in the documentation, [ASCIIGenome](https://asciigenome.readthedocs

ASCIIGenome screenshot

+### Nanopore: Variants long table + +
+Output files + +* `/` + * `variants_long_table.csv`: Long format table collating per-sample information for individual variants, functional effect prediction and lineage analysis. + +**NB:** The value of `` in the output directory name above is determined by the `--artic_minion_caller` parameter (Default: 'nanopolish'). + +
+ +Create variants long format table collating per-sample information for individual variants ([`BCFTools`](http://samtools.github.io/bcftools/bcftools.html)), functional effect prediction ([`SnpSift`](http://snpeff.sourceforge.net/SnpSift.html)) and lineage analysis ([`Pangolin`](https://github.com/cov-lineages/pangolin)). + +The more pertinent variant information is summarised in this table to make it easier for researchers to assess the impact of variants found amongst the sequenced sample(s). An example of the fields included in the table are shown below: + +```bash +SAMPLE,CHROM,POS,REF,ALT,FILTER,DP,REF_DP,ALT_DP,AF,GENE,EFFECT,HGVS_C,HGVS_P,HGVS_P_1LETTER,CALLER,LINEAGE +SAMPLE1_PE,MN908947.3,241,C,T,PASS,489,4,483,0.99,orf1ab,upstream_gene_variant,c.-25C>T,.,.,ivar,B.1 +SAMPLE1_PE,MN908947.3,1875,C,T,PASS,92,62,29,0.32,orf1ab,missense_variant,c.1610C>T,p.Ala537Val,p.A537V,ivar,B.1 +SAMPLE1_PE,MN908947.3,3037,C,T,PASS,213,0,213,1.0,orf1ab,synonymous_variant,c.2772C>T,p.Phe924Phe,p.F924F,ivar,B.1 +SAMPLE1_PE,MN908947.3,11719,G,A,PASS,195,9,186,0.95,orf1ab,synonymous_variant,c.11454G>A,p.Gln3818Gln,p.Q3818Q,ivar,B.1 +``` + ## Nanopore: Workflow reporting ### Nanopore: MultiQC diff --git a/workflows/nanopore.nf b/workflows/nanopore.nf index a1bf36d9..84478f14 100644 --- a/workflows/nanopore.nf +++ b/workflows/nanopore.nf @@ -63,9 +63,10 @@ include { MULTIQC_TSV_FROM_LIST as MULTIQC_TSV_NEXTCLADE } from '../mod // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' -include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_nanopore' -include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' +include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { PREPARE_GENOME } from '../subworkflows/local/prepare_genome_nanopore' +include { SNPEFF_SNPSIFT } from '../subworkflows/local/snpeff_snpsift' +include { VARIANTS_LONG_TABLE } from '../subworkflows/local/variants_long_table' /* ======================================================================================== @@ -482,6 +483,19 @@ workflow NANOPORE { ch_versions = ch_versions.mix(ASCIIGENOME.out.versions.first().ifEmpty(null)) } + // + // SUBWORKFLOW: Create variants long table report + // + if (!params.skip_variants_long_table && params.gff && !params.skip_snpeff) { + VARIANTS_LONG_TABLE ( + VCFLIB_VCFUNIQ.out.vcf, + TABIX_TABIX.out.tbi, + ch_snpsift_txt, + ch_pangolin_multiqc + ) + ch_versions = ch_versions.mix(VARIANTS_LONG_TABLE.out.versions) + } + // // MODULE: Pipeline reporting // From 06b8c04e5b4341c67b9ac1cf8216874b0061041b Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 14:30:35 +0000 Subject: [PATCH 224/238] Add long table for nanopolish --- bin/make_variants_long_table.py | 81 +++++++++++++++++++++++++-------- conf/modules_nanopore.config | 5 +- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/bin/make_variants_long_table.py b/bin/make_variants_long_table.py index 9a01a962..3df667ef 100755 --- a/bin/make_variants_long_table.py +++ b/bin/make_variants_long_table.py @@ -45,6 +45,7 @@ def make_dir(path): def get_file_dict(file_dir, file_suffix): files = glob.glob(os.path.join(file_dir, f'*{file_suffix}')) samples = [os.path.basename(x).rstrip(f'{file_suffix}') for x in files] + return dict(zip(samples, files)) @@ -61,6 +62,7 @@ def three_letter_aa_to_one(hgvs_three): for key in aa_dict: if key in hgvs_one: hgvs_one = hgvs_one.replace(str(key),str(aa_dict[key])) + return hgvs_one @@ -74,9 +76,12 @@ def ivar_bcftools_query_to_table(bcftools_query_file): old_colnames = list(table.columns) new_colnames = [x.split(']')[-1].split(':')[-1] for x in old_colnames] table.rename(columns=dict(zip(old_colnames, new_colnames)), inplace=True) - table[["ALT_DP", "DP"]] = table[["ALT_DP", "DP"]].apply(pd.to_numeric) - table['AF'] = table['ALT_DP'] / table['DP'] - table['AF'] = table['AF'].round(2) + + if not table.empty: + table[["ALT_DP", "DP"]] = table[["ALT_DP", "DP"]].apply(pd.to_numeric) + table['AF'] = table['ALT_DP'] / table['DP'] + table['AF'] = table['AF'].round(2) + return table @@ -90,11 +95,41 @@ def bcftools_bcftools_query_to_table(bcftools_query_file): old_colnames = list(table.columns) new_colnames = [x.split(']')[-1].split(':')[-1] for x in old_colnames] table.rename(columns=dict(zip(old_colnames, new_colnames)), inplace=True) - table[['REF_DP','ALT_DP']] = table['AD'].str.split(',', expand=True) - table[["ALT_DP", "DP"]] = table[["ALT_DP", "DP"]].apply(pd.to_numeric) - table['AF'] = table['ALT_DP'] / table['DP'] - table['AF'] = table['AF'].round(2) - table.drop('AD', axis=1, inplace=True) + + if not table.empty: + table[['REF_DP','ALT_DP']] = table['AD'].str.split(',', expand=True) + table[["ALT_DP", "DP"]] = table[["ALT_DP", "DP"]].apply(pd.to_numeric) + table['AF'] = table['ALT_DP'] / table['DP'] + table['AF'] = table['AF'].round(2) + table.drop('AD', axis=1, inplace=True) + + return table + + +## Returns a pandas dataframe in the format: + # CHROM POS REF ALT FILTER DP REF_DP ALT_DP AF + # 0 MN908947.3 241 C T PASS 30 1 29 0.97 + # 1 MN908947.3 1163 A T PASS 28 0 28 1.00 +def nanopolish_bcftools_query_to_table(bcftools_query_file): + table = pd.read_table(bcftools_query_file, header='infer') + table = table.dropna(how='all', axis=1) + old_colnames = list(table.columns) + new_colnames = [x.split(']')[-1].split(':')[-1] for x in old_colnames] + table.rename(columns=dict(zip(old_colnames, new_colnames)), inplace=True) + + ## Split out ref/alt depths from StrandSupport column + if not table.empty: + table_cp = table.copy() + table_cp[['FORW_REF_DP','REV_REF_DP', 'FORW_ALT_DP','REV_ALT_DP']] = table_cp['StrandSupport'].str.split(',', expand=True) + table_cp[['FORW_REF_DP','REV_REF_DP', 'FORW_ALT_DP','REV_ALT_DP']] = table_cp[['FORW_REF_DP','REV_REF_DP', 'FORW_ALT_DP','REV_ALT_DP']].apply(pd.to_numeric) + + table['DP'] = table_cp[['FORW_REF_DP','REV_REF_DP', 'FORW_ALT_DP','REV_ALT_DP']].sum(axis=1) + table['REF_DP'] = table_cp[['FORW_REF_DP','REV_REF_DP']].sum(axis=1) + table['ALT_DP'] = table_cp[['FORW_ALT_DP','REV_ALT_DP']].sum(axis=1) + table['AF'] = table['ALT_DP'] / table['DP'] + table['AF'] = table['AF'].round(2) + table.drop('StrandSupport', axis=1, inplace=True) + return table @@ -122,6 +157,7 @@ def snpsift_to_table(snpsift_file): hgvs_p = three_letter_aa_to_one(str(item)) aa.append(hgvs_p) table["HGVS_P_1LETTER"] = pd.Series(aa) + return table @@ -163,24 +199,29 @@ def main(args=None): bcftools_table = ivar_bcftools_query_to_table(bcftools_files[sample]) elif args.variant_caller == 'bcftools': bcftools_table = bcftools_bcftools_query_to_table(bcftools_files[sample]) + elif args.variant_caller == 'nanopolish': + bcftools_table = nanopolish_bcftools_query_to_table(bcftools_files[sample]) - ## Read in SnpSift file - snpsift_table = snpsift_to_table(snpsift_files[sample]) + if not bcftools_table.empty: - merged_table = pd.DataFrame(data = bcftools_table) - merged_table.insert(0,'SAMPLE', sample) - merged_table = pd.merge(merged_table, snpsift_table, how='outer') - merged_table['CALLER'] = args.variant_caller + ## Read in SnpSift file + snpsift_table = snpsift_to_table(snpsift_files[sample]) - ## Read in Pangolin lineage file - if pangolin_files: - merged_table['LINEAGE'] = get_pangolin_lineage(pangolin_files[sample]) + merged_table = pd.DataFrame(data = bcftools_table) + merged_table.insert(0,'SAMPLE', sample) + merged_table = pd.merge(merged_table, snpsift_table, how='outer') + merged_table['CALLER'] = args.variant_caller + + ## Read in Pangolin lineage file + if pangolin_files: + merged_table['LINEAGE'] = get_pangolin_lineage(pangolin_files[sample]) - sample_tables.append(merged_table) + sample_tables.append(merged_table) ## Merge table across samples - merged_tables = pd.concat(sample_tables) - merged_tables.to_csv(args.output_file, index=False, encoding='utf-8-sig') + if sample_tables: + merged_tables = pd.concat(sample_tables) + merged_tables.to_csv(args.output_file, index=False, encoding='utf-8-sig') if __name__ == '__main__': diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index 8f3a0743..c22ec4d1 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -323,9 +323,10 @@ if (!params.skip_snpeff) { if (!params.skip_variants_long_table) { process { withName: 'BCFTOOLS_QUERY' { + cache = false ext.args = [ - params.artic_minion_caller == 'nanopolish' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '', - params.artic_minion_caller == 'medaka' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '' + params.artic_minion_caller == 'nanopolish' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t%StrandSupport\\n'" : '', + params.artic_minion_caller == 'medaka' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t%DP\\t%AC\\n'" : '' ].join(' ').trim() publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}" }, From 7e1b09abe09092632f2d14b8ca34d595715cbdb4 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 15:02:04 +0000 Subject: [PATCH 225/238] Add long table for medaka --- bin/make_variants_long_table.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/bin/make_variants_long_table.py b/bin/make_variants_long_table.py index 3df667ef..44b87950 100755 --- a/bin/make_variants_long_table.py +++ b/bin/make_variants_long_table.py @@ -133,8 +133,30 @@ def nanopolish_bcftools_query_to_table(bcftools_query_file): return table +## Returns a pandas dataframe in the format: + # CHROM POS REF ALT FILTER DP REF_DP ALT_DP AF + # 0 MN908947.3 241 C T PASS 21 0 21 1.00 + # 1 MN908947.3 3037 C T PASS 28 0 25 0.89 +def medaka_bcftools_query_to_table(bcftools_query_file): + table = pd.read_table(bcftools_query_file, header='infer') + table = table.dropna(how='all', axis=1) + old_colnames = list(table.columns) + new_colnames = [x.split(']')[-1].split(':')[-1] for x in old_colnames] + table.rename(columns=dict(zip(old_colnames, new_colnames)), inplace=True) + + if not table.empty: + table[['REF_DP','ALT_DP']] = table['AC'].str.split(',', expand=True) + table[["ALT_DP", "DP"]] = table[["ALT_DP", "DP"]].apply(pd.to_numeric) + table['AF'] = table['ALT_DP'] / table['DP'] + table['AF'] = table['AF'].round(2) + table.drop('AC', axis=1, inplace=True) + + return table + + def get_pangolin_lineage(pangolin_file): table = pd.read_csv(pangolin_file, sep=",", header="infer") + return table['lineage'][0] @@ -201,6 +223,8 @@ def main(args=None): bcftools_table = bcftools_bcftools_query_to_table(bcftools_files[sample]) elif args.variant_caller == 'nanopolish': bcftools_table = nanopolish_bcftools_query_to_table(bcftools_files[sample]) + elif args.variant_caller == 'medaka': + bcftools_table = medaka_bcftools_query_to_table(bcftools_files[sample]) if not bcftools_table.empty: From 7728bf58225c02ed633d6d37bb919fb7662fd335 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 15:28:12 +0000 Subject: [PATCH 226/238] Update modules in pipeline --- bin/make_variants_long_table.py | 2 +- conf/modules_illumina.config | 1 + conf/modules_nanopore.config | 2 +- modules.json | 8 +++---- .../nf-core/modules/bcftools/query/main.nf | 23 +++++++++---------- .../nf-core/modules/bcftools/query/meta.yml | 15 ++++++------ .../modules/nextclade/datasetget/main.nf | 6 ++--- modules/nf-core/modules/nextclade/run/main.nf | 6 ++--- modules/nf-core/modules/pangolin/main.nf | 6 ++--- subworkflows/local/variants_long_table.nf | 12 ++++------ 10 files changed, 39 insertions(+), 42 deletions(-) diff --git a/bin/make_variants_long_table.py b/bin/make_variants_long_table.py index 44b87950..750ffe16 100755 --- a/bin/make_variants_long_table.py +++ b/bin/make_variants_long_table.py @@ -25,7 +25,7 @@ def parser_args(args=None): parser.add_argument("-bd", "--bcftools_query_dir" , type=str, default="./bcftools_query" , help="Directory containing output of BCFTools query for each sample (default: './bcftools_query').") parser.add_argument("-sd", "--snpsift_dir" , type=str, default="./snpsift" , help="Directory containing output of SnpSift for each sample (default: './snpsift').") parser.add_argument("-pd", "--pangolin_dir" , type=str, default="./pangolin" , help="Directory containing output of Pangolin for each sample (default: './pangolin').") - parser.add_argument("-bs", "--bcftools_file_suffix", type=str, default=".table" , help="Suffix to trim off BCFTools query file name to obtain sample name (default: '.table').") + parser.add_argument("-bs", "--bcftools_file_suffix", type=str, default=".bcftools_query.txt" , help="Suffix to trim off BCFTools query file name to obtain sample name (default: '.bcftools_query.txt').") parser.add_argument("-ss", "--snpsift_file_suffix" , type=str, default=".snpsift.txt" , help="Suffix to trim off SnpSift file name to obtain sample name (default: '.snpsift.txt').") parser.add_argument("-ps", "--pangolin_file_suffix", type=str, default=".pangolin.csv" , help="Suffix to trim off Pangolin file name to obtain sample name (default: '.pangolin.csv').") parser.add_argument("-of", "--output_file" , type=str, default="variants_long_table.csv", help="Full path to output file (default: 'variants_long_table.csv').") diff --git a/conf/modules_illumina.config b/conf/modules_illumina.config index aeedea5d..d21a5eeb 100644 --- a/conf/modules_illumina.config +++ b/conf/modules_illumina.config @@ -532,6 +532,7 @@ if (!params.skip_variants) { variant_caller == 'ivar' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%REF_DP\\t]\\t[%ALT_DP\\t]\\n'" : '', variant_caller == 'bcftools' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t[%DP\\t]\\t[%AD\\t]\\n'" : '', ].join(' ').trim() + ext.prefix = { "${meta.id}.bcftools_query" } publishDir = [ path: { "${params.outdir}/variants/${variant_caller}" }, enabled: false diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index c22ec4d1..b2754800 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -323,11 +323,11 @@ if (!params.skip_snpeff) { if (!params.skip_variants_long_table) { process { withName: 'BCFTOOLS_QUERY' { - cache = false ext.args = [ params.artic_minion_caller == 'nanopolish' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t%StrandSupport\\n'" : '', params.artic_minion_caller == 'medaka' ? "-H -f '%CHROM\\t%POS\\t%REF\\t%ALT\\t%FILTER\\t%DP\\t%AC\\n'" : '' ].join(' ').trim() + ext.prefix = { "${meta.id}.bcftools_query" } publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}" }, enabled: false diff --git a/modules.json b/modules.json index de69a691..9295f28c 100644 --- a/modules.json +++ b/modules.json @@ -28,7 +28,7 @@ "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "bcftools/query": { - "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + "git_sha": "aa2eca69975dc3b53b0c2fbffcaf70b0112c08d8" }, "bcftools/stats": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" @@ -94,13 +94,13 @@ "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" }, "nextclade/datasetget": { - "git_sha": "d70526c80665d412404e50708998b19cfb0dee7d" + "git_sha": "aa2eca69975dc3b53b0c2fbffcaf70b0112c08d8" }, "nextclade/run": { - "git_sha": "d70526c80665d412404e50708998b19cfb0dee7d" + "git_sha": "aa2eca69975dc3b53b0c2fbffcaf70b0112c08d8" }, "pangolin": { - "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" + "git_sha": "aa2eca69975dc3b53b0c2fbffcaf70b0112c08d8" }, "picard/collectmultiplemetrics": { "git_sha": "e751e5040af57e1b4e06ed4e0f3efe6de25c1683" diff --git a/modules/nf-core/modules/bcftools/query/main.nf b/modules/nf-core/modules/bcftools/query/main.nf index 49a73cd1..a165b103 100644 --- a/modules/nf-core/modules/bcftools/query/main.nf +++ b/modules/nf-core/modules/bcftools/query/main.nf @@ -8,30 +8,29 @@ process BCFTOOLS_QUERY { 'quay.io/biocontainers/bcftools:1.14--h88f3f91_0' }" input: - tuple val(meta), path(vcf), path(index) - path(regions) - path(targets) - path(samples) + tuple val(meta), path(vcf), path(tbi) + path regions + path targets + path samples output: - tuple val(meta), path("*.table") , emit: vcf + tuple val(meta), path("*.txt"), emit: txt path "versions.yml" , emit: versions script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - def regions_file = regions ? "--regions-file ${regions}" : "" + def regions_file = regions ? "--regions-file ${regions}" : "" def targets_file = targets ? "--targets-file ${targets}" : "" def samples_file = samples ? "--samples-file ${samples}" : "" - """ bcftools query \\ - --output ${prefix}.table \\ - ${regions_file} \\ - ${targets_file} \\ - ${samples_file} \\ + --output ${prefix}.txt \\ + $regions_file \\ + $targets_file \\ + $samples_file \\ $args \\ - ${vcf} + $vcf cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/modules/bcftools/query/meta.yml b/modules/nf-core/modules/bcftools/query/meta.yml index e450f73e..e49f13c8 100644 --- a/modules/nf-core/modules/bcftools/query/meta.yml +++ b/modules/nf-core/modules/bcftools/query/meta.yml @@ -23,22 +23,20 @@ input: type: file description: | The vcf file to be qeuried. - e.g. 'file.vcf' - - index: + pattern: "*.{vcf.gz, vcf}" + - tbi: type: file description: | The tab index for the VCF file to be inspected. - e.g. 'file.tbi' + pattern: "*.tbi" - regions: type: file description: | Optionally, restrict the operation to regions listed in this file. - e.g. 'file.vcf' - targets: type: file description: | Optionally, restrict the operation to regions listed in this file (doesn't rely upon index files) - e.g. 'file.vcf' - samples: type: file description: | @@ -50,13 +48,14 @@ output: description: | Groovy Map containing sample information e.g. [ id:'test', single_end:false ] - - vcf: + - txt: type: file - description: VCF query output file - pattern: "*.{vcf.gz}" + description: BCFTools query output file + pattern: "*.txt" - versions: type: file description: File containing software versions pattern: "versions.yml" authors: - "@abhi18av" + - "@drpatelh" diff --git a/modules/nf-core/modules/nextclade/datasetget/main.nf b/modules/nf-core/modules/nextclade/datasetget/main.nf index 00dc8ff9..75bb88f3 100644 --- a/modules/nf-core/modules/nextclade/datasetget/main.nf +++ b/modules/nf-core/modules/nextclade/datasetget/main.nf @@ -2,10 +2,10 @@ process NEXTCLADE_DATASETGET { tag "$dataset" label 'process_low' - conda (params.enable_conda ? "bioconda::nextclade=1.10.1" : null) + conda (params.enable_conda ? "bioconda::nextclade=1.10.2" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/nextclade:1.10.1--h9ee0642_0' : - 'quay.io/biocontainers/nextclade:1.10.1--h9ee0642_0' }" + 'https://depot.galaxyproject.org/singularity/nextclade:1.10.2--h9ee0642_0' : + 'quay.io/biocontainers/nextclade:1.10.2--h9ee0642_0' }" input: val dataset diff --git a/modules/nf-core/modules/nextclade/run/main.nf b/modules/nf-core/modules/nextclade/run/main.nf index 36e19aab..b3d101ce 100644 --- a/modules/nf-core/modules/nextclade/run/main.nf +++ b/modules/nf-core/modules/nextclade/run/main.nf @@ -2,10 +2,10 @@ process NEXTCLADE_RUN { tag "$meta.id" label 'process_low' - conda (params.enable_conda ? "bioconda::nextclade=1.10.1" : null) + conda (params.enable_conda ? "bioconda::nextclade=1.10.2" : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/nextclade:1.10.1--h9ee0642_0' : - 'quay.io/biocontainers/nextclade:1.10.1--h9ee0642_0' }" + 'https://depot.galaxyproject.org/singularity/nextclade:1.10.2--h9ee0642_0' : + 'quay.io/biocontainers/nextclade:1.10.2--h9ee0642_0' }" input: tuple val(meta), path(fasta) diff --git a/modules/nf-core/modules/pangolin/main.nf b/modules/nf-core/modules/pangolin/main.nf index 40d6d78e..6c8682e3 100644 --- a/modules/nf-core/modules/pangolin/main.nf +++ b/modules/nf-core/modules/pangolin/main.nf @@ -2,10 +2,10 @@ process PANGOLIN { tag "$meta.id" label 'process_medium' - conda (params.enable_conda ? 'bioconda::pangolin=3.1.17' : null) + conda (params.enable_conda ? 'bioconda::pangolin=3.1.19' : null) container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pangolin:3.1.17--pyhdfd78af_0' : - 'quay.io/biocontainers/pangolin:3.1.17--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pangolin:3.1.19--pyhdfd78af_0' : + 'quay.io/biocontainers/pangolin:3.1.19--pyhdfd78af_0' }" input: tuple val(meta), path(fasta) diff --git a/subworkflows/local/variants_long_table.nf b/subworkflows/local/variants_long_table.nf index 4d31b44c..938f3f3b 100644 --- a/subworkflows/local/variants_long_table.nf +++ b/subworkflows/local/variants_long_table.nf @@ -22,20 +22,18 @@ workflow VARIANTS_LONG_TABLE { [], [] ) - ch_query_table = BCFTOOLS_QUERY.out.vcf - ch_versions = ch_versions.mix(BCFTOOLS_QUERY.out.versions.first()) + ch_versions = ch_versions.mix(BCFTOOLS_QUERY.out.versions.first()) MAKE_VARIANTS_LONG_TABLE ( - ch_query_table.collect{it[1]}, + BCFTOOLS_QUERY.out.txt.collect{it[1]}, snpsift.collect{it[1]}.ifEmpty([]), pangolin.collect{it[1]}.ifEmpty([]) ) - ch_long_table = MAKE_VARIANTS_LONG_TABLE.out.csv - ch_versions = ch_versions.mix(MAKE_VARIANTS_LONG_TABLE.out.versions) + ch_versions = ch_versions.mix(MAKE_VARIANTS_LONG_TABLE.out.versions) emit: - query_table = ch_query_table // channel: [ val(meta), [ txt ] ] - long_table = ch_long_table // channel: [ val(meta), [ csv ] ] + query_table = BCFTOOLS_QUERY.out.txt // channel: [ val(meta), [ txt ] ] + long_table = MAKE_VARIANTS_LONG_TABLE.out.csv // channel: [ val(meta), [ csv ] ] versions = ch_versions // channel: [ versions.yml ] } From cbd844fe364520ee182ea5080beabe82db7b43b7 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 17:19:56 +0000 Subject: [PATCH 227/238] Add software dependency updates to CHANGELOG --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff333d65..8ee61c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 > **NB:** Parameter has been __added__ if just the new parameter information is present. > **NB:** Parameter has been __removed__ if new parameter information isn't present. +### Software dependencies + +Note, since the pipeline is now using Nextflow DSL2, each process will be run with its own [Biocontainer](https://biocontainers.pro/#/registry). This means that on occasion it is entirely possible for the pipeline to be using different versions of the same tool. However, the overall software dependency changes compared to the last release have been listed below for reference. + +| Dependency | Old version | New version | +|-------------------------------|-------------|-------------| +| `bcftools` | 1.11 | 1.14 | +| `blast` | 2.10.1 | 2.12.0 | +| `bowtie2` | 2.4.2 | 2.4.4 | +| `cutadapt` | 3.2 | 3.5 | +| `fastp` | 0.20.1 | 0.23.2 | +| `kraken2` | 2.1.1 | 2.1.2 | +| `minia` | 3.2.4 | 3.2.6 | +| `mosdepth` | 0.3.1 | 0.3.2 | +| `nanoplot` | 1.36.1 | 1.39.0 | +| `nextclade` | | 1.10.2 | +| `pangolin` | 3.1.7 | 3.1.19 | +| `picard` | 2.23.9 | 2.26.10 | +| `python` | 3.8.3 | 3.9.5 | +| `samtools` | 1.10 | 1.14 | +| `spades` | 3.15.2 | 3.15.3 | +| `tabix` | 0.2.6 | 1.11 | +| `vcflib` | | 1.0.2 | + +> **NB:** Dependency has been __updated__ if both old and new version information is present. +> **NB:** Dependency has been __added__ if just the new version information is present. +> **NB:** Dependency has been __removed__ if new version information isn't present. + ## [[2.2](https://github.com/nf-core/viralrecon/releases/tag/2.2)] - 2021-07-29 ### Enhancements & fixes From a0f5bef02680225e1b785aa01677e1c3cdaf581b Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Wed, 2 Feb 2022 17:51:21 +0000 Subject: [PATCH 228/238] Bump pipeline version to 2.3 --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index e15b78d1..19e1f2a0 100644 --- a/nextflow.config +++ b/nextflow.config @@ -234,7 +234,7 @@ manifest { description = 'Assembly and intrahost/low-frequency variant calling for viral samples' mainScript = 'main.nf' nextflowVersion = '!>=21.10.3' - version = '2.3dev' + version = '2.3' } // Load modules.config for DSL2 module specific options From c9fcd92c2ac79a4c5ba9f02790d33f46dafb3251 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 3 Feb 2022 11:58:24 +0000 Subject: [PATCH 229/238] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ee61c5d..ae655596 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### :warning: Major enhancements +* Please see [Major updates in v2.3](https://github.com/nf-core/viralrecon/issues/271) for a more verbose list of changes added in this version. * When using `--protocol amplicon`, in the previous release, iVar was used for both the variant calling and consensus sequence generation. The pipeline will now perform the variant calling and consensus sequence generation with iVar and BCFTools/BEDTools, respectively. * Bump minimum Nextflow version from `21.04.0` -> `21.10.3` From a0cbb1c3b8b8bdd7cfd5e6146b5353b9d6ea9fa7 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 3 Feb 2022 12:49:56 +0000 Subject: [PATCH 230/238] Tidy up script and make spacing, variable naming consistent --- bin/ivar_variants_to_vcf.py | 240 ++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 119 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 72ce0032..6574d858 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -10,51 +10,48 @@ def parse_args(args=None): - Description = "Convert iVar variants tsv file to vcf format." - Epilog = """Example usage: python ivar_variants_to_vcf.py """ + Description = "Convert iVar variants TSV file to VCF format." + Epilog = """Example usage: python ivar_variants_to_vcf.py """ parser = argparse.ArgumentParser(description=Description, epilog=Epilog) - parser.add_argument("FILE_IN", help="Input tsv file.") - parser.add_argument("FILE_OUT", help="Full path to output vcf file.") + parser.add_argument("file_in", help="Input iVar TSV file.") + parser.add_argument("file_out", help="Full path to output VCF file.") parser.add_argument( "-po", "--pass_only", - dest="PASS_ONLY", - help="Only output variants that PASS all filters.", + help="Only output variants that PASS filters.", action="store_true", ) parser.add_argument( "-af", - "--allele_freq_thresh", + "--allele_freq_threshold", type=float, - dest="ALLELE_FREQ_THRESH", default=0, - help="Only output variants where allele frequency greater than this number (default: 0).", + help="Only output variants where allele frequency is greater than this number (default: 0).", ) parser.add_argument( - "-nsb", + "-is", "--ignore_strand_bias", - dest="NOT_STRAND_BIAS", default=False, - help="Does not take into account strand bias, use this option when not using amplicons for sequencing", + help="Does not take strand bias into account, use this option when not using amplicon sequencing.", action="store_true" ) parser.add_argument( - "-nmc", + "-ic", "--ignore_merge_codons", - dest="NOT_MERGE_CODONS", - help="Only output variants without taking into accout if the positions are consecutive and belong to the same codon.", + help="Output variants without taking into account if consecutive positions belong to the same codon.", action="store_true" ) return parser.parse_args(args) + def check_consecutive(mylist): ''' Description: This function checks if a list of three or two numbers are consecutive and returns how many items are consecutive. input: - my_list - An integer list + my_list - A list of integers return: Number of items consecutive in the list - [False, 1, 2] ''' @@ -73,17 +70,18 @@ def check_consecutive(mylist): return False return False + def codon_position(seq1,seq2): ''' Description: - Function compares two codon nucleotide sequences (size 3) and retuns the position where it differs. + Function to compare two codon nucleotide sequences (size 3) and retuns the position where it differs. Input: seq1 - list size 3 [A,T,C,G] seq2 - list size 3 [A,T,C,G] Returns: Returns position where seq1 != seq2 ''' - if seq1 =="NA": + if seq1 == "NA": return False ind_diff = [i for i in range(len(seq1)) if seq1[i] != seq2[i]] @@ -93,6 +91,7 @@ def codon_position(seq1,seq2): else: return ind_diff[0] + def rename_vars(dict_lines,num_collapse): ''' Description: @@ -130,10 +129,11 @@ def rename_vars(dict_lines,num_collapse): SAMPLE = dict_lines["SAMPLE"][0] return CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE + def make_dir(path): ''' Description: - Create directory. + Create directory if it doesn't exist. Input: path - path where the directory will be created. Returns: @@ -146,22 +146,27 @@ def make_dir(path): if exception.errno != errno.EEXIST: raise -def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias=False,NotMergeCodon=False): + +def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequency=0, ignore_strand_bias=False, ignore_merge_codons=False): ''' Description: - Main function to to the parsing from tsv to vcf. + Main function to convert iVar variants TSV to VCF. Input: - FileIn - tsv file - FileOut - vcf filename - passOnly - whether to keep only pass filter variants [True, False] - minAF - min Alternate frequency to keep a variant. - NotStrandBias - whether to perform strand-bias filter [True, False] - NotMergeCodon - whether to perform codon merging in consecutive positions [True, False] - + file_in : iVar variants TSV file + file_out : VCF output file + pass_only : Only keep variants that PASS filter [True, False] + min_allele_freq : Minimum allele frequency to keep a variant [0] + ignore_strand_bias : Do not apply strand-bias filter [True, False] + ignore_merge_codons : Do not take into account consecutive positions belong to the same codon. Returns: None ''' - filename = os.path.splitext(FileIn)[0] + ## Create output directory + out_dir = os.path.dirname(file_out) + make_dir(out_dir) + + ## Define VCF header + filename = os.path.splitext(file_in)[0] header = ( "##fileformat=VCFv4.2\n" "##source=iVar\n" @@ -182,35 +187,38 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t" + filename + "\n" ) - varList = [] - varCountDict = {"SNP": 0, "INS": 0, "DEL": 0} - dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[],'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER': [],'INFO':[],'FORMAT':[],'SAMPLE':[]} - writeLine=False - OutDir = os.path.dirname(FileOut) - make_dir(OutDir) - fout = open(FileOut, "w") + ## Initialise variables + var_list = [] + var_count_dict = {"SNP": 0, "INS": 0, "DEL": 0} + dict_lines = {'CHROM':[], 'POS':[], 'ID':[], 'REF':[], 'ALT':[], 'REF_DP':[], 'REF_RV':[], 'ALT_DP':[], 'ALT_RV':[], 'QUAL':[], 'REF_CODON':[], 'ALT_CODON':[], 'FILTER': [], 'INFO':[], 'FORMAT':[], 'SAMPLE':[]} + write_line = False + fout = open(file_out, "w") fout.write(header) - - with open(FileIn) as f: - for line in f: + with open(file_in, 'r') as fin: + for line in fin: if not re.match("REGION", line): line = re.split("\t", line) + + ## Assign intial fields to variables CHROM = line[0] POS = line[1] ID = "." REF = line[2] ALT = line[3] + + ## REF/ALF depths REF_DP = int(line[4]) REF_RV = int(line[5]) REF_FW = REF_DP - REF_RV ALT_RV = int(line[8]) ALT_DP = int(line[7]) + ALT_FW = ALT_DP - ALT_RV ## Perform a fisher_exact test for strand bias detection - ALT_FW = ALT_DP - ALT_RV table = np.array([[REF_FW, REF_RV], [ALT_FW, ALT_RV]]) - oddsr, p = fisher_exact(table, alternative='greater') + oddsr, pvalue = fisher_exact(table, alternative='greater') + ## Determine variant type var_type = "SNP" if ALT[0] == "+": ALT = REF + ALT[1:] @@ -221,81 +229,67 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= var_type = "DEL" QUAL = "." - pass_test = line[13] - REF_CODON = line[15] - ALT_CODON = line[17] - if NotStrandBias: - if pass_test =="TRUE": + ## Determine FILTER field + INFO = f"DP={line[11]}" + pass_test = line[13] + if ignore_strand_bias: + if pass_test == "TRUE": FILTER = "PASS" else: FILTER = "FAIL" - INFO = "DP=" + line[11] else: - # If strand-bias test significative add SB in the FILTER field, else PASS - if p<0.05 and pass_test =="TRUE": + ## Add SB in the FILTER field if strand-bias p-value is significant + if pvalue < 0.05 and pass_test == "TRUE": FILTER = "SB" - elif p>0.05 and pass_test =="TRUE": + elif pvalue > 0.05 and pass_test == "TRUE": FILTER = "PASS" - elif p<=0.05 and pass_test == "FALSE": + elif pvalue <= 0.05 and pass_test == "FALSE": FILTER = "SB,other" else: FILTER = "FAIL" - INFO = "DP=" + line[11]+":SB_pvalue="+str(round(p,5)) + INFO += f":SB={str(round(pvalue, 5))}" FORMAT = "GT:REF_DP:REF_RV:REF_QUAL:ALT_DP:ALT_RV:ALT_QUAL:ALT_FREQ" - SAMPLE = ( - "1:" - + line[4] - + ":" - + line[5] - + ":" - + line[6] - + ":" - + line[7] - + ":" - + line[8] - + ":" - + line[9] - + ":" - + line[10] - ) - param_list = [CHROM,POS,ID,REF,ALT,REF_DP,REF_RV,ALT_DP,ALT_RV,QUAL,REF_CODON,ALT_CODON,FILTER,INFO,FORMAT,SAMPLE] - - if NotMergeCodon or var_type != "SNP": - writeLine = True - oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + SAMPLE = f'1:{":".join(line[4:11])}' + + REF_CODON = line[15] + ALT_CODON = line[17] + param_list = [CHROM, POS, ID, REF, ALT, REF_DP, REF_RV, ALT_DP, ALT_RV, QUAL, REF_CODON, ALT_CODON, FILTER, INFO, FORMAT, SAMPLE] + + if ignore_merge_codons or var_type != "SNP": + write_line = True + oline = (CHROM + "\t" + POS + "\t" + ID + "\t" + REF + "\t" + ALT + "\t" + QUAL + "\t" + FILTER + "\t" + INFO + "\t" + FORMAT + "\t" + SAMPLE + "\n") else: ## dict_lines contains all the informative fields for 3 positions in the vcf. # dict_lines has a maximum size of three. ## Always fill dict_lines until size 2. - if len(dict_lines["POS"]) ==0 or len(dict_lines["POS"]) == 1: + if len(dict_lines["POS"]) == 0 or len(dict_lines["POS"]) == 1: for i,j in enumerate(dict_lines): dict_lines.setdefault(j, []).append(param_list[i]) - writeLine=False + write_line=False # If queue has size 2, we include the third line - elif len(dict_lines["POS"]) == 2: + elif len(dict_lines["POS"]) == 2: for i,j in enumerate(dict_lines): dict_lines.setdefault(j, []).append(param_list[i]) # Are two positions in the dict consecutive? if check_consecutive(dict_lines["POS"]) == 2: ## If the first position is not on the third position of the codon they are in the same codon. if codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) != 2: - writeLine = True + write_line = True num_collapse = "2" - CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) - oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+"\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + CHROM, POS, ID, REF, ALT, QUAL, FILTER, INFO, FORMAT, SAMPLE = rename_vars(dict_lines, num_collapse) + oline = (CHROM + "\t" + POS + "\t" + ID + "\t" + REF + "\t" + ALT + "\t" + QUAL + "\t" + FILTER + "\t" + INFO + "\t" + FORMAT + "\t" + SAMPLE + "\n") ## We removed the first two items in dict_lines with have been just processed. for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines[list(dict_lines.keys())[i]].pop(0) else: - writeLine = True - oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") - + write_line = True + oline =(dict_lines["CHROM"][0] + "\t" + dict_lines["POS"][0] + "\t" + dict_lines["ID"][0] + "\t" + dict_lines["REF"][0] + "\t" + dict_lines["ALT"][0] + "\t" + dict_lines["QUAL"][0] + "\t" + dict_lines["FILTER"][0] + "\t" + dict_lines["INFO"][0] + "\t" + dict_lines["FORMAT"][0] + "\t" + dict_lines["SAMPLE"][0] + "\n") for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) @@ -303,83 +297,91 @@ def ivar_variants_to_vcf(FileIn, FileOut, passOnly=False, minAF=0,NotStrandBias= elif check_consecutive(dict_lines["POS"]) == 3: ## we check the first position in which codon position is to process it acordingly. # If first position is in the first codon position all three positions belong to the same codon. - if codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 0: - writeLine = True + if codon_position(dict_lines["REF_CODON"][0], dict_lines["ALT_CODON"][0]) == 0: + write_line = True num_collapse = 3 - CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) - oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + CHROM, POS, ID, REF, ALT, QUAL, FILTER, INFO, FORMAT, SAMPLE = rename_vars(dict_lines, num_collapse) + oline = (CHROM + "\t" + POS + "\t" + ID + "\t" + REF + "\t" + ALT + "\t" + QUAL + "\t" + FILTER + "\t" + INFO + "\t" + FORMAT + "\t" + SAMPLE + "\n") for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines[list(dict_lines.keys())[i]].pop(0) - # we empty the dict_lines - dict_lines = {'CHROM':[],'POS':[],'ID':[],'REF':[],'ALT':[],'REF_DP':[],'REF_RV':[],'ALT_DP':[], 'ALT_RV':[],'QUAL':[],'REF_CODON':[],'ALT_CODON':[],'FILTER':[],'INFO':[],'FORMAT':[],'SAMPLE':[]} + dict_lines = {'CHROM':[], 'POS':[], 'ID':[], 'REF':[], 'ALT':[], 'REF_DP':[], 'REF_RV':[], 'ALT_DP':[], 'ALT_RV':[], 'QUAL':[], 'REF_CODON':[], 'ALT_CODON':[], 'FILTER':[], 'INFO':[], 'FORMAT':[], 'SAMPLE':[]} # If first position is in the second codon position, we have the two first positions belonging to the same codon and the last one independent. - elif codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 1: - writeLine = True + elif codon_position(dict_lines["REF_CODON"][0], dict_lines["ALT_CODON"][0]) == 1: + write_line = True num_collapse = 2 - CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) - oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+ "\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + CHROM, POS, ID, REF, ALT, QUAL, FILTER, INFO, FORMAT, SAMPLE = rename_vars(dict_lines, num_collapse) + oline = (CHROM + "\t" + POS + "\t" + ID + "\t" + REF + "\t" + ALT + "\t" + QUAL + "\t" + FILTER + "\t" + INFO + "\t" + FORMAT + "\t" + SAMPLE + "\n") for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) dict_lines[list(dict_lines.keys())[i]].pop(0) ## Finally if we have the first position in the last codon position, we write first position and left the remaining two to be evaluated in the next iteration. - elif codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) == 2: - writeLine = True - oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t" + dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") - + elif codon_position(dict_lines["REF_CODON"][0], dict_lines["ALT_CODON"][0]) == 2: + write_line = True + oline =(dict_lines["CHROM"][0] + "\t" + dict_lines["POS"][0] + "\t" + dict_lines["ID"][0] + "\t" + dict_lines["REF"][0] + "\t" + dict_lines["ALT"][0] + "\t" + dict_lines["QUAL"][0] + "\t" + dict_lines["FILTER"][0] + "\t" + dict_lines["INFO"][0] + "\t" + dict_lines["FORMAT"][0] + "\t" + dict_lines["SAMPLE"][0] + "\n") for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) elif check_consecutive(dict_lines["POS"]) == False: - writeLine = True - oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") - + write_line = True + oline =(dict_lines["CHROM"][0] + "\t" + dict_lines["POS"][0] + "\t" + dict_lines["ID"][0] + "\t" + dict_lines["REF"][0] + "\t" + dict_lines["ALT"][0] + "\t" + dict_lines["QUAL"][0] + "\t" + dict_lines["FILTER"][0] + "\t" + dict_lines["INFO"][0] + "\t" + dict_lines["FORMAT"][0] + "\t" + dict_lines["SAMPLE"][0] + "\n") for i,j in enumerate(dict_lines): dict_lines[list(dict_lines.keys())[i]].pop(0) else: print("Something went terribly wrong!!" + str(len(dict_lines["POS"]))) - if passOnly and FILTER != "PASS": - writeLine = False - if float(line[10]) < minAF: - writeLine = False - if (CHROM, POS, REF, ALT) in varList: - writeLine = False + ## Determine whether to output variant + if pass_only and FILTER != "PASS": + write_line = False + if float(line[10]) < min_allele_frequency: + write_line = False + if (CHROM, POS, REF, ALT) in var_list: + write_line = False else: - varList.append((CHROM, POS, REF, ALT)) - if writeLine: - varCountDict[var_type] += 1 + var_list.append((CHROM, POS, REF, ALT)) + + ## Write to file + if write_line: + var_count_dict[var_type] += 1 fout.write(oline) ## Print variant counts to pass to MultiQC - varCountList = [(k, str(v)) for k, v in sorted(varCountDict.items())] - print("\t".join(["sample"] + [x[0] for x in varCountList])) - print("\t".join([filename] + [x[1] for x in varCountList])) + var_count_list = [(k, str(v)) for k, v in sorted(var_count_dict.items())] + print("\t".join(["sample"] + [x[0] for x in var_count_list])) + print("\t".join([filename] + [x[1] for x in var_count_list])) ## Handle last 3 lines. if len(dict_lines["POS"]) == 2: if check_consecutive(dict_lines["POS"]) == 2: if codon_position(dict_lines["REF_CODON"][0],dict_lines["ALT_CODON"][0]) != 2: - writeLine = True + write_line = True num_collapse = 2 - CHROM,POS,ID,REF,ALT,QUAL,FILTER,INFO,FORMAT,SAMPLE = rename_vars(dict_lines,num_collapse) - oline = (CHROM+ "\t"+ POS+ "\t"+ ID+ "\t"+ REF+ "\t"+ ALT+ "\t"+ QUAL+ "\t"+ FILTER+ "\t"+ INFO+"\t"+ FORMAT+ "\t"+ SAMPLE+ "\n" ) + CHROM, POS, ID, REF, ALT, QUAL, FILTER, INFO, FORMAT, SAMPLE = rename_vars(dict_lines, num_collapse) + oline = (CHROM + "\t" + POS + "\t" + ID + "\t" + REF + "\t" + ALT + "\t" + QUAL + "\t" + FILTER + "\t" + INFO + "\t" + FORMAT + "\t" + SAMPLE + "\n") fout.write(oline) else: - oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") - oline1 =(dict_lines["CHROM"][1] + "\t"+ dict_lines["POS"][1]+ "\t"+ dict_lines["ID"][1]+ "\t"+ dict_lines ["REF"][1]+ "\t"+ dict_lines["ALT"][1]+ "\t"+ dict_lines["QUAL"][1]+ "\t"+ dict_lines["FILTER"][1]+ "\t"+ dict_lines["INFO"][1]+ "\t"+ dict_lines["FORMAT"][1]+ "\t"+ dict_lines["SAMPLE"][1]+ "\n") + oline = (dict_lines["CHROM"][0] + "\t" + dict_lines["POS"][0] + "\t" + dict_lines["ID"][0] + "\t" + dict_lines["REF"][0] + "\t" + dict_lines["ALT"][0] + "\t" + dict_lines["QUAL"][0] + "\t" + dict_lines["FILTER"][0] + "\t" + dict_lines["INFO"][0] + "\t" + dict_lines["FORMAT"][0] + "\t" + dict_lines["SAMPLE"][0] + "\n") + oline1 = (dict_lines["CHROM"][1] + "\t" + dict_lines["POS"][1] + "\t" + dict_lines["ID"][1] + "\t" + dict_lines["REF"][1] + "\t" + dict_lines["ALT"][1] + "\t" + dict_lines["QUAL"][1] + "\t" + dict_lines["FILTER"][1] + "\t" + dict_lines["INFO"][1] + "\t" + dict_lines["FORMAT"][1] + "\t" + dict_lines["SAMPLE"][1] + "\n") fout.write(oline) fout.write(oline1) - elif len(dict_lines["POS"]) == 1: - oline =(dict_lines["CHROM"][0] + "\t"+ dict_lines["POS"][0]+ "\t"+ dict_lines["ID"][0]+ "\t"+ dict_lines ["REF"][0]+ "\t"+ dict_lines["ALT"][0]+ "\t"+ dict_lines["QUAL"][0]+ "\t"+ dict_lines["FILTER"][0]+ "\t"+ dict_lines["INFO"][0]+ "\t"+ dict_lines["FORMAT"][0]+ "\t"+ dict_lines["SAMPLE"][0]+ "\n") + elif len(dict_lines["POS"]) == 1: + oline =(dict_lines["CHROM"][0] + "\t" + dict_lines["POS"][0] + "\t" + dict_lines["ID"][0] + "\t" + dict_lines["REF"][0] + "\t" + dict_lines["ALT"][0] + "\t" + dict_lines["QUAL"][0] + "\t" + dict_lines["FILTER"][0] + "\t" + dict_lines["INFO"][0] + "\t" + dict_lines["FORMAT"][0] + "\t" + dict_lines["SAMPLE"][0] + "\n") fout.write(oline) + fout.close() + def main(args=None): args = parse_args(args) ivar_variants_to_vcf( - args.FILE_IN, args.FILE_OUT, args.PASS_ONLY, args.ALLELE_FREQ_THRESH, args.NOT_STRAND_BIAS, args.NOT_MERGE_CODONS + args.file_in, + args.file_out, + args.pass_only, + args.allele_freq_threshold, + args.ignore_strand_bias, + args.ignore_merge_codons, ) + if __name__ == "__main__": sys.exit(main()) From 6539629ad90b201b7202972ffa23614489159c74 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 3 Feb 2022 13:06:46 +0000 Subject: [PATCH 231/238] Fix small issues noticed during code review --- bin/ivar_variants_to_vcf.py | 57 +++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 6574d858..e75ff3c3 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -162,30 +162,43 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc None ''' ## Create output directory + filename = os.path.splitext(file_in)[0] out_dir = os.path.dirname(file_out) make_dir(out_dir) ## Define VCF header - filename = os.path.splitext(file_in)[0] - header = ( - "##fileformat=VCFv4.2\n" - "##source=iVar\n" - '##INFO=\n' - '##FILTER=\n' - '##FILTER= 0.05">\n' - '##FILTER=\n' - '##FORMAT=\n' - '##FORMAT=\n' - '##FORMAT=\n' - '##FORMAT=\n' - '##FORMAT=\n' - '##FORMAT=\n' - '##FORMAT=\n' - '##FORMAT=\n' - ) - header += ( - "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t" + filename + "\n" - ) + header_source = [ + "##fileformat=VCFv4.2", + "##source=iVar" + ] + header_info = [ + '##INFO=' + ] + header_filter = [ + '##FILTER=', + '##FILTER= 0.05">' + ] + header_format = [ + '##FORMAT=', + '##FORMAT=', + '##FORMAT=', + '##FORMAT=', + '##FORMAT=', + '##FORMAT=', + '##FORMAT=', + '##FORMAT=', + ] + header_cols = [ + f"#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t{filename}" + ] + if not ignore_strand_bias: + header_info += [ + '##INFO=' + ] + header_filter += [ + '##FILTER=' + ] + header = header_source + header_info + header_filter + header_format + header_cols ## Initialise variables var_list = [] @@ -193,7 +206,7 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc dict_lines = {'CHROM':[], 'POS':[], 'ID':[], 'REF':[], 'ALT':[], 'REF_DP':[], 'REF_RV':[], 'ALT_DP':[], 'ALT_RV':[], 'QUAL':[], 'REF_CODON':[], 'ALT_CODON':[], 'FILTER': [], 'INFO':[], 'FORMAT':[], 'SAMPLE':[]} write_line = False fout = open(file_out, "w") - fout.write(header) + fout.write('\n'.join(header) + '\n') with open(file_in, 'r') as fin: for line in fin: if not re.match("REGION", line): @@ -245,7 +258,7 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc elif pvalue > 0.05 and pass_test == "TRUE": FILTER = "PASS" elif pvalue <= 0.05 and pass_test == "FALSE": - FILTER = "SB,other" + FILTER = "FAIL" else: FILTER = "FAIL" INFO += f":SB={str(round(pvalue, 5))}" From 4f76c2f0ae218817ebe22558dd9c07b53a13c715 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 3 Feb 2022 13:11:11 +0000 Subject: [PATCH 232/238] Point to issue with major updates --- CHANGELOG.md | 2 +- README.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae655596..bde3bd6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### :warning: Major enhancements -* Please see [Major updates in v2.3](https://github.com/nf-core/viralrecon/issues/271) for a more verbose list of changes added in this version. +* Please see [Major updates in v2.3](https://github.com/nf-core/viralrecon/issues/271) for a more detailed list of changes added in this version. * When using `--protocol amplicon`, in the previous release, iVar was used for both the variant calling and consensus sequence generation. The pipeline will now perform the variant calling and consensus sequence generation with iVar and BCFTools/BEDTools, respectively. * Bump minimum Nextflow version from `21.04.0` -> `21.10.3` diff --git a/README.md b/README.md index 57dbce79..f915e315 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ The pipeline has numerous options to allow you to run only specific aspects of t The SRA download functionality has been removed from the pipeline (`>=2.1`) and ported to an independent workflow called [nf-core/fetchngs](https://nf-co.re/fetchngs). You can provide `--nf_core_pipeline viralrecon` when running nf-core/fetchngs to download and auto-create a samplesheet containing publicly available samples that can be accepted directly by the Illumina processing mode of nf-core/viralrecon. +A number of improvements were made to the pipeline recently, mainly with regard to the variant calling. Please see [Major updates in v2.3](https://github.com/nf-core/viralrecon/issues/271) for a more detailed description. + ### Illumina 1. Merge re-sequenced FastQ files ([`cat`](http://www.linfo.org/cat.html)) From 94fa8ff2cad0d1aea4556f2dab5471874e79c4b2 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 3 Feb 2022 13:42:44 +0000 Subject: [PATCH 233/238] PR review fixes --- bin/ivar_variants_to_vcf.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index e75ff3c3..1322e39e 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -189,11 +189,11 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc '##FORMAT=', ] header_cols = [ - f"#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t{filename}" + f"#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\t{filename}" ] if not ignore_strand_bias: header_info += [ - '##INFO=' + '##INFO=' ] header_filter += [ '##FILTER=' @@ -257,11 +257,11 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc FILTER = "SB" elif pvalue > 0.05 and pass_test == "TRUE": FILTER = "PASS" - elif pvalue <= 0.05 and pass_test == "FALSE": - FILTER = "FAIL" + elif pvalue <= 0.05 and pass_test == "FALSE": + FILTER = "FAIL,SB" else: FILTER = "FAIL" - INFO += f":SB={str(round(pvalue, 5))}" + INFO += f":SB_PV={str(round(pvalue, 5))}" FORMAT = "GT:REF_DP:REF_RV:REF_QUAL:ALT_DP:ALT_RV:ALT_QUAL:ALT_FREQ" SAMPLE = f'1:{":".join(line[4:11])}' From 2ac8afcf73a533aeedcfe25484aa4991c326383b Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 3 Feb 2022 20:45:23 +0000 Subject: [PATCH 234/238] Add -f option to bgzip in vcflib/vcfuniq module --- conf/modules_nanopore.config | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/modules_nanopore.config b/conf/modules_nanopore.config index b2754800..60a9e6b4 100644 --- a/conf/modules_nanopore.config +++ b/conf/modules_nanopore.config @@ -62,6 +62,7 @@ process { } withName: 'VCFLIB_VCFUNIQ' { + ext.args = '-f' ext.prefix = { "${meta.id}.pass.unique" } publishDir = [ path: { "${params.outdir}/${params.artic_minion_caller}" }, From 2c6ee2691ae881b615bff1ed80a48c128b3b758f Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 3 Feb 2022 22:05:17 +0000 Subject: [PATCH 235/238] Filter out samples with 0 variants for iVar --- lib/WorkflowCommons.groovy | 11 +++++++++++ subworkflows/local/variants_ivar.nf | 11 +++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/WorkflowCommons.groovy b/lib/WorkflowCommons.groovy index 395cb1c2..5672a1cf 100755 --- a/lib/WorkflowCommons.groovy +++ b/lib/WorkflowCommons.groovy @@ -91,6 +91,17 @@ class WorkflowCommons { return vals } + // + // Function that returns the number of lines in a file + // + public static Integer getNumLinesInFile(input_file) { + def num_lines = 0 + input_file.eachLine { line -> + num_lines ++ + } + return num_lines + } + // // Function to generate an error if contigs in BED file do not match those in reference genome // diff --git a/subworkflows/local/variants_ivar.nf b/subworkflows/local/variants_ivar.nf index c4bf2eeb..27e92ff8 100644 --- a/subworkflows/local/variants_ivar.nf +++ b/subworkflows/local/variants_ivar.nf @@ -33,11 +33,18 @@ workflow VARIANTS_IVAR { ) ch_versions = ch_versions.mix(IVAR_VARIANTS.out.versions.first()) + // Filter out samples with 0 variants + IVAR_VARIANTS + .out + .tsv + .filter { meta, tsv -> WorkflowCommons.getNumLinesInFile(tsv) > 1 } + .set { ch_ivar_tsv } + // // Convert original iVar output to VCF, zip and index // IVAR_VARIANTS_TO_VCF ( - IVAR_VARIANTS.out.tsv, + ch_ivar_tsv, ivar_multiqc_header ) ch_versions = ch_versions.mix(IVAR_VARIANTS_TO_VCF.out.versions.first()) @@ -64,7 +71,7 @@ workflow VARIANTS_IVAR { ch_versions = ch_versions.mix(VARIANTS_QC.out.versions) emit: - tsv = IVAR_VARIANTS.out.tsv // channel: [ val(meta), [ tsv ] ] + tsv = ch_ivar_tsv // channel: [ val(meta), [ tsv ] ] vcf_orig = IVAR_VARIANTS_TO_VCF.out.vcf // channel: [ val(meta), [ vcf ] ] log_out = IVAR_VARIANTS_TO_VCF.out.log // channel: [ val(meta), [ log ] ] From 0581f040489d24d0edc29ee2f8ea41649b1e6225 Mon Sep 17 00:00:00 2001 From: Harshil Patel Date: Thu, 3 Feb 2022 22:30:16 +0000 Subject: [PATCH 236/238] Filter out samples with 0 variants for BCFTools --- subworkflows/local/variants_bcftools.nf | 29 +++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/subworkflows/local/variants_bcftools.nf b/subworkflows/local/variants_bcftools.nf index c250e04f..662c860a 100644 --- a/subworkflows/local/variants_bcftools.nf +++ b/subworkflows/local/variants_bcftools.nf @@ -31,11 +31,32 @@ workflow VARIANTS_BCFTOOLS { ) ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) + // Filter out samples with 0 variants + BCFTOOLS_MPILEUP + .out + .vcf + .join(BCFTOOLS_MPILEUP.out.tbi) + .join(BCFTOOLS_MPILEUP.out.stats) + .filter { meta, vcf, tbi, stats -> WorkflowCommons.getNumVariantsFromBCFToolsStats(stats) > 0 } + .set { ch_vcf_tbi_stats } + + ch_vcf_tbi_stats + .map { meta, vcf, tbi, stats -> [ meta, vcf ] } + .set { ch_vcf } + + ch_vcf_tbi_stats + .map { meta, vcf, tbi, stats -> [ meta, tbi ] } + .set { ch_tbi } + + ch_vcf_tbi_stats + .map { meta, vcf, tbi, stats -> [ meta, stats ] } + .set { ch_stats } + // // Split multi-allelic positions // BCFTOOLS_NORM ( - BCFTOOLS_MPILEUP.out.vcf, + ch_vcf, fasta ) ch_versions = ch_versions.mix(BCFTOOLS_NORM.out.versions.first()) @@ -62,9 +83,9 @@ workflow VARIANTS_BCFTOOLS { ch_versions = ch_versions.mix(VARIANTS_QC.out.versions) emit: - vcf_orig = BCFTOOLS_MPILEUP.out.vcf // channel: [ val(meta), [ vcf ] ] - tbi_orig = BCFTOOLS_MPILEUP.out.tbi // channel: [ val(meta), [ tbi ] ] - stats_orig = BCFTOOLS_MPILEUP.out.stats // channel: [ val(meta), [ txt ] ] + vcf_orig = ch_vcf // channel: [ val(meta), [ vcf ] ] + tbi_orig = ch_tbi // channel: [ val(meta), [ tbi ] ] + stats_orig = ch_stats // channel: [ val(meta), [ txt ] ] vcf = BCFTOOLS_NORM.out.vcf // channel: [ val(meta), [ vcf ] ] tbi = VCF_TABIX_STATS.out.tbi // channel: [ val(meta), [ tbi ] ] From 605e810e493537c9cf2bfa70cce11e68b0b23c3d Mon Sep 17 00:00:00 2001 From: Michael L Heuer Date: Fri, 4 Feb 2022 09:38:55 -0600 Subject: [PATCH 237/238] Use ft,sb filters for Fisher's exact test and strand bias --- bin/ivar_variants_to_vcf.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index 1322e39e..b738c63b 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -175,8 +175,8 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc '##INFO=' ] header_filter = [ - '##FILTER=', - '##FILTER= 0.05">' + '##FILTER=', + '##FILTER= 0.05">' ] header_format = [ '##FORMAT=', @@ -196,7 +196,7 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc '##INFO=' ] header_filter += [ - '##FILTER=' + '##FILTER=' ] header = header_source + header_info + header_filter + header_format + header_cols @@ -250,17 +250,17 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc if pass_test == "TRUE": FILTER = "PASS" else: - FILTER = "FAIL" + FILTER = "ft" else: ## Add SB in the FILTER field if strand-bias p-value is significant if pvalue < 0.05 and pass_test == "TRUE": - FILTER = "SB" + FILTER = "sb" elif pvalue > 0.05 and pass_test == "TRUE": FILTER = "PASS" elif pvalue <= 0.05 and pass_test == "FALSE": - FILTER = "FAIL,SB" + FILTER = "ft;sb" else: - FILTER = "FAIL" + FILTER = "ft" INFO += f":SB_PV={str(round(pvalue, 5))}" FORMAT = "GT:REF_DP:REF_RV:REF_QUAL:ALT_DP:ALT_RV:ALT_QUAL:ALT_FREQ" From 7675ecbc24bcc5eda5d8cdd98e58ca7816309e59 Mon Sep 17 00:00:00 2001 From: Michael L Heuer Date: Fri, 4 Feb 2022 10:45:19 -0600 Subject: [PATCH 238/238] Fix type on ALT_QUAL format header line --- bin/ivar_variants_to_vcf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/ivar_variants_to_vcf.py b/bin/ivar_variants_to_vcf.py index b738c63b..d3f126ea 100755 --- a/bin/ivar_variants_to_vcf.py +++ b/bin/ivar_variants_to_vcf.py @@ -185,7 +185,7 @@ def ivar_variants_to_vcf(file_in, file_out, pass_only=False, min_allele_frequenc '##FORMAT=', '##FORMAT=', '##FORMAT=', - '##FORMAT=', + '##FORMAT=', '##FORMAT=', ] header_cols = [