From f58f9582056f756e1eb3360cc3db79e0610f0b29 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Tue, 26 Mar 2024 09:29:55 +0100 Subject: [PATCH 01/65] Add validation step --- conf/modules.config | 6 +- conf/test_sim.config | 10 +- conf/test_validate.config | 34 + docs/development.md | 4 +- docs/images/metro/Concordance.png | Bin 112900 -> 82053 bytes docs/images/metro/MetroMap.xml | 2921 +---------------- docs/images/metro/txt2image.md | 3 +- main.nf | 22 +- modules.json | 15 + modules/local/addcolumns/main.nf | 32 + modules/local/concatenate/main.nf | 25 + .../glimpse/concordance/environment.yml | 7 + modules/nf-core/glimpse/concordance/main.nf | 65 + modules/nf-core/glimpse/concordance/meta.yml | 85 + .../glimpse/concordance/tests/main.nf.test | 86 + .../concordance/tests/main.nf.test.snap | 99 + .../glimpse/concordance/tests/tags.yml | 2 + .../glimpse2/concordance/environment.yml | 7 + modules/nf-core/glimpse2/concordance/main.nf | 79 + modules/nf-core/glimpse2/concordance/meta.yml | 110 + modules/nf-core/gunzip/environment.yml | 7 + modules/nf-core/gunzip/main.nf | 48 + modules/nf-core/gunzip/meta.yml | 39 + modules/nf-core/gunzip/tests/main.nf.test | 36 + .../nf-core/gunzip/tests/main.nf.test.snap | 31 + modules/nf-core/gunzip/tests/tags.yml | 2 + subworkflows/local/compute_gl/main.nf | 10 +- subworkflows/local/get_panel/main.nf | 2 +- .../utils_nfcore_phaseimpute_pipeline/main.nf | 24 +- .../local/vcf_concordance_glimpse/main.nf | 50 + .../tests/main.nf.test | 97 + .../tests/main.nf.test.snap | 35 + .../tests/nextflow.config | 3 + .../vcf_concordance_glimpse/tests/tags.yml | 2 + tests/config/nf-test.config | 1 + tests/csv/sample_validate.csv | 4 + workflows/phaseimpute/main.nf | 81 +- 37 files changed, 1112 insertions(+), 2972 deletions(-) create mode 100644 conf/test_validate.config create mode 100644 modules/local/addcolumns/main.nf create mode 100644 modules/local/concatenate/main.nf create mode 100644 modules/nf-core/glimpse/concordance/environment.yml create mode 100644 modules/nf-core/glimpse/concordance/main.nf create mode 100644 modules/nf-core/glimpse/concordance/meta.yml create mode 100644 modules/nf-core/glimpse/concordance/tests/main.nf.test create mode 100644 modules/nf-core/glimpse/concordance/tests/main.nf.test.snap create mode 100644 modules/nf-core/glimpse/concordance/tests/tags.yml create mode 100644 modules/nf-core/glimpse2/concordance/environment.yml create mode 100644 modules/nf-core/glimpse2/concordance/main.nf create mode 100644 modules/nf-core/glimpse2/concordance/meta.yml create mode 100644 modules/nf-core/gunzip/environment.yml create mode 100644 modules/nf-core/gunzip/main.nf create mode 100644 modules/nf-core/gunzip/meta.yml create mode 100644 modules/nf-core/gunzip/tests/main.nf.test create mode 100644 modules/nf-core/gunzip/tests/main.nf.test.snap create mode 100644 modules/nf-core/gunzip/tests/tags.yml create mode 100644 subworkflows/local/vcf_concordance_glimpse/main.nf create mode 100644 subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test create mode 100644 subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap create mode 100644 subworkflows/local/vcf_concordance_glimpse/tests/nextflow.config create mode 100644 subworkflows/local/vcf_concordance_glimpse/tests/tags.yml create mode 100644 tests/csv/sample_validate.csv diff --git a/conf/modules.config b/conf/modules.config index a04bf589..05300610 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -143,10 +143,10 @@ process { withName: GLIMPSE_LIGATE { ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}" } } - withName: GLIMPSE_CONCORDANCE { - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region}" } + withName: GLIMPSE2_CONCORDANCE { + ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}" } } withName: ADD_COLUMNS { - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region}_SNP" } + ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}_SNP" } } } diff --git a/conf/test_sim.config b/conf/test_sim.config index 8c2bd1f5..5e06fd2e 100644 --- a/conf/test_sim.config +++ b/conf/test_sim.config @@ -11,7 +11,7 @@ */ params { - config_profile_name = 'Test simulation mode' + config_profile_name = 'Test simulation / imputation / validation mode' config_profile_description = 'Minimal test dataset to check pipeline function' // Limit resources so that this can run on GitHub Actions @@ -24,8 +24,12 @@ params { input_region = "${projectDir}/tests/csv/region.csv" depth = 1 + // Genome references + fasta = "https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/reference_genome/21_22/hs38DH.chr21_22.fa" + panel = "${projectDir}/tests/csv/panel.csv" + phased = true map = "${projectDir}/tests/csv/map.csv" - fasta = "https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/reference_genome/21_22/hs38DH.chr21_22.fa" - step = "simulate" + step = "all" + tools = "glimpse1" } diff --git a/conf/test_validate.config b/conf/test_validate.config new file mode 100644 index 00000000..7d7e7057 --- /dev/null +++ b/conf/test_validate.config @@ -0,0 +1,34 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running minimal tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a fast and simple pipeline test. + + Use as follows: + nextflow run nf-core/phaseimpute -profile test_validate, --outdir + +---------------------------------------------------------------------------------------- +*/ + +params { + config_profile_name = 'Test validation mode' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data + input = "${projectDir}/tests/csv/sample_sim.csv" + input_region = "${projectDir}/tests/csv/region.csv" + depth = 1 + + // Genome references + fasta = "https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/reference_genome/21_22/hs38DH.chr21_22.fa" + panel = "${projectDir}/tests/csv/panel.csv" + phased = true + map = "${projectDir}/tests/csv/map.csv" + + step = "validate" +} diff --git a/docs/development.md b/docs/development.md index 8126332d..33051d56 100644 --- a/docs/development.md +++ b/docs/development.md @@ -21,6 +21,7 @@ ```bash nextflow run main.nf -profile singularity,test --outdir results -resume +nextflow run main.nf -profile singularity,test_sim --outdir results -resume ``` ## Problematic @@ -39,8 +40,7 @@ All channel need to be identified by a meta map as follow: - M : map used - T : tool used - G : reference genome used (is it needed ?) -- D : depth - +- S : simulation (depth or genotype array) ## Open questions How to use different schema ? diff --git a/docs/images/metro/Concordance.png b/docs/images/metro/Concordance.png index ad99865889f7725a94b7c2b658d45c7cb43da0f5..87f4e68c0770ded9373392bad783eeb4d57641a3 100644 GIT binary patch literal 82053 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

ykFl;8K|L*N>q4yLT_#E*ZPjuSdrYK6 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

B@6IOLCL>A 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 Vo6bSh++zGkO-1`I_Kx-Q{{bX(V{`xj literal 112900 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

0t*k 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 diff --git a/docs/images/metro/MetroMap.xml b/docs/images/metro/MetroMap.xml index bc371e9f..7731642f 100644 --- a/docs/images/metro/MetroMap.xml +++ b/docs/images/metro/MetroMap.xml @@ -1,2920 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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 \ No newline at end of file diff --git a/docs/images/metro/txt2image.md b/docs/images/metro/txt2image.md index abfe7cd7..33aaba3d 100644 --- a/docs/images/metro/txt2image.md +++ b/docs/images/metro/txt2image.md @@ -14,8 +14,9 @@ To use drawio drawio --version drawio docs/images/metro/MetroMap.xml --export --format png --page-index 0 --output docs/images/metro/MetroMap.png --scale 3 drawio docs/images/metro/MetroMap.xml --export --format png --layers 0 --page-index 1 --output docs/images/metro/PostProcessing.png --scale 3 -drawio docs/images/metro/MetroMap.xml --export --format png --layers 1 --page-index 1 --output docs/images/metro/Concordance.png --scale 3 +drawio docs/images/metro/MetroMap.xml --export --format png --layers 1 --page-index 1 --output docs/images/metro/Concordance2.png --scale 3 drawio docs/images/metro/MetroMap.xml --export --format png --layers 2 --page-index 1 --output docs/images/metro/Simulate.png --scale 3 drawio docs/images/metro/MetroMap.xml --export --format png --layers 3 --page-index 1 --output docs/images/metro/Phase.png --scale 3 drawio docs/images/metro/MetroMap.xml --export --format png --layers 4 --page-index 1 --output docs/images/metro/PreProcessing.png --scale 3 +drawio docs/images/metro/MetroMap.xml --export --format png --layers 5 --page-index 1 --output docs/images/metro/Concordance.png --scale 3 ``` diff --git a/main.nf b/main.nf index 597a6f46..65b6ae1a 100644 --- a/main.nf +++ b/main.nf @@ -42,11 +42,31 @@ workflow NFCORE_PHASEIMPUTE { ch_versions // channel: versions of software used main: + + // + // Initialise input channels + // + + input_impute = Channel.empty() + input_simulate = Channel.empty() + input_validate = Channel.empty() + + if (params.step == "impute") { + input_impute = ch_input + } else if (params.step == "simulate" || params.step == "all") { + input_simulate = ch_input + } else if (params.step == "validate") { + input_validate = ch_input + } + + // // WORKFLOW: Run pipeline // PHASEIMPUTE ( - ch_input, + input_impute, + input_simulate, + input_validate, ch_fasta, ch_panel, ch_regions, diff --git a/modules.json b/modules.json index 0a99d93d..87f67435 100644 --- a/modules.json +++ b/modules.json @@ -53,6 +53,11 @@ "git_sha": "7e56daae390ff896b292ddc70823447683a79936", "installed_by": ["vcf_impute_glimpse"] }, + "glimpse/concordance": { + "branch": "master", + "git_sha": "7e56daae390ff896b292ddc70823447683a79936", + "installed_by": ["modules"] + }, "glimpse/ligate": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", @@ -68,6 +73,11 @@ "git_sha": "14ba46490cae3c78ed8e8f48d2c0f8f3be1e7c03", "installed_by": ["multiple_impute_glimpse2"] }, + "glimpse2/concordance": { + "branch": "master", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "installed_by": ["modules"] + }, "glimpse2/ligate": { "branch": "master", "git_sha": "ee7fee68281944b002bd27a8ff3f19200b4d3fad", @@ -83,6 +93,11 @@ "git_sha": "fa12139827a18b324bd63fce654818586a8e9cc7", "installed_by": ["multiple_impute_glimpse2"] }, + "gunzip": { + "branch": "master", + "git_sha": "3a5fef109d113b4997c9822198664ca5f2716208", + "installed_by": ["modules"] + }, "multiqc": { "branch": "master", "git_sha": "b7ebe95761cd389603f9cc0e0dc384c0f663815a", diff --git a/modules/local/addcolumns/main.nf b/modules/local/addcolumns/main.nf new file mode 100644 index 00000000..54da94a6 --- /dev/null +++ b/modules/local/addcolumns/main.nf @@ -0,0 +1,32 @@ +process ADD_COLUMNS { + label 'process_single' + + input: + tuple val(meta), path(input) + + output: + tuple val(meta), path('*.txt'), emit: txt + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + awk '(NR>=2) && (NR<=10)' $input | \\ + awk 'NR==1{\$(NF+1)="ID"} NR>1{\$(NF+1)="${meta.id}"}1' | \\ + awk 'NR==1{\$(NF+1)="Region"} NR>1{\$(NF+1)="${meta.region}"}1' | \\ + awk 'NR==1{\$(NF+1)="Depth"} NR>1{\$(NF+1)="${meta.depth}"}1' | \\ + awk 'NR==1{\$(NF+1)="GPArray"} NR>1{\$(NF+1)="${meta.gparray}"}1' | \\ + awk 'NR==1{\$(NF+1)="Tools"} NR>1{\$(NF+1)="${meta.tools}"}1' | \\ + awk 'NR==1{\$(NF+1)="Panel"} NR>1{\$(NF+1)="${meta.panel}"}1' > \\ + ${prefix}.txt + + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$(awk --version | head -1 | grep -o -E '([0-9]+.){1,2}[0-9]') + END_VERSIONS + """ +} diff --git a/modules/local/concatenate/main.nf b/modules/local/concatenate/main.nf new file mode 100644 index 00000000..6616a4ae --- /dev/null +++ b/modules/local/concatenate/main.nf @@ -0,0 +1,25 @@ +process CONCATENATE { + label 'process_single' + + input: + tuple val(meta), path(input) + + output: + tuple val(meta), path('*.txt'), emit: txt + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + awk '(NR == 1) || (FNR > 1)' $input > ${prefix}.txt + + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$(awk --version | head -1 | grep -o -E '([0-9]+.){1,2}[0-9]') + END_VERSIONS + """ +} diff --git a/modules/nf-core/glimpse/concordance/environment.yml b/modules/nf-core/glimpse/concordance/environment.yml new file mode 100644 index 00000000..739ab78d --- /dev/null +++ b/modules/nf-core/glimpse/concordance/environment.yml @@ -0,0 +1,7 @@ +name: glimpse_concordance +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::glimpse-bio=1.1.1 diff --git a/modules/nf-core/glimpse/concordance/main.nf b/modules/nf-core/glimpse/concordance/main.nf new file mode 100644 index 00000000..48785dd3 --- /dev/null +++ b/modules/nf-core/glimpse/concordance/main.nf @@ -0,0 +1,65 @@ +process GLIMPSE_CONCORDANCE { + tag "$meta.id" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/glimpse-bio:1.1.1--hce55b13_1': + 'biocontainers/glimpse-bio:1.1.1--hce55b13_1' }" + + input: + tuple val(meta), path(estimate), path(estimate_index), path(freq), path(freq_index), path(truth), path(truth_index), val(region) + val(min_prob) + val(min_dp) + val(bins) + + output: + tuple val(meta), path("*.error.cal.txt.gz") , emit: errors_cal + tuple val(meta), path("*.error.grp.txt.gz") , emit: errors_grp + tuple val(meta), path("*.error.spl.txt.gz") , emit: errors_spl + tuple val(meta), path("*.rsquare.grp.txt.gz"), emit: rsquare_grp + tuple val(meta), path("*.rsquare.spl.txt.gz"), emit: rsquare_spl + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def min_prob_cmd = min_prob ? "--minPROB ${min_prob}" : "--minPROB 0.9999" + def min_dp_cmd = min_dp ? "--minDP ${min_dp}" : "--minDP 8" + def bins_cmd = bins ? "--bins ${bins}" : "--bins 0.00000 0.00100 0.00200 0.00500 0.01000 0.05000 0.10000 0.20000 0.50000" + """ + echo $region $freq $truth $estimate > input.txt + GLIMPSE_concordance \\ + $args \\ + --input input.txt \\ + --thread $task.cpus \\ + --output ${prefix} \\ + $min_prob_cmd \\ + $min_dp_cmd \\ + $bins_cmd + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + glimpse: "\$(GLIMPSE_concordance --help | sed -nr '/Version/p' | grep -o -E '([0-9]+.){1,2}[0-9]')" + END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + def args = task.ext.args ?: "" + """ + touch ${prefix}.error.cal.txt.gz + touch ${prefix}.error.grp.txt.gz + touch ${prefix}.error.spl.txt.gz + touch ${prefix}.rsquare.grp.txt.gz + touch ${prefix}.rsquare.spl.txt.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + glimpse: "\$(GLIMPSE_concordance --help | sed -nr '/Version/p' | grep -o -E '([0-9]+.){1,2}[0-9]')" + END_VERSIONS + """ +} diff --git a/modules/nf-core/glimpse/concordance/meta.yml b/modules/nf-core/glimpse/concordance/meta.yml new file mode 100644 index 00000000..2b2d7195 --- /dev/null +++ b/modules/nf-core/glimpse/concordance/meta.yml @@ -0,0 +1,85 @@ +name: "glimpse_concordance" +description: Compute the r2 correlation between imputed dosages (in MAF bins) and highly-confident genotype calls from the high-coverage dataset. +keywords: + - concordance + - low-coverage + - glimpse + - imputation +tools: + - "glimpse": + description: "GLIMPSE is a phasing and imputation method for large-scale low-coverage sequencing studies." + homepage: "https://odelaneau.github.io/GLIMPSE" + documentation: "https://odelaneau.github.io/GLIMPSE/commands.html" + tool_dev_url: "https://github.com/odelaneau/GLIMPSE" + doi: "10.1038/s41588-020-00756-0" + licence: ["MIT"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - region: + type: string + description: Target region used for imputation, including left and right buffers (e.g. chr20:1000000-2000000). + pattern: "chrXX:leftBufferPosition-rightBufferPosition" + - freq: + type: file + description: File containing allele frequencies at each site. + pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" + - truth: + type: file + description: Validation dataset called at the same positions as the imputed file. + pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" + - estimate: + type: file + description: Imputed data. + pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" + - min_prob: + type: float + description: Minimum posterior probability P(G|R) in validation data + - min_dp: + type: integer + description: | + Minimum coverage in validation data. + If FORMAT/DP is missing and --minDP > 0, the program exits with an error. + - bins: + type: string + description: | + Allele frequency bins used for rsquared computations. + By default they should as MAF bins [0-0.5], while + they should take the full range [0-1] if --use-ref-alt is used. +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" + - errors_cal: + type: file + description: Calibration correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.errors.cal.txt.gz" + - errors_grp: + type: file + description: Groups correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.errors.grp.txt.gz" + - errors_spl: + type: file + description: Samples correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.errors.spl.txt.gz" + - rsquared_grp: + type: file + description: Groups r-squared correlation between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.rsquare.grp.txt.gz" + - rsquared_spl: + type: file + description: Samples r-squared correlation between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.rsquare.spl.txt.gz" +authors: + - "@louislenezet" +maintainers: + - "@louislenezet" diff --git a/modules/nf-core/glimpse/concordance/tests/main.nf.test b/modules/nf-core/glimpse/concordance/tests/main.nf.test new file mode 100644 index 00000000..7e850535 --- /dev/null +++ b/modules/nf-core/glimpse/concordance/tests/main.nf.test @@ -0,0 +1,86 @@ +nextflow_process { + + name "Test Process GLIMPSE_CONCORDANCE" + script "../main.nf" + process "GLIMPSE_CONCORDANCE" + + tag "modules" + tag "modules_nfcore" + tag "glimpse" + tag "glimpse/concordance" + tag "glimpse/phase" + tag "bcftools/index" + + test("test_glimpse_concordance") { + setup { + run("GLIMPSE_PHASE") { + script "../../phase/main.nf" + process { + """ + ch_sample = Channel.of('NA12878 2').collectFile(name: 'sampleinfos.txt') + region = Channel.fromList([ + ["chr21:16600000-16750000","chr21:16650000-16700000"] + ]) + input_vcf = Channel.of([ + [ id:'input'], // meta map + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.1x.vcf.gz", checkIfExists: true), + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.1x.vcf.gz.csi", checkIfExists: true) + ]) + ref_panel = Channel.of([ + file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.bcf", checkIfExists: true), + file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.bcf.csi", checkIfExists: true) + ]) + ch_map = Channel.of([ + file(params.modules_testdata_base_path + "delete_me/glimpse/chr21.b38.gmap.gz", checkIfExists: true), + ]) + + input[0] = input_vcf + | combine(ch_sample) + | combine(region) + | combine(ref_panel) + | combine(ch_map) + """ + } + } + run("BCFTOOLS_INDEX") { + script "../../../bcftools/index/main.nf" + process { + """ + input[0] = GLIMPSE_PHASE.out.phased_variants + """ + } + } + } + when { + process { + """ + allele_freq = Channel.fromList([ + file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.sites.vcf.gz",checkIfExists:true), + file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.sites.vcf.gz.csi",checkIfExists:true) + ]).collect() + truth = Channel.fromList([ + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.bcf",checkIfExists:true), + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.bcf.csi",checkIfExists:true) + ]).collect() + estimate = GLIMPSE_PHASE.out.phased_variants + | join (BCFTOOLS_INDEX.out.csi) + input[0] = estimate + | combine (allele_freq) + | combine (truth) + | combine (["chr21"]) + input[1] = [] + input[2] = [] + input[3] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } +} diff --git a/modules/nf-core/glimpse/concordance/tests/main.nf.test.snap b/modules/nf-core/glimpse/concordance/tests/main.nf.test.snap new file mode 100644 index 00000000..c0838446 --- /dev/null +++ b/modules/nf-core/glimpse/concordance/tests/main.nf.test.snap @@ -0,0 +1,99 @@ +{ + "test_glimpse_concordance": { + "content": [ + { + "0": [ + [ + { + "id": "input" + }, + "input.error.cal.txt.gz:md5,15c6a120d9fd3ac8c0ff6a6aedc76571" + ] + ], + "1": [ + [ + { + "id": "input" + }, + "input.error.grp.txt.gz:md5,532bec52c03f16dcd6cc6d2b7c26673b" + ] + ], + "2": [ + [ + { + "id": "input" + }, + "input.error.spl.txt.gz:md5,35cb463e8db41e2180f21941ab0324e0" + ] + ], + "3": [ + [ + { + "id": "input" + }, + "input.rsquare.grp.txt.gz:md5,15bc7bf7980fd63e0f09bd267e548b57" + ] + ], + "4": [ + [ + { + "id": "input" + }, + "input.rsquare.spl.txt.gz:md5,55659f466775d828ee1ba723464bb460" + ] + ], + "5": [ + "versions.yml:md5,f79c864118d03a4afa93082c46c0d608" + ], + "errors_cal": [ + [ + { + "id": "input" + }, + "input.error.cal.txt.gz:md5,15c6a120d9fd3ac8c0ff6a6aedc76571" + ] + ], + "errors_grp": [ + [ + { + "id": "input" + }, + "input.error.grp.txt.gz:md5,532bec52c03f16dcd6cc6d2b7c26673b" + ] + ], + "errors_spl": [ + [ + { + "id": "input" + }, + "input.error.spl.txt.gz:md5,35cb463e8db41e2180f21941ab0324e0" + ] + ], + "rsquare_grp": [ + [ + { + "id": "input" + }, + "input.rsquare.grp.txt.gz:md5,15bc7bf7980fd63e0f09bd267e548b57" + ] + ], + "rsquare_spl": [ + [ + { + "id": "input" + }, + "input.rsquare.spl.txt.gz:md5,55659f466775d828ee1ba723464bb460" + ] + ], + "versions": [ + "versions.yml:md5,f79c864118d03a4afa93082c46c0d608" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-03-18T23:37:00.537398654" + } +} \ No newline at end of file diff --git a/modules/nf-core/glimpse/concordance/tests/tags.yml b/modules/nf-core/glimpse/concordance/tests/tags.yml new file mode 100644 index 00000000..e636bb70 --- /dev/null +++ b/modules/nf-core/glimpse/concordance/tests/tags.yml @@ -0,0 +1,2 @@ +glimpse/concordance: + - modules/nf-core/glimpse/concordance/** diff --git a/modules/nf-core/glimpse2/concordance/environment.yml b/modules/nf-core/glimpse2/concordance/environment.yml new file mode 100644 index 00000000..c3ad98fb --- /dev/null +++ b/modules/nf-core/glimpse2/concordance/environment.yml @@ -0,0 +1,7 @@ +name: glimpse2_concordance +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::glimpse-bio=2.0.0 diff --git a/modules/nf-core/glimpse2/concordance/main.nf b/modules/nf-core/glimpse2/concordance/main.nf new file mode 100644 index 00000000..4fcb587b --- /dev/null +++ b/modules/nf-core/glimpse2/concordance/main.nf @@ -0,0 +1,79 @@ +process GLIMPSE2_CONCORDANCE { + tag "$meta.id" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/glimpse-bio:2.0.0--hf340a29_0': + 'biocontainers/glimpse-bio:2.0.0--hf340a29_0' }" + + input: + tuple val(meta), path(estimate), path(estimate_index), path(truth), path(truth_index), path(freq), path(freq_index), path(samples), val(region) + tuple val(meta2), path(groups), val(bins), val(ac_bins), val(allele_counts) + val(min_val_gl) + val(min_val_dp) + + output: + tuple val(meta), path("*.error.cal.txt.gz") , emit: errors_cal + tuple val(meta), path("*.error.grp.txt.gz") , emit: errors_grp + tuple val(meta), path("*.error.spl.txt.gz") , emit: errors_spl + tuple val(meta), path("*.rsquare.grp.txt.gz"), emit: rsquare_grp + tuple val(meta), path("*.rsquare.spl.txt.gz"), emit: rsquare_spl + tuple val(meta), path("*_r2_sites.txt.gz") , emit: rsquare_per_site, optional: true + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def samples_cmd = samples ? "--samples ${samples}" : "" + def groups_cmd = groups ? "--groups ${groups}" : "" + def bins_cmd = bins ? "--bins ${bins}" : "" + def ac_bins_cmd = ac_bins ? "--ac-bins ${ac_bins}" : "" + def ale_ct_cmd = allele_counts ? "--allele-counts ${allele_counts}" : "" + def region_str = region instanceof List ? region.join('\\n') : region + + if (((groups ? 1:0) + (bins ? 1:0) + (ac_bins ? 1:0) + (allele_counts ? 1:0)) != 1) error "One and only one argument should be selected between groups, bins, ac_bins, allele_counts" + + """ + printf '$region_str' > regions.txt + sed 's/\$/ $freq $truth $estimate/' regions.txt > input.txt + GLIMPSE2_concordance \\ + $args \\ + $samples_cmd \\ + $groups_cmd \\ + $bins_cmd \\ + $ac_bins_cmd \\ + $ale_ct_cmd \\ + --min-val-gl $min_val_gl \\ + --min-val-dp $min_val_dp \\ + --input input.txt \\ + --thread $task.cpus \\ + --output ${prefix} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + glimpse2: "\$(GLIMPSE2_concordance --help | sed -nr '/Version/p' | grep -o -E '([0-9]+.){1,2}[0-9]' | head -1)" + END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + def args = task.ext.args ?: "" + def rsquare_per_site_cmd = args.contains("--out-r2-per-site") ? "touch ${prefix}_r2_sites.txt.gz" : "" + """ + touch ${prefix}.error.cal.txt.gz + touch ${prefix}.error.grp.txt.gz + touch ${prefix}.error.spl.txt.gz + touch ${prefix}.rsquare.grp.txt.gz + touch ${prefix}.rsquare.spl.txt.gz + ${rsquare_per_site_cmd} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + glimpse: "\$(GLIMPSE_concordance --help | sed -nr '/Version/p' | grep -o -E '([0-9]+.){1,2}[0-9]')" + END_VERSIONS + """ +} diff --git a/modules/nf-core/glimpse2/concordance/meta.yml b/modules/nf-core/glimpse2/concordance/meta.yml new file mode 100644 index 00000000..7c82c350 --- /dev/null +++ b/modules/nf-core/glimpse2/concordance/meta.yml @@ -0,0 +1,110 @@ +name: "glimpse2_concordance" +description: Program to compute the genotyping error rate at the sample or marker level. +keywords: + - concordance + - low-coverage + - glimpse + - imputation +tools: + - "glimpse2": + description: "GLIMPSE2 is a phasing and imputation method for large-scale low-coverage sequencing studies." + homepage: "https://odelaneau.github.io/GLIMPSE" + documentation: "https://odelaneau.github.io/GLIMPSE/commands.html" + tool_dev_url: "https://github.com/odelaneau/GLIMPSE" + doi: "10.1038/s41588-020-00756-0" + licence: "['MIT']" +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - region: + type: string + description: Target region used for imputation, including left and right buffers (e.g. chr20:1000000-2000000). Can also be a list of such regions. + pattern: "chrXX:leftBufferPosition-rightBufferPosition" + - freq: + type: file + description: File containing allele frequencies at each site. + pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" + - truth: + type: file + description: Validation dataset called at the same positions as the imputed file. + pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" + - estimate: + type: file + description: Imputed dataset file obtain after phasing. + pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" + - samples: + type: file + description: List of samples to process, one sample ID per line. + pattern: "*.{txt,tsv}" + - groups: + type: file + description: Alternative to frequency bins, group bins are user defined, provided in a file. + pattern: "*.{txt,tsv}" + - bins: + type: string + description: | + Allele frequency bins used for rsquared computations. + By default they should as MAF bins [0-0.5], while + they should take the full range [0-1] if --use-ref-alt is used. + pattern: "0 0.01 0.05 ... 0.5" + - ac_bins: + type: string + description: User-defined allele count bins used for rsquared computations. + pattern: "1 2 5 10 20 ... 100000" + - allele_counts: + type: string + description: | + Default allele count bins used for rsquared computations. + AN field must be defined in the frequency file. + - min_val_gl: + type: float + description: | + Minimum genotype likelihood probability P(G|R) in validation data. + Set to zero to have no filter of if using –gt-validation + - min_val_dp: + type: integer + description: | + Minimum coverage in validation data. + If FORMAT/DP is missing and –min_val_dp > 0, the program exits with an error. + Set to zero to have no filter of if using –gt-validation +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" + - errors_cal: + type: file + description: Calibration correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.errors.cal.txt.gz" + - errors_grp: + type: file + description: Groups correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.errors.grp.txt.gz" + - errors_spl: + type: file + description: Samples correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.errors.spl.txt.gz" + - rsquare_grp: + type: file + description: Groups r-squared correlation between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.rsquare.grp.txt.gz" + - rsquare_spl: + type: file + description: Samples r-squared correlation between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "*.rsquare.spl.txt.gz" + - rsquare_per_site: + type: file + description: Variant r-squared correlation between imputed dosages (in MAF bins) and highly-confident genotype. + pattern: "_r2_sites.txt.gz" +authors: + - "@louislenezet" +maintainers: + - "@louislenezet" diff --git a/modules/nf-core/gunzip/environment.yml b/modules/nf-core/gunzip/environment.yml new file mode 100644 index 00000000..25910b34 --- /dev/null +++ b/modules/nf-core/gunzip/environment.yml @@ -0,0 +1,7 @@ +name: gunzip +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - conda-forge::sed=4.7 diff --git a/modules/nf-core/gunzip/main.nf b/modules/nf-core/gunzip/main.nf new file mode 100644 index 00000000..468a6f28 --- /dev/null +++ b/modules/nf-core/gunzip/main.nf @@ -0,0 +1,48 @@ +process GUNZIP { + tag "$archive" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : + 'nf-core/ubuntu:20.04' }" + + input: + tuple val(meta), path(archive) + + output: + tuple val(meta), path("$gunzip"), emit: gunzip + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + gunzip = archive.toString() - '.gz' + """ + # Not calling gunzip itself because it creates files + # with the original group ownership rather than the + # default one for that user / the work directory + gzip \\ + -cd \\ + $args \\ + $archive \\ + > $gunzip + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + gunzip: \$(echo \$(gunzip --version 2>&1) | sed 's/^.*(gzip) //; s/ Copyright.*\$//') + END_VERSIONS + """ + + stub: + gunzip = archive.toString() - '.gz' + """ + touch $gunzip + 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/gunzip/meta.yml b/modules/nf-core/gunzip/meta.yml new file mode 100644 index 00000000..231034f2 --- /dev/null +++ b/modules/nf-core/gunzip/meta.yml @@ -0,0 +1,39 @@ +name: gunzip +description: Compresses and decompresses files. +keywords: + - gunzip + - compression + - decompression +tools: + - gunzip: + description: | + gzip is a file format and a software application used for file compression and decompression. + documentation: https://www.gnu.org/software/gzip/manual/gzip.html + licence: ["GPL-3.0-or-later"] +input: + - meta: + type: map + description: | + Optional groovy Map containing meta information + e.g. [ id:'test', single_end:false ] + - archive: + type: file + description: File to be compressed/uncompressed + pattern: "*.*" +output: + - gunzip: + type: file + description: Compressed/uncompressed file + pattern: "*.*" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@joseespinosa" + - "@drpatelh" + - "@jfy133" +maintainers: + - "@joseespinosa" + - "@drpatelh" + - "@jfy133" diff --git a/modules/nf-core/gunzip/tests/main.nf.test b/modules/nf-core/gunzip/tests/main.nf.test new file mode 100644 index 00000000..6406008e --- /dev/null +++ b/modules/nf-core/gunzip/tests/main.nf.test @@ -0,0 +1,36 @@ +nextflow_process { + + name "Test Process GUNZIP" + script "../main.nf" + process "GUNZIP" + tag "gunzip" + tag "modules_nfcore" + tag "modules" + + test("Should run without failures") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = Channel.of([ + [], + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) + ] + ) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/nf-core/gunzip/tests/main.nf.test.snap b/modules/nf-core/gunzip/tests/main.nf.test.snap new file mode 100644 index 00000000..720fd9ff --- /dev/null +++ b/modules/nf-core/gunzip/tests/main.nf.test.snap @@ -0,0 +1,31 @@ +{ + "Should run without failures": { + "content": [ + { + "0": [ + [ + [ + + ], + "test_1.fastq:md5,4161df271f9bfcd25d5845a1e220dbec" + ] + ], + "1": [ + "versions.yml:md5,54376d32aca20e937a4ec26dac228e84" + ], + "gunzip": [ + [ + [ + + ], + "test_1.fastq:md5,4161df271f9bfcd25d5845a1e220dbec" + ] + ], + "versions": [ + "versions.yml:md5,54376d32aca20e937a4ec26dac228e84" + ] + } + ], + "timestamp": "2023-10-17T15:35:37.690477896" + } +} \ No newline at end of file diff --git a/modules/nf-core/gunzip/tests/tags.yml b/modules/nf-core/gunzip/tests/tags.yml new file mode 100644 index 00000000..fd3f6915 --- /dev/null +++ b/modules/nf-core/gunzip/tests/tags.yml @@ -0,0 +1,2 @@ +gunzip: + - modules/nf-core/gunzip/** diff --git a/subworkflows/local/compute_gl/main.nf b/subworkflows/local/compute_gl/main.nf index 3e552f64..ba266f74 100644 --- a/subworkflows/local/compute_gl/main.nf +++ b/subworkflows/local/compute_gl/main.nf @@ -5,7 +5,7 @@ include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/in workflow COMPUTE_GL { take: - ch_input // channel: [ [id, ref], bam, bai ] + ch_input // channel: [ [id, chr, region], bam, bai ] ch_target // channel: [ [panel, chr], sites, tsv] ch_fasta // channel: [ [ref], fasta, fai] @@ -14,10 +14,10 @@ workflow COMPUTE_GL { ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() - ch_mpileup = ch_input - .combine(ch_target) - .map{metaI, bam, bai, metaPC, sites, tsv -> - [metaI + metaPC, bam, sites, tsv]} + ch_mpileup = ch_input.map{metaICR, bam, bai -> [metaICR.subMap("chr"), metaICR, bam, bai]} + .combine(ch_target.map{metaPC, sites, tsv -> [metaPC.subMap("chr"), metaPC, sites, tsv]}, by:0) + .map{metaC, metaICR, bam, bai, metaPC, sites, tsv -> + [metaICR + metaPC, bam, sites, tsv]} BCFTOOLS_MPILEUP( ch_mpileup, diff --git a/subworkflows/local/get_panel/main.nf b/subworkflows/local/get_panel/main.nf index 67904059..d4a03a33 100644 --- a/subworkflows/local/get_panel/main.nf +++ b/subworkflows/local/get_panel/main.nf @@ -85,6 +85,6 @@ workflow GET_PANEL { } emit: - panel = ch_panel // channel: [ [panel], norm, n_index, sites, s_index, tsv, t_index, phased, p_index] + panel = ch_panel // channel: [ [panel, chr], norm, n_index, sites, s_index, tsv, t_index, phased, p_index] versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf index 4ce4b840..e82c4a67 100644 --- a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf @@ -175,14 +175,24 @@ workflow PIPELINE_INITIALISATION { ch_depth = Channel.of([[],[]]) } + // + // Create genotype array channel + // + if (params.genotype) { + ch_genotype = Channel.of([[gparray: params.genotype], params.genotype]) + } else { + ch_genotype = Channel.of([[],[]]) + } + + emit: - input = ch_input // [ [meta], bam, bai ] - fasta = ch_ref_gen // [ [genome], fasta, fai ] - panel = ch_panel // [ [panel, chr], vcf, index ] - depth = ch_depth // [ [depth], depth ] - regions = ch_regions // [ [chr, region], region ] - map = ch_map // [ [map], map ] - versions = ch_versions + input = ch_input // [ [meta], bam, bai ] + fasta = ch_ref_gen // [ [genome], fasta, fai ] + panel = ch_panel // [ [panel, chr], vcf, index ] + depth = ch_depth // [ [depth], depth ] + regions = ch_regions // [ [chr, region], region ] + map = ch_map // [ [map], map ] + versions = ch_versions } /* diff --git a/subworkflows/local/vcf_concordance_glimpse/main.nf b/subworkflows/local/vcf_concordance_glimpse/main.nf new file mode 100644 index 00000000..37ef9f30 --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse/main.nf @@ -0,0 +1,50 @@ +include { GLIMPSE2_CONCORDANCE } from '../../../modules/nf-core/glimpse2/concordance' +include { CONCATENATE } from '../../../modules/local/concatenate' +include { ADD_COLUMNS } from '../../../modules/local/addcolumns' +include { GUNZIP } from '../../../modules/nf-core/gunzip' + +workflow VCF_CONCORDANCE_GLIMPSE { + + take: + ch_vcf_emul // VCF file with imputed genotypes [[id, chr, region, panel, simulate, tools], vcf, csi] + ch_vcf_truth // VCF file with truth genotypes [[id, chr, region], vcf, csi] + ch_vcf_freq // VCF file with panel frequencies [[panel, chr], vcf, csi] + + main: + + ch_versions = Channel.empty() + + ch_concordance = ch_vcf_emul + .map{ + metaICRPST, vcf, csi -> + [metaICRPST.subMap(["id", "chr", "region", "panel"]), metaICRPST, vcf, csi] + } + .combine(ch_vcf_truth, by:0) + .map{metaICRP, metaIPCRTS, emul, e_csi, truth, t_csi -> + [metaICRP.subMap(["chr"]), metaIPCRTS, emul, e_csi, truth, t_csi] + } + .combine(ch_vcf_freq.map{metaCRP, vcf, csi -> + [metaCRP.subMap(["chr"]), metaCRP, vcf, csi]}, + by:0) + .map{metaC, metaIPCRTS, emul, e_csi, truth, t_csi, metaCRP, freq, f_csi -> + [metaIPCRTS, emul, e_csi, truth, t_csi, freq, f_csi, [], metaIPCRTS.region] + } + + GLIMPSE2_CONCORDANCE ( + ch_concordance, + [[], [], "0 0.01 0.05 0.1 0.2 0.5", [], []], + 0.9, 5 + ) + GUNZIP(GLIMPSE2_CONCORDANCE.out.errors_grp) + ADD_COLUMNS(GUNZIP.out.gunzip) + + CONCATENATE(ADD_COLUMNS.out.txt + .map{meta, txt -> [["id":"TestQuality"], txt]} + .groupTuple() + ) + + + emit: + stats = CONCATENATE.out.txt // [ meta, txt ] + versions = ch_versions // channel: [ versions.yml ] +} diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test b/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test new file mode 100644 index 00000000..9d00a486 --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test @@ -0,0 +1,97 @@ +nextflow_workflow { + + name "Test Subworkflow VCF_CONCORDANCE_GLIMPSE" + script "../main.nf" + config "./nextflow.config" + + workflow "VCF_CONCORDANCE_GLIMPSE" + + tag "subworkflows" + tag "subworkflows_local" + tag "subworkflows/vcf_concordance_glimpse" + tag "vcf_concordance_glimpse" + + tag "bcftools" + tag "bcftools/index" + tag "glimpse" + tag "glimpse/phase" + tag "glimpse/concordance" + + test("vcf_concordance_glimpse") { + setup { + run("GLIMPSE_PHASE") { + script "../../../../modules/nf-core/glimpse/phase/main.nf" + process { + """ + ch_sample = Channel.of('NA12878 2', 'NA12878_2 2').collectFile(name: 'sampleinfos.txt', newLine: true) + region = Channel.fromList([ + ["chr21:16600000-16750000","chr21:16650000-16700000"] + ]) + input_vcf = Channel.fromList([ + [[ id:'NA12878', chr:'21', region:'chr21:16650000-16700000', panel: '1000GP', depth:'1', tools: 'Glimpse'], // meta map + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.1x.vcf.gz", checkIfExists: true), + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.1x.vcf.gz.csi", checkIfExists: true)], + [[ id:'NA12878_2', chr:'21', region:'chr21:16650000-16700000', panel: '1000GP', depth:'0.5', tools: 'Glimpse2'], // meta map + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.1x.vcf.gz", checkIfExists: true), + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.1x.vcf.gz.csi", checkIfExists: true)] + ]) + ref_panel = Channel.of([ + file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.bcf", checkIfExists: true), + file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.bcf.csi", checkIfExists: true) + ]) + ch_map = Channel.of([ + file(params.modules_testdata_base_path + "delete_me/glimpse/chr21.b38.gmap.gz", checkIfExists: true), + ]) + + input[0] = input_vcf + | combine(ch_sample) + | combine(region) + | combine(ref_panel) + | combine(ch_map) + """ + } + } + run("BCFTOOLS_INDEX") { + script "../../../../modules/nf-core/bcftools/index/main.nf" + process { + """ + input[0] = GLIMPSE_PHASE.out.phased_variants + """ + } + } + } + when { + workflow { + """ + + allele_freq = Channel.of([ + [panel:'1000GP', chr:'21'], // meta map + file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.sites.vcf.gz",checkIfExists:true), + file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.sites.vcf.gz.csi",checkIfExists:true) + ]) + truth = Channel.fromList([ + [[id:'NA12878', chr:'21', region:'chr21:16650000-16700000'], // meta map + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.bcf",checkIfExists:true), + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.bcf.csi",checkIfExists:true)], + [[id:'NA12878_2', chr:'21', region:'chr21:16650000-16700000'], // meta map + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.bcf",checkIfExists:true), + file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.bcf.csi",checkIfExists:true)] + ]) + estimate = GLIMPSE_PHASE.out.phased_variants + | join (BCFTOOLS_INDEX.out.csi) + input[0] = estimate + input[1] = truth + input[2] = allele_freq + """ + } + } + + then { + assertAll( + { assert workflow.success }, + { assert snapshot(workflow.out).match() } + ) + } + + } +} diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap b/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap new file mode 100644 index 00000000..608ceca5 --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap @@ -0,0 +1,35 @@ +{ + "vcf_concordance_glimpse": { + "content": [ + { + "0": [ + [ + { + "id": "TestQuality" + }, + "TestQuality.txt:md5,910b294df62dbe64e8f16379428d93ad" + ] + ], + "1": [ + + ], + "stats": [ + [ + { + "id": "TestQuality" + }, + "TestQuality.txt:md5,910b294df62dbe64e8f16379428d93ad" + ] + ], + "versions": [ + + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-03-25T12:44:54.967846019" + } +} \ No newline at end of file diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/nextflow.config b/subworkflows/local/vcf_concordance_glimpse/tests/nextflow.config new file mode 100644 index 00000000..227aed3d --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse/tests/nextflow.config @@ -0,0 +1,3 @@ +params { + max_memory = '7.GB' +} diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/tags.yml b/subworkflows/local/vcf_concordance_glimpse/tests/tags.yml new file mode 100644 index 00000000..56e39343 --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse/tests/tags.yml @@ -0,0 +1,2 @@ +subworkflows/vcf_concordance_glimpse: + - subworkflows/local/vcf_concordance_glimpse/** diff --git a/tests/config/nf-test.config b/tests/config/nf-test.config index 775c5ad7..417172e2 100644 --- a/tests/config/nf-test.config +++ b/tests/config/nf-test.config @@ -2,6 +2,7 @@ params { publish_dir_mode = "copy" singularity_pull_docker_container = false test_data_base = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules' + modules_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/' } process { diff --git a/tests/csv/sample_validate.csv b/tests/csv/sample_validate.csv new file mode 100644 index 00000000..ad25d415 --- /dev/null +++ b/tests/csv/sample_validate.csv @@ -0,0 +1,4 @@ +sample,vcf,csi +NA12878,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.bcf.csi +NA19401,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.bcf.csi +NA20359,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.bcf.csi diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 1cde520b..8a3f218a 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -17,11 +17,12 @@ include { methodsDescriptionText } from '../../subworkflows/local/utils_nfc // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // +include { VCF_IMPUTE_GLIMPSE } from '../../subworkflows/nf-core/vcf_impute_glimpse' include { BAM_REGION } from '../../subworkflows/local/bam_region' include { BAM_DOWNSAMPLE } from '../../subworkflows/local/bam_downsample' include { COMPUTE_GL as GL_TRUTH } from '../../subworkflows/local/compute_gl' include { COMPUTE_GL as GL_INPUT } from '../../subworkflows/local/compute_gl' -include { VCF_IMPUTE_GLIMPSE } from '../../subworkflows/nf-core/vcf_impute_glimpse' +include { VCF_CONCORDANCE_GLIMPSE } from '../../subworkflows/local/vcf_concordance_glimpse' include { VCF_CHR_RENAME } from '../../subworkflows/local/vcf_chr_rename' include { GET_PANEL } from '../../subworkflows/local/get_panel' @@ -34,27 +35,31 @@ include { GET_PANEL } from '../../subworkflows/local/get_panel workflow PHASEIMPUTE { take: - ch_input // channel: input file [ [id, chr], bam, bai ] - ch_fasta // channel: fasta file [ [genome], fasta, fai ] - ch_panel // channel: panel file [ [id, chr], chr, vcf, index ] - ch_region // channel: region to use [ [chr, region], region] - ch_depth // channel: depth to downsample to [ [depth], depth ] - ch_map // channel: genetic map [ [chr], map] - ch_versions // channel: versions of software used + ch_input_impute // channel: input file [ [id, chr], bam, bai ] + ch_input_sim // channel: input file [ [id, chr], bam, bai ] + ch_input_validate // channel: input file [ [id, chr], bam, bai ] + ch_fasta // channel: fasta file [ [genome], fasta, fai ] + ch_panel // channel: panel file [ [id, chr], chr, vcf, index ] + ch_region // channel: region to use [ [chr, region], region] + ch_depth // channel: depth select [ [depth], depth ] + ch_map // channel: genetic map [ [chr], map] + ch_versions // channel: versions of software used main: ch_multiqc_files = Channel.empty() + ch_validate_truth = Channel.empty() + // // Simulate data if asked // - if (params.step == 'simulate') { + if (params.step == 'simulate' || params.step == 'all') { // Output channel of simulate process ch_sim_output = Channel.empty() // Split the bam into the region specified - BAM_REGION(ch_input, ch_region, ch_fasta) + BAM_REGION(ch_input_sim, ch_region, ch_fasta) // Initialize channel to impute ch_bam_to_impute = Channel.empty() @@ -68,7 +73,8 @@ workflow PHASEIMPUTE { ) ch_versions = ch_versions.mix(BAM_DOWNSAMPLE.out.versions.first()) - ch_input = ch_input.mix(BAM_DOWNSAMPLE.out.bam_emul) + ch_input_impute = ch_input_impute.mix(BAM_DOWNSAMPLE.out.bam_emul) + ch_validate_truth = ch_validate_truth.mix(BAM_REGION.out.bam_region) } if (params.genotype) { @@ -79,7 +85,7 @@ workflow PHASEIMPUTE { // // Prepare panel // - if (params.step == 'impute' || params.step == 'panel_prep') { + if (params.step == 'impute' || params.step == 'panel_prep' || params.step == 'all') { // Remove if necessary "chr" if (params.panel_chr_rename != null) { print("Need to rename the chromosome prefix of the panel") @@ -92,26 +98,30 @@ workflow PHASEIMPUTE { GET_PANEL(ch_panel, ch_fasta) } - ch_versions = ch_versions.mix(GET_PANEL.out.versions.first()) + ch_panel_sites_tsv = GET_PANEL.out.panel + .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index + -> [metaPC, sites, tsv] + } + ch_panel_sites = GET_PANEL.out.panel + .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index + -> [metaPC, sites, s_index] + } + ch_panel_phased = GET_PANEL.out.panel + .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index + -> [metaPC, phased, p_index] + } - // Output channel of input process - ch_impute_output = Channel.empty() + ch_versions = ch_versions.mix(GET_PANEL.out.versions.first()) - if (params.step == 'impute') { + if (params.step == 'impute' || params.step == 'all') { + // Output channel of input process + ch_impute_output = Channel.empty() if (params.tools.contains("glimpse1")) { println "Impute with Glimpse1" - ch_panel_sites_tsv = GET_PANEL.out.panel - .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index - -> [metaPC, sites, tsv] - } - ch_panel_phased = GET_PANEL.out.panel - .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index - -> [metaPC, phased, p_index] - } // Glimpse1 subworkflow GL_INPUT( // Compute GL for input data once per panel - ch_input, + ch_input_impute, ch_panel_sites_tsv, ch_fasta ) @@ -134,7 +144,8 @@ workflow PHASEIMPUTE { VCF_IMPUTE_GLIMPSE(impute_input) output_glimpse1 = VCF_IMPUTE_GLIMPSE.out.merged_variants - .map{ metaIPCR, vcf -> [metaIPCR + [tool: "Glimpse1"], vcf] } + .combine(VCF_IMPUTE_GLIMPSE.out.merged_variants_index, by: 0) + .map{ metaIPCR, vcf, csi -> [metaIPCR + [tools: "Glimpse1"], vcf, csi] } ch_impute_output = ch_impute_output.mix(output_glimpse1) } if (params.tools.contains("glimpse2")) { @@ -147,13 +158,25 @@ workflow PHASEIMPUTE { error "Quilt not yet implemented" // Quilt subworkflow } - + ch_input_validate = ch_input_validate.mix(ch_impute_output) } } - if (params.step == 'validate') { - error "validate step not yet implemented" + if (params.step == 'validate' || params.step == 'all') { + // Compute truth genotypes likelihoods + GL_TRUTH( + ch_validate_truth, + ch_panel_sites_tsv, + ch_fasta + ) + + // Compute concordance analysis + VCF_CONCORDANCE_GLIMPSE( + ch_input_validate, + GL_TRUTH.out.vcf, + ch_panel_sites + ) } if (params.step == 'refine') { From b11bd7c45f481655b675431b3b81eaf76f8f153e Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Tue, 26 Mar 2024 09:37:19 +0100 Subject: [PATCH 02/65] Fix Eclint --- docs/images/metro/MetroMap.xml | 2 +- modules/local/concatenate/main.nf | 50 +++++++++++++++---------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/images/metro/MetroMap.xml b/docs/images/metro/MetroMap.xml index 7731642f..7f105c75 100644 --- a/docs/images/metro/MetroMap.xml +++ b/docs/images/metro/MetroMap.xml @@ -1 +1 @@ -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 \ No newline at end of file +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 diff --git a/modules/local/concatenate/main.nf b/modules/local/concatenate/main.nf index 6616a4ae..77a179c6 100644 --- a/modules/local/concatenate/main.nf +++ b/modules/local/concatenate/main.nf @@ -1,25 +1,25 @@ -process CONCATENATE { - label 'process_single' - - input: - tuple val(meta), path(input) - - output: - tuple val(meta), path('*.txt'), emit: txt - path "versions.yml", emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def prefix = task.ext.prefix ?: "${meta.id}" - """ - awk '(NR == 1) || (FNR > 1)' $input > ${prefix}.txt - - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - awk: \$(awk --version | head -1 | grep -o -E '([0-9]+.){1,2}[0-9]') - END_VERSIONS - """ -} +process CONCATENATE { + label 'process_single' + + input: + tuple val(meta), path(input) + + output: + tuple val(meta), path('*.txt'), emit: txt + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + awk '(NR == 1) || (FNR > 1)' $input > ${prefix}.txt + + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$(awk --version | head -1 | grep -o -E '([0-9]+.){1,2}[0-9]') + END_VERSIONS + """ +} From 7f205d0a044b031655bbcbe2c3d8c21588a2f854 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Tue, 26 Mar 2024 10:14:01 +0100 Subject: [PATCH 03/65] Update table stats selection --- docs/development.md | 1 + modules/local/addcolumns/main.nf | 7 ++++++- .../tests/main.nf.test.snap | 16 +++------------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/development.md b/docs/development.md index 33051d56..5b358bec 100644 --- a/docs/development.md +++ b/docs/development.md @@ -41,6 +41,7 @@ All channel need to be identified by a meta map as follow: - T : tool used - G : reference genome used (is it needed ?) - S : simulation (depth or genotype array) + ## Open questions How to use different schema ? diff --git a/modules/local/addcolumns/main.nf b/modules/local/addcolumns/main.nf index 54da94a6..2fbe882c 100644 --- a/modules/local/addcolumns/main.nf +++ b/modules/local/addcolumns/main.nf @@ -14,7 +14,12 @@ process ADD_COLUMNS { script: def prefix = task.ext.prefix ?: "${meta.id}" """ - awk '(NR>=2) && (NR<=10)' $input | \\ + # Find the header line + HEADER_STR="#Genotype concordance by allele frequency bin (Variants: SNPs + indels)" + HEADER_LINE=\$(grep -n -m 1 "^\${HEADER_STR}" $input | cut -d: -f1 ) + HEADER_START=\$((HEADER_LINE + 1)) + + tail -n +\$HEADER_START $input | \\ awk 'NR==1{\$(NF+1)="ID"} NR>1{\$(NF+1)="${meta.id}"}1' | \\ awk 'NR==1{\$(NF+1)="Region"} NR>1{\$(NF+1)="${meta.region}"}1' | \\ awk 'NR==1{\$(NF+1)="Depth"} NR>1{\$(NF+1)="${meta.depth}"}1' | \\ diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap b/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap index 608ceca5..94785ba1 100644 --- a/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap +++ b/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap @@ -3,23 +3,13 @@ "content": [ { "0": [ - [ - { - "id": "TestQuality" - }, - "TestQuality.txt:md5,910b294df62dbe64e8f16379428d93ad" - ] + ], "1": [ ], "stats": [ - [ - { - "id": "TestQuality" - }, - "TestQuality.txt:md5,910b294df62dbe64e8f16379428d93ad" - ] + ], "versions": [ @@ -30,6 +20,6 @@ "nf-test": "0.8.4", "nextflow": "23.10.1" }, - "timestamp": "2024-03-25T12:44:54.967846019" + "timestamp": "2024-03-26T10:07:33.413253799" } } \ No newline at end of file From eda2632a5346f96c4e9afad8c3b1882934029738 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Tue, 26 Mar 2024 10:16:00 +0100 Subject: [PATCH 04/65] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb015936..1f4010e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ Initial release of nf-core/phaseimpute, created with the [nf-core](https://nf-co - correct meta map propagation - Test impute and test sim works - [#19](https://github.com/nf-core/phaseimpute/pull/19) - Changed reference panel to accept a csv, update modules and subworkflows (glimpse1/2 and shapeit5) +- [#20](https://github.com/nf-core/phaseimpute/pull/20) - Add validation step for concordance analysis. Input channels changed to +match inputs steps. ### `Fixed` From 5b1c76289d3c8b96c88bc50029436b4b8e3ff25a Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Tue, 26 Mar 2024 10:17:58 +0100 Subject: [PATCH 05/65] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f4010e5..14fe9f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ Initial release of nf-core/phaseimpute, created with the [nf-core](https://nf-co - Test impute and test sim works - [#19](https://github.com/nf-core/phaseimpute/pull/19) - Changed reference panel to accept a csv, update modules and subworkflows (glimpse1/2 and shapeit5) - [#20](https://github.com/nf-core/phaseimpute/pull/20) - Add validation step for concordance analysis. Input channels changed to -match inputs steps. + match inputs steps. ### `Fixed` From 3c749706330b1fab46c71cc0dc039220f022f0c0 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Tue, 26 Mar 2024 10:34:20 +0100 Subject: [PATCH 06/65] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14fe9f3e..51389580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Initial release of nf-core/phaseimpute, created with the [nf-core](https://nf-co - correct meta map propagation - Test impute and test sim works - [#19](https://github.com/nf-core/phaseimpute/pull/19) - Changed reference panel to accept a csv, update modules and subworkflows (glimpse1/2 and shapeit5) -- [#20](https://github.com/nf-core/phaseimpute/pull/20) - Add validation step for concordance analysis. Input channels changed to +- [#22](https://github.com/nf-core/phaseimpute/pull/20) - Add validation step for concordance analysis. Input channels changed to match inputs steps. ### `Fixed` From 8f93eb5e80d9e2794be9e5cc2f859a1596240f57 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 10 Apr 2024 16:19:27 +0200 Subject: [PATCH 07/65] Rename sbwf to glimpse1 and glimpse2 Concatenate is now done by gawk process --- subworkflows/local/vcf_chr_rename/main.nf | 9 +++---- .../vcf_concordance_glimpse/tests/tags.yml | 2 -- .../main.nf | 13 ++++++---- .../tests/main.nf.test | 10 ++++---- .../tests/main.nf.test.snap | 2 +- .../tests/nextflow.config | 0 .../vcf_concordance_glimpse2/tests/tags.yml | 2 ++ workflows/phaseimpute/main.nf | 24 +++++++++---------- 8 files changed, 33 insertions(+), 29 deletions(-) delete mode 100644 subworkflows/local/vcf_concordance_glimpse/tests/tags.yml rename subworkflows/local/{vcf_concordance_glimpse => vcf_concordance_glimpse2}/main.nf (85%) rename subworkflows/local/{vcf_concordance_glimpse => vcf_concordance_glimpse2}/tests/main.nf.test (95%) rename subworkflows/local/{vcf_concordance_glimpse => vcf_concordance_glimpse2}/tests/main.nf.test.snap (93%) rename subworkflows/local/{vcf_concordance_glimpse => vcf_concordance_glimpse2}/tests/nextflow.config (100%) create mode 100644 subworkflows/local/vcf_concordance_glimpse2/tests/tags.yml diff --git a/subworkflows/local/vcf_chr_rename/main.nf b/subworkflows/local/vcf_chr_rename/main.nf index 84a3b8b9..20c2e967 100644 --- a/subworkflows/local/vcf_chr_rename/main.nf +++ b/subworkflows/local/vcf_chr_rename/main.nf @@ -1,6 +1,6 @@ -include { BCFTOOLS_ANNOTATE } from '../../../modules/nf-core/bcftools/annotate/main.nf' -include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index/main.nf' -include { GAWK as FAITOCHR } from '../../../modules/nf-core/gawk/main.nf' +include { BCFTOOLS_ANNOTATE } from '../../../modules/nf-core/bcftools/annotate' +include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index' +include { GAWK as FAITOCHR } from '../../../modules/nf-core/gawk' workflow VCF_CHR_RENAME { take: @@ -16,7 +16,8 @@ workflow VCF_CHR_RENAME { ch_fasta.map{ metaG, fasta, fai -> [metaG, fai] }, Channel.of( 'BEGIN {FS="\\t"} NR==1 { if ($1 ~ /^chr/) { col1=""; col2="chr" } else { col1="chr"; col2="" } } { sub(/^chr/, "", $1); if ($1 ~ /^[0-9]+|[XYMT]$/) print col1$1, col2$1; else print $1, $1 }' - ).collectFile(name:"program.txt")) + ).collectFile(name:"program.txt") + ) ch_versions = ch_versions.mix(FAITOCHR.out.versions) // Rename the chromosome without prefix diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/tags.yml b/subworkflows/local/vcf_concordance_glimpse/tests/tags.yml deleted file mode 100644 index 56e39343..00000000 --- a/subworkflows/local/vcf_concordance_glimpse/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -subworkflows/vcf_concordance_glimpse: - - subworkflows/local/vcf_concordance_glimpse/** diff --git a/subworkflows/local/vcf_concordance_glimpse/main.nf b/subworkflows/local/vcf_concordance_glimpse2/main.nf similarity index 85% rename from subworkflows/local/vcf_concordance_glimpse/main.nf rename to subworkflows/local/vcf_concordance_glimpse2/main.nf index 37ef9f30..0eed4f7f 100644 --- a/subworkflows/local/vcf_concordance_glimpse/main.nf +++ b/subworkflows/local/vcf_concordance_glimpse2/main.nf @@ -1,9 +1,9 @@ include { GLIMPSE2_CONCORDANCE } from '../../../modules/nf-core/glimpse2/concordance' -include { CONCATENATE } from '../../../modules/local/concatenate' +include { GAWK as CONCATENATE } from '../../../modules/nf-core/gawk' include { ADD_COLUMNS } from '../../../modules/local/addcolumns' include { GUNZIP } from '../../../modules/nf-core/gunzip' -workflow VCF_CONCORDANCE_GLIMPSE { +workflow VCF_CONCORDANCE_GLIMPSE2 { take: ch_vcf_emul // VCF file with imputed genotypes [[id, chr, region, panel, simulate, tools], vcf, csi] @@ -38,12 +38,15 @@ workflow VCF_CONCORDANCE_GLIMPSE { GUNZIP(GLIMPSE2_CONCORDANCE.out.errors_grp) ADD_COLUMNS(GUNZIP.out.gunzip) - CONCATENATE(ADD_COLUMNS.out.txt + CONCATENATE( + ADD_COLUMNS.out.txt .map{meta, txt -> [["id":"TestQuality"], txt]} - .groupTuple() + .groupTuple(), + Channel.of( + '(NR == 1) || (FNR > 1)' + ).collectFile(name:"program.txt") ) - emit: stats = CONCATENATE.out.txt // [ meta, txt ] versions = ch_versions // channel: [ versions.yml ] diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test similarity index 95% rename from subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test rename to subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test index 9d00a486..5b75c20d 100644 --- a/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test @@ -1,15 +1,15 @@ nextflow_workflow { - name "Test Subworkflow VCF_CONCORDANCE_GLIMPSE" + name "Test Subworkflow VCF_CONCORDANCE_GLIMPSE2" script "../main.nf" config "./nextflow.config" - workflow "VCF_CONCORDANCE_GLIMPSE" + workflow "VCF_CONCORDANCE_GLIMPSE2" tag "subworkflows" tag "subworkflows_local" - tag "subworkflows/vcf_concordance_glimpse" - tag "vcf_concordance_glimpse" + tag "subworkflows/vcf_concordance_glimpse2" + tag "vcf_concordance_glimpse2" tag "bcftools" tag "bcftools/index" @@ -17,7 +17,7 @@ nextflow_workflow { tag "glimpse/phase" tag "glimpse/concordance" - test("vcf_concordance_glimpse") { + test("vcf_concordance_glimpse2") { setup { run("GLIMPSE_PHASE") { script "../../../../modules/nf-core/glimpse/phase/main.nf" diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap similarity index 93% rename from subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap rename to subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap index 94785ba1..c1e6a6dc 100644 --- a/subworkflows/local/vcf_concordance_glimpse/tests/main.nf.test.snap +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap @@ -1,5 +1,5 @@ { - "vcf_concordance_glimpse": { + "vcf_concordance_glimpse2": { "content": [ { "0": [ diff --git a/subworkflows/local/vcf_concordance_glimpse/tests/nextflow.config b/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config similarity index 100% rename from subworkflows/local/vcf_concordance_glimpse/tests/nextflow.config rename to subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config diff --git a/subworkflows/local/vcf_concordance_glimpse2/tests/tags.yml b/subworkflows/local/vcf_concordance_glimpse2/tests/tags.yml new file mode 100644 index 00000000..35cfc8a3 --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/tags.yml @@ -0,0 +1,2 @@ +subworkflows/vcf_concordance_glimpse2: + - subworkflows/local/vcf_concordance_glimpse2/** diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 8d18d607..e50afcd1 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -17,14 +17,14 @@ include { methodsDescriptionText } from '../../subworkflows/local/utils_nfc // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { VCF_IMPUTE_GLIMPSE } from '../../subworkflows/nf-core/vcf_impute_glimpse' -include { BAM_REGION } from '../../subworkflows/local/bam_region' -include { BAM_DOWNSAMPLE } from '../../subworkflows/local/bam_downsample' -include { COMPUTE_GL as GL_TRUTH } from '../../subworkflows/local/compute_gl' -include { COMPUTE_GL as GL_INPUT } from '../../subworkflows/local/compute_gl' -include { VCF_CONCORDANCE_GLIMPSE } from '../../subworkflows/local/vcf_concordance_glimpse' -include { VCF_CHR_CHECK } from '../../subworkflows/local/vcf_chr_check' -include { GET_PANEL } from '../../subworkflows/local/get_panel' +include { VCF_IMPUTE_GLIMPSE as VCF_IMPUTE_GLIMPSE1 } from '../../subworkflows/nf-core/vcf_impute_glimpse' +include { BAM_REGION } from '../../subworkflows/local/bam_region' +include { BAM_DOWNSAMPLE } from '../../subworkflows/local/bam_downsample' +include { COMPUTE_GL as GL_TRUTH } from '../../subworkflows/local/compute_gl' +include { COMPUTE_GL as GL_INPUT } from '../../subworkflows/local/compute_gl' +include { VCF_CONCORDANCE_GLIMPSE2 } from '../../subworkflows/local/vcf_concordance_glimpse2' +include { VCF_CHR_CHECK } from '../../subworkflows/local/vcf_chr_check' +include { GET_PANEL } from '../../subworkflows/local/get_panel' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -137,9 +137,9 @@ workflow PHASEIMPUTE { -> [metaIPC+metaCR.subMap("Region"), vcf, index, sample, region, panel, p_index, map] } //[ metaIPCR, vcf, csi, sample, region, ref, ref_index, map ] - VCF_IMPUTE_GLIMPSE(impute_input) - output_glimpse1 = VCF_IMPUTE_GLIMPSE.out.merged_variants - .combine(VCF_IMPUTE_GLIMPSE.out.merged_variants_index, by: 0) + VCF_IMPUTE_GLIMPSE1(impute_input) + output_glimpse1 = VCF_IMPUTE_GLIMPSE1.out.merged_variants + .combine(VCF_IMPUTE_GLIMPSE1.out.merged_variants_index, by: 0) .map{ metaIPCR, vcf, csi -> [metaIPCR + [tools: "Glimpse1"], vcf, csi] } ch_impute_output = ch_impute_output.mix(output_glimpse1) } @@ -165,7 +165,7 @@ workflow PHASEIMPUTE { ) // Compute concordance analysis - VCF_CONCORDANCE_GLIMPSE( + VCF_CONCORDANCE_GLIMPSE2( ch_input_validate, GL_TRUTH.out.vcf, ch_panel_sites From b7426fe2a914983c20d585a4c55ea8a7ba261420 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 10 Apr 2024 17:48:08 +0200 Subject: [PATCH 08/65] Separate simulation config --- conf/modules.config | 34 +++++++++++++++++++++------------- conf/modules/simulation.config | 24 ++++++++++++++++++++++++ nextflow.config | 5 +++++ 3 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 conf/modules/simulation.config diff --git a/conf/modules.config b/conf/modules.config index 0fa706c2..003ccb26 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -35,18 +35,6 @@ process { ] } - // Simulation workflow - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_REGION:SAMTOOLS_VIEW' { - ext.args = [ - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region}" } - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:SAMTOOLS_VIEW' { - ext.args = [ - ].join(' ') - ext.prefix = { "${meta.id}_D${meta.depth}" } - } - // Panel preparation workflow withName: VIEW_VCF_REGION { ext.args = [ @@ -127,6 +115,10 @@ process { } withName: GLIMPSE_PHASE { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" }, + mode: params.publish_dir_mode + ] ext.args = [ "--impute-reference-only-variants" ].join(' ') @@ -134,6 +126,10 @@ process { ext.suffix = "bcf" } withName: GLIMPSE_CHUNK { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" }, + mode: params.publish_dir_mode + ] ext.args = [ "--window-size 200000", "--buffer-size 20000" @@ -141,12 +137,24 @@ process { ext.prefix = { "${meta.id}" } } withName: GLIMPSE_LIGATE { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" }, + mode: params.publish_dir_mode + ] ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}" } } - withName: GLIMPSE2_CONCORDANCE { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { + publishDir = [ + path: { "${params.outdir}/validation/concordance" }, + mode: params.publish_dir_mode + ] ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}" } } withName: ADD_COLUMNS { + publishDir = [ + path: { "${params.outdir}/validation" }, + mode: params.publish_dir_mode + ] ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}_SNP" } } } diff --git a/conf/modules/simulation.config b/conf/modules/simulation.config new file mode 100644 index 00000000..1566cb89 --- /dev/null +++ b/conf/modules/simulation.config @@ -0,0 +1,24 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_REGION:SAMTOOLS_VIEW' { + ext.args = [ + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region}" } + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:SAMTOOLS_VIEW' { + ext.args = [ + ].join(' ') + ext.prefix = { "${meta.id}_D${meta.depth}" } + } +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index ba21f779..91cc7cfe 100644 --- a/nextflow.config +++ b/nextflow.config @@ -262,6 +262,11 @@ manifest { // Load modules.config for DSL2 module specific options includeConfig 'conf/modules.config' + +// simulation step +includeConfig 'conf/modules/simulation.config' + + // Function to ensure that resource requirements don't go beyond // a maximum limit def check_max(obj, type) { From 59f3203da347c6e26c49519ec418bf773bd77d41 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Thu, 11 Apr 2024 10:34:28 +0200 Subject: [PATCH 09/65] Separate configuration into each step and use full path --- conf/modules.config | 123 --------------------------------- conf/modules/imputation.config | 58 ++++++++++++++++ conf/modules/panel_prep.config | 77 +++++++++++++++++++++ conf/modules/simulation.config | 2 +- conf/modules/validation.config | 42 +++++++++++ nextflow.config | 9 ++- 6 files changed, 186 insertions(+), 125 deletions(-) create mode 100644 conf/modules/imputation.config create mode 100644 conf/modules/panel_prep.config create mode 100644 conf/modules/validation.config diff --git a/conf/modules.config b/conf/modules.config index 003ccb26..a1c706f9 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -34,127 +34,4 @@ process { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - - // Panel preparation workflow - withName: VIEW_VCF_REGION { - ext.args = [ - "--output-type z", - "--no-version" - ].join(' ') - ext.prefix = { "${meta.id}_${meta.region}" } - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CHR_CHECK:VCF_CHR_RENAME:BCFTOOLS_ANNOTATE' { - ext.args = [ - "-Oz", - "--no-version" - ].join(' ') - ext.prefix = { "${meta.id}_chrrename" } - } - - withName: VIEW_VCF_SNPS { - ext.args = [ - "-m 2", - "-M 2", - "-v snps", - "--output-type z", - "--no-version" - ].join(' ') - ext.prefix = { "${meta.id}_SPNS" } - } - withName: BCFTOOLS_NORM{ - ext.args = [ - "-m", - "-any", - "--no-version" - ].join(' ') - ext.prefix = { "${meta.id}_norm" } - } - withName: VIEW_VCF_SITES { - ext.args = [ - "-G", - "-m 2", - "-M 2", - "-v snps", - "--output-type z", - "--no-version" - ].join(' ') - ext.prefix = { "${meta.id}_SITES" } - } - withName: BCFTOOLS_QUERY { - ext.args = [ - "-f'%CHROM\t%POS\t%REF,%ALT\n'", - ].join(' ') - ext.prefix = { "${meta.id}_SITES_TSV" } - } - withName: TABIX_TABIX { - ext.args = [ - "-s1", - "-b2", - "-e2" - ].join(' ') - ext.prefix = { "${meta.id}_SITES_TSV" } - } - withName: BEDTOOLS_MAKEWINDOWS { - ext.args = [ - '-w 60000', - '-s 40000' - ].join(' ') - ext.prefix = { "${meta.id}_chunks" } - } - withName: BCFTOOLS_MPILEUP { - ext.args = [ - "-I", - "-E", - "-a 'FORMAT/DP'" - ].join(' ') - ext.args2 = [ - "-Aim", - "-C alleles" - ].join(' ') - } - - withName: GLIMPSE_PHASE { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" }, - mode: params.publish_dir_mode - ] - ext.args = [ - "--impute-reference-only-variants" - ].join(' ') - ext.prefix = { "${meta.id}" } - ext.suffix = "bcf" - } - withName: GLIMPSE_CHUNK { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" }, - mode: params.publish_dir_mode - ] - ext.args = [ - "--window-size 200000", - "--buffer-size 20000" - ].join(' ') - ext.prefix = { "${meta.id}" } - } - withName: GLIMPSE_LIGATE { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" }, - mode: params.publish_dir_mode - ] - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}" } - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { - publishDir = [ - path: { "${params.outdir}/validation/concordance" }, - mode: params.publish_dir_mode - ] - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}" } - } - withName: ADD_COLUMNS { - publishDir = [ - path: { "${params.outdir}/validation" }, - mode: params.publish_dir_mode - ] - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}_SNP" } - } } diff --git a/conf/modules/imputation.config b/conf/modules/imputation.config new file mode 100644 index 00000000..4347fee7 --- /dev/null +++ b/conf/modules/imputation.config @@ -0,0 +1,58 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + // Configuration for the glimpse1 imputation subworkflow + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { + ext.args = [ + "-I", + "-E", + "-a 'FORMAT/DP'" + ].join(' ') + ext.args2 = [ + "-Aim", + "-C alleles" + ].join(' ') + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" }, + mode: params.publish_dir_mode + ] + ext.args = [ + "--window-size 200000", + "--buffer-size 20000" + ].join(' ') + ext.prefix = { "${meta.id}" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" }, + mode: params.publish_dir_mode + ] + ext.args = [ + "--impute-reference-only-variants" + ].join(' ') + ext.prefix = { "${meta.id}" } + ext.suffix = "bcf" + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" }, + mode: params.publish_dir_mode + ] + ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}" } + } +} diff --git a/conf/modules/panel_prep.config b/conf/modules/panel_prep.config new file mode 100644 index 00000000..28e4c77f --- /dev/null +++ b/conf/modules/panel_prep.config @@ -0,0 +1,77 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CHR_CHECK:VCF_CHR_RENAME:BCFTOOLS_ANNOTATE' { + ext.args = [ + "-Oz", + "--no-version" + ].join(' ') + ext.prefix = { "${meta.id}_chrrename" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:BCFTOOLS_NORM' { + ext.args = [ + "-m", + "-any", + "--no-version" + ].join(' ') + ext.prefix = { "${meta.id}_norm" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:VIEW_VCF_SNPS' { + ext.args = [ + "-m 2", + "-M 2", + "-v snps", + "--output-type z", + "--no-version" + ].join(' ') + ext.prefix = { "${meta.id}_SPNS" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:VIEW_VCF_SITES' { + ext.args = [ + "-G", + "-m 2", + "-M 2", + "-v snps", + "--output-type z", + "--no-version" + ].join(' ') + ext.prefix = { "${meta.id}_SITES" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:BCFTOOLS_QUERY' { + ext.args = [ + "-f'%CHROM\t%POS\t%REF,%ALT\n'", + ].join(' ') + ext.prefix = { "${meta.id}_SITES_TSV" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:TABIX_TABIX' { + ext.args = [ + "-s1", + "-b2", + "-e2" + ].join(' ') + ext.prefix = { "${meta.id}_SITES_TSV" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_PHASE_SHAPEIT5:BEDTOOLS_MAKEWINDOWS' { + ext.args = [ + '-w 60000', + '-s 40000' + ].join(' ') + ext.prefix = { "${meta.id}_chunks" } + } +} diff --git a/conf/modules/simulation.config b/conf/modules/simulation.config index 1566cb89..df09ad74 100644 --- a/conf/modules/simulation.config +++ b/conf/modules/simulation.config @@ -21,4 +21,4 @@ process { ].join(' ') ext.prefix = { "${meta.id}_D${meta.depth}" } } -} \ No newline at end of file +} diff --git a/conf/modules/validation.config b/conf/modules/validation.config new file mode 100644 index 00000000..3fb51ced --- /dev/null +++ b/conf/modules/validation.config @@ -0,0 +1,42 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + // Configuration for the validation step + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:BCFTOOLS_MPILEUP' { + ext.args = [ + "-I", + "-E", + "-a 'FORMAT/DP'" + ].join(' ') + ext.args2 = [ + "-Aim", + "-C alleles" + ].join(' ') + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { + publishDir = [ + path: { "${params.outdir}/validation/concordance" }, + mode: params.publish_dir_mode + ] + ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:ADD_COLUMNS' { + publishDir = [ + path: { "${params.outdir}/validation" }, + mode: params.publish_dir_mode + ] + ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}_SNP" } + } +} diff --git a/nextflow.config b/nextflow.config index 91cc7cfe..439372ab 100644 --- a/nextflow.config +++ b/nextflow.config @@ -262,10 +262,17 @@ manifest { // Load modules.config for DSL2 module specific options includeConfig 'conf/modules.config' - // simulation step includeConfig 'conf/modules/simulation.config' +// panel_prep step +includeConfig 'conf/modules/panel_prep.config' + +// imputation step +includeConfig 'conf/modules/imputation.config' + +// validation step +includeConfig 'conf/modules/validation.config' // Function to ensure that resource requirements don't go beyond // a maximum limit From a582b343e0c54fcde4c4e25b575320e042944c52 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Thu, 11 Apr 2024 11:33:29 +0200 Subject: [PATCH 10/65] Update conf files --- conf/modules/imputation.config | 16 +++++++--------- conf/modules/panel_prep.config | 11 +++++++++++ conf/modules/validation.config | 11 +++++------ conf/test_validate.config | 5 ++--- nextflow.config | 12 +++++++++--- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/conf/modules/imputation.config b/conf/modules/imputation.config index 4347fee7..c183ea15 100644 --- a/conf/modules/imputation.config +++ b/conf/modules/imputation.config @@ -13,6 +13,10 @@ process { // Configuration for the glimpse1 imputation subworkflow withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" }, + mode: params.publish_dir_mode + ] ext.args = [ "-I", "-E", @@ -24,11 +28,13 @@ process { ].join(' ') } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:*' { publishDir = [ path: { "${params.outdir}/imputation/glimpse1" }, mode: params.publish_dir_mode ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { ext.args = [ "--window-size 200000", "--buffer-size 20000" @@ -37,10 +43,6 @@ process { } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" }, - mode: params.publish_dir_mode - ] ext.args = [ "--impute-reference-only-variants" ].join(' ') @@ -49,10 +51,6 @@ process { } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" }, - mode: params.publish_dir_mode - ] ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}" } } } diff --git a/conf/modules/panel_prep.config b/conf/modules/panel_prep.config index 28e4c77f..d55b7182 100644 --- a/conf/modules/panel_prep.config +++ b/conf/modules/panel_prep.config @@ -12,6 +12,10 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CHR_CHECK:VCF_CHR_RENAME:BCFTOOLS_ANNOTATE' { + publishDir = [ + path: { "${params.outdir}/prep_panel" }, + mode: params.publish_dir_mode + ] ext.args = [ "-Oz", "--no-version" @@ -19,6 +23,13 @@ process { ext.prefix = { "${meta.id}_chrrename" } } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:*' { + publishDir = [ + path: { "${params.outdir}/prep_panel" }, + mode: params.publish_dir_mode + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:BCFTOOLS_NORM' { ext.args = [ "-m", diff --git a/conf/modules/validation.config b/conf/modules/validation.config index 3fb51ced..875195c1 100644 --- a/conf/modules/validation.config +++ b/conf/modules/validation.config @@ -24,19 +24,18 @@ process { ].join(' ') } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:*' { publishDir = [ - path: { "${params.outdir}/validation/concordance" }, + path: { "${params.outdir}/validation" }, mode: params.publish_dir_mode ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}" } } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:ADD_COLUMNS' { - publishDir = [ - path: { "${params.outdir}/validation" }, - mode: params.publish_dir_mode - ] ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}_SNP" } } } diff --git a/conf/test_validate.config b/conf/test_validate.config index 7d7e7057..ad9c476d 100644 --- a/conf/test_validate.config +++ b/conf/test_validate.config @@ -28,7 +28,6 @@ params { fasta = "https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/reference_genome/21_22/hs38DH.chr21_22.fa" panel = "${projectDir}/tests/csv/panel.csv" phased = true - map = "${projectDir}/tests/csv/map.csv" - - step = "validate" + map = "${projectDir}/tests/csv/map.csv" + step = "validate" } diff --git a/nextflow.config b/nextflow.config index 439372ab..f7d55c55 100644 --- a/nextflow.config +++ b/nextflow.config @@ -42,6 +42,11 @@ params { depth = 1 genotype = null + // Validation + bins = "0 0.01 0.05 0.1 0.2 0.5" + min_val_gl = 0.9 + min_val_dp = 5 + // Boilerplate options outdir = null publish_dir_mode = 'copy' @@ -189,9 +194,10 @@ profiles { executor.cpus = 4 executor.memory = 8.GB } - test { includeConfig 'conf/test.config' } - test_full { includeConfig 'conf/test_full.config' } - test_sim { includeConfig 'conf/test_sim.config' } + test { includeConfig 'conf/test.config' } + test_full { includeConfig 'conf/test_full.config' } + test_sim { includeConfig 'conf/test_sim.config' } + test_validate { includeConfig 'conf/test_validate.config' } } // Set default registry for Apptainer, Docker, Podman and Singularity independent of -profile From f58adb0386bc1b0350acea5f4557feaa5fb5f5bb Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Thu, 11 Apr 2024 11:34:06 +0200 Subject: [PATCH 11/65] Add params for concordance analysis --- subworkflows/local/vcf_concordance_glimpse2/main.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/vcf_concordance_glimpse2/main.nf b/subworkflows/local/vcf_concordance_glimpse2/main.nf index 0eed4f7f..a8b1f0e7 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/main.nf +++ b/subworkflows/local/vcf_concordance_glimpse2/main.nf @@ -32,8 +32,8 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { GLIMPSE2_CONCORDANCE ( ch_concordance, - [[], [], "0 0.01 0.05 0.1 0.2 0.5", [], []], - 0.9, 5 + [[], [], params.bins, [], []], + params.min_val_gl, params.min_val_dp ) GUNZIP(GLIMPSE2_CONCORDANCE.out.errors_grp) ADD_COLUMNS(GUNZIP.out.gunzip) From 81a74bb08582a8fd863f1dd8a0525ec821856549 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Thu, 11 Apr 2024 11:34:40 +0200 Subject: [PATCH 12/65] VCF and BCF can now bbe use as input files --- assets/schema_input.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/schema_input.json b/assets/schema_input.json index aca033f6..7ed0b967 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -13,15 +13,15 @@ "errorMessage": "Sample name must be provided and cannot contain spaces", "meta": ["id"] }, - "bam": { + "file": { "type": "string", - "pattern": "^\\S+\\.bam$", - "errorMessage": "BAM file must be provided, cannot contain spaces and must have extension '.bam'" + "pattern": "^\\S+\\.(bam)|((vcf|bcf)(\\.gz))?$", + "errorMessage": "BAM, VCF or BCF file must be provided, cannot contain spaces and must have extension '.bam' or '.vcf', '.bcf' with optional '.gz' extension" }, - "bai": { - "errorMessage": "BAI file must be provided, cannot contain spaces and must have extension '.bai'", + "index": { + "errorMessage": "Input file index must be provided, cannot contain spaces and must have extension '.bai', '.tbi' or '.csi'", "type": "string", - "pattern": "^\\S+\\.bai$" + "pattern": "^\\S+\\.(bai|tbi|csi)$" } }, "required": ["sample", "bam", "bai"] From d1531b186ecf031531ccd1f9bb2ab8d0724efa53 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Thu, 11 Apr 2024 11:43:17 +0200 Subject: [PATCH 13/65] Update schema for validation params --- nextflow_schema.json | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index b1552439..dc78ad06 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -8,7 +8,7 @@ "simulate": { "title": "Simulate", "type": "object", - "description": "Argument for the simulation mode", + "description": "Arguments for the simulation mode", "default": "", "properties": { "depth": { @@ -29,7 +29,7 @@ "panelprep": { "title": "Panel preparation", "type": "object", - "description": "Argument for the preparation of the reference panel", + "description": "Arguments for the preparation of the reference panel", "default": "", "properties": { "panel": { @@ -55,6 +55,29 @@ } } }, + "validation": { + "title": "Concordance analysis", + "type": "object", + "description": "Arguments for the concordance analysis of the imputed data", + "default": "", + "properties": { + "bins": { + "type": "string", + "description": "User-defined allele count bins used for rsquared computations.", + "pattern": "^(\\d+ )+\\d+$" + }, + "min_val_gl": { + "type": "number", + "description": "Minimum genotype likelihood probability P(G|R) in validation data. Set to zero to have no filter of if using –gt-validation", + "pattern": "^\\d+(\\.\\d+)?$" + }, + "min_val_dp": { + "description": "Minimum coverage in validation data. If FORMAT/DP is missing and –min_val_dp > 0, the program exits with an error. Set to zero to have no filter of if using –gt-validation", + "type": "integer", + "pattern": "^\\d+$" + } + } + }, "input_output_options": { "title": "Input/output options", "type": "object", From 99831cd2acc6bf88ee82e185d59de2b5aafa87e5 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Thu, 11 Apr 2024 11:51:19 +0200 Subject: [PATCH 14/65] Update modules --- modules.json | 4 +- modules/nf-core/gawk/tests/main.nf.test | 56 +++++++++++++++ modules/nf-core/gawk/tests/main.nf.test.snap | 68 +++++++++++++++++++ modules/nf-core/gawk/tests/nextflow.config | 6 ++ .../tests/nextflow_with_program_file.config | 5 ++ modules/nf-core/gawk/tests/tags.yml | 2 + .../nf-core/samtools/faidx/tests/main.nf.test | 25 ++++--- 7 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 modules/nf-core/gawk/tests/main.nf.test create mode 100644 modules/nf-core/gawk/tests/main.nf.test.snap create mode 100644 modules/nf-core/gawk/tests/nextflow.config create mode 100644 modules/nf-core/gawk/tests/nextflow_with_program_file.config create mode 100644 modules/nf-core/gawk/tests/tags.yml diff --git a/modules.json b/modules.json index b7c97ae5..43bfae97 100644 --- a/modules.json +++ b/modules.json @@ -50,7 +50,7 @@ }, "gawk": { "branch": "master", - "git_sha": "dc3527855e7358c6d8400828754c0caa5f11698f", + "git_sha": "da4d05d04e65227d4307e87940842f1a14de62c7", "installed_by": ["modules"] }, "glimpse/chunk": { @@ -116,7 +116,7 @@ }, "samtools/faidx": { "branch": "master", - "git_sha": "aeb02a39d4c463598bfdcb2d964dbb7acbcf1298", + "git_sha": "f153f1f10e1083c49935565844cccb7453021682", "installed_by": ["modules"] }, "samtools/index": { diff --git a/modules/nf-core/gawk/tests/main.nf.test b/modules/nf-core/gawk/tests/main.nf.test new file mode 100644 index 00000000..fce82ca9 --- /dev/null +++ b/modules/nf-core/gawk/tests/main.nf.test @@ -0,0 +1,56 @@ +nextflow_process { + + name "Test Process GAWK" + script "../main.nf" + process "GAWK" + + tag "modules" + tag "modules_nfcore" + tag "gawk" + + test("convert fasta to bed") { + config "./nextflow.config" + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ] + input[1] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("convert fasta to bed with program file") { + config "./nextflow_with_program_file.config" + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ] + input[1] = Channel.of('BEGIN {FS="\t"}; {print \$1 FS "0" FS \$2}').collectFile(name:"program.txt") + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } +} \ No newline at end of file diff --git a/modules/nf-core/gawk/tests/main.nf.test.snap b/modules/nf-core/gawk/tests/main.nf.test.snap new file mode 100644 index 00000000..ce207478 --- /dev/null +++ b/modules/nf-core/gawk/tests/main.nf.test.snap @@ -0,0 +1,68 @@ +{ + "convert fasta to bed with program file": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.bed:md5,87a15eb9c2ff20ccd5cd8735a28708f7" + ] + ], + "1": [ + "versions.yml:md5,4c320d8c98ca80690afd7651da1ba520" + ], + "output": [ + [ + { + "id": "test" + }, + "test.bed:md5,87a15eb9c2ff20ccd5cd8735a28708f7" + ] + ], + "versions": [ + "versions.yml:md5,4c320d8c98ca80690afd7651da1ba520" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "24.02.0" + }, + "timestamp": "2024-04-05T11:00:28.097563" + }, + "convert fasta to bed": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.bed:md5,87a15eb9c2ff20ccd5cd8735a28708f7" + ] + ], + "1": [ + "versions.yml:md5,4c320d8c98ca80690afd7651da1ba520" + ], + "output": [ + [ + { + "id": "test" + }, + "test.bed:md5,87a15eb9c2ff20ccd5cd8735a28708f7" + ] + ], + "versions": [ + "versions.yml:md5,4c320d8c98ca80690afd7651da1ba520" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "24.02.0" + }, + "timestamp": "2024-04-05T10:28:15.625869" + } +} \ No newline at end of file diff --git a/modules/nf-core/gawk/tests/nextflow.config b/modules/nf-core/gawk/tests/nextflow.config new file mode 100644 index 00000000..6e5d43a3 --- /dev/null +++ b/modules/nf-core/gawk/tests/nextflow.config @@ -0,0 +1,6 @@ +process { + withName: GAWK { + ext.suffix = "bed" + ext.args2 = '\'BEGIN {FS="\t"}; {print \$1 FS "0" FS \$2}\'' + } +} diff --git a/modules/nf-core/gawk/tests/nextflow_with_program_file.config b/modules/nf-core/gawk/tests/nextflow_with_program_file.config new file mode 100644 index 00000000..693ad419 --- /dev/null +++ b/modules/nf-core/gawk/tests/nextflow_with_program_file.config @@ -0,0 +1,5 @@ +process { + withName: GAWK { + ext.suffix = "bed" + } +} diff --git a/modules/nf-core/gawk/tests/tags.yml b/modules/nf-core/gawk/tests/tags.yml new file mode 100644 index 00000000..72e4531d --- /dev/null +++ b/modules/nf-core/gawk/tests/tags.yml @@ -0,0 +1,2 @@ +gawk: + - "modules/nf-core/gawk/**" diff --git a/modules/nf-core/samtools/faidx/tests/main.nf.test b/modules/nf-core/samtools/faidx/tests/main.nf.test index 136b2126..17244ef2 100644 --- a/modules/nf-core/samtools/faidx/tests/main.nf.test +++ b/modules/nf-core/samtools/faidx/tests/main.nf.test @@ -15,7 +15,7 @@ nextflow_process { process { """ input[0] = [ [ id:'test', single_end:false ], // meta map - file(params.test_data['sarscov2']['genome']['genome_fasta'], checkIfExists: true) ] + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) ] input[1] = [[],[]] """ @@ -27,7 +27,7 @@ nextflow_process { { assert process.success }, { assert snapshot(process.out).match() } ) - } + } } test("test_samtools_faidx_bgzip") { @@ -36,7 +36,7 @@ nextflow_process { process { """ input[0] = [ [ id:'test', single_end:false ], // meta map - file(params.test_data['sarscov2']['genome']['genome_fasta_gz'], checkIfExists: true) ] + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.gz', checkIfExists: true)] input[1] = [[],[]] """ @@ -48,7 +48,7 @@ nextflow_process { { assert process.success }, { assert snapshot(process.out).match() } ) - } + } } test("test_samtools_faidx_fasta") { @@ -59,10 +59,10 @@ nextflow_process { process { """ input[0] = [ [ id:'test', single_end:false ], // meta map - file(params.test_data['sarscov2']['genome']['genome_fasta'], checkIfExists: true) ] + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) ] input[1] = [ [ id:'test', single_end:false ], // meta map - file(params.test_data['sarscov2']['genome']['genome_fasta_fai'], checkIfExists: true) ] + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true) ] """ } } @@ -72,7 +72,7 @@ nextflow_process { { assert process.success }, { assert snapshot(process.out).match() } ) - } + } } test("test_samtools_faidx_stub_fasta") { @@ -83,10 +83,10 @@ nextflow_process { process { """ input[0] = [ [ id:'test', single_end:false ], // meta map - file(params.test_data['sarscov2']['genome']['genome_fasta'], checkIfExists: true) ] + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) ] input[1] = [ [ id:'test', single_end:false ], // meta map - file(params.test_data['sarscov2']['genome']['genome_fasta_fai'], checkIfExists: true) ] + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true) ] """ } } @@ -96,7 +96,7 @@ nextflow_process { { assert process.success }, { assert snapshot(process.out).match() } ) - } + } } test("test_samtools_faidx_stub_fai") { @@ -105,7 +105,7 @@ nextflow_process { process { """ input[0] = [ [ id:'test', single_end:false ], // meta map - file(params.test_data['sarscov2']['genome']['genome_fasta'], checkIfExists: true) ] + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) ] input[1] = [[],[]] """ @@ -117,7 +117,6 @@ nextflow_process { { assert process.success }, { assert snapshot(process.out).match() } ) - } + } } - } \ No newline at end of file From b1c04f5fead2d524ffc07ca1da9f09c40f0c7756 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Thu, 11 Apr 2024 11:51:39 +0200 Subject: [PATCH 15/65] Fix schema --- nextflow_schema.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nextflow_schema.json b/nextflow_schema.json index dc78ad06..b6f09e62 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -393,6 +393,9 @@ { "$ref": "#/definitions/panelprep" }, + { + "$ref": "#/definitions/validation" + }, { "$ref": "#/definitions/input_output_options" }, From 2f1dbeca4e4c028b18240db2127e692e5003a6f8 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Thu, 11 Apr 2024 16:54:49 +0200 Subject: [PATCH 16/65] Update validation process to allow vcf and direct input --- assets/schema_input.json | 2 +- conf/test_all.config | 35 ++++++++++++ conf/test_sim.config | 7 +-- conf/test_validate.config | 4 +- main.nf | 27 ++++++--- nextflow.config | 1 + nextflow_schema.json | 13 ++++- .../utils_nfcore_phaseimpute_pipeline/main.nf | 31 +++++++++- .../local/vcf_concordance_glimpse2/main.nf | 14 ++--- tests/csv/sample_bam.csv | 2 +- tests/csv/sample_sim.csv | 2 +- tests/csv/sample_sim_full.csv | 2 +- tests/csv/sample_validate_imputed.csv | 4 ++ ...validate.csv => sample_validate_truth.csv} | 2 +- tests/csv/sample_vcf.csv | 2 +- workflows/phaseimpute/main.nf | 56 +++++++++++-------- 16 files changed, 149 insertions(+), 55 deletions(-) create mode 100644 conf/test_all.config create mode 100644 tests/csv/sample_validate_imputed.csv rename tests/csv/{sample_validate.csv => sample_validate_truth.csv} (97%) diff --git a/assets/schema_input.json b/assets/schema_input.json index 7ed0b967..971c3fb3 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -24,6 +24,6 @@ "pattern": "^\\S+\\.(bai|tbi|csi)$" } }, - "required": ["sample", "bam", "bai"] + "required": ["sample", "file", "index"] } } diff --git a/conf/test_all.config b/conf/test_all.config new file mode 100644 index 00000000..f007a039 --- /dev/null +++ b/conf/test_all.config @@ -0,0 +1,35 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running minimal tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a fast and simple pipeline test. + + Use as follows: + nextflow run nf-core/phaseimpute -profile test_sim, --outdir + +---------------------------------------------------------------------------------------- +*/ + +params { + config_profile_name = 'Test simulation / imputation / validation mode' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data + input = "${projectDir}/tests/csv/sample_sim.csv" + input_region = "${projectDir}/tests/csv/region.csv" + depth = 1 + + // Genome references + fasta = "https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/reference_genome/21_22/hs38DH.chr21_22.fa" + panel = "${projectDir}/tests/csv/panel.csv" + phased = true + map = "${projectDir}/tests/csv/map.csv" + + step = "all" + tools = "glimpse1" +} diff --git a/conf/test_sim.config b/conf/test_sim.config index 5e06fd2e..6f18229a 100644 --- a/conf/test_sim.config +++ b/conf/test_sim.config @@ -26,10 +26,5 @@ params { // Genome references fasta = "https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/reference_genome/21_22/hs38DH.chr21_22.fa" - panel = "${projectDir}/tests/csv/panel.csv" - phased = true - map = "${projectDir}/tests/csv/map.csv" - - step = "all" - tools = "glimpse1" + step = "simulate" } diff --git a/conf/test_validate.config b/conf/test_validate.config index ad9c476d..c92c39bb 100644 --- a/conf/test_validate.config +++ b/conf/test_validate.config @@ -20,9 +20,9 @@ params { max_time = '6.h' // Input data - input = "${projectDir}/tests/csv/sample_sim.csv" + input = "${projectDir}/tests/csv/sample_validate_imputed.csv" + input_truth = "${projectDir}/tests/csv/sample_validate_truth.csv" input_region = "${projectDir}/tests/csv/region.csv" - depth = 1 // Genome references fasta = "https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/reference_genome/21_22/hs38DH.chr21_22.fa" diff --git a/main.nf b/main.nf index 65b6ae1a..4593f061 100644 --- a/main.nf +++ b/main.nf @@ -33,13 +33,14 @@ include { getGenomeAttribute } from './subworkflows/local/utils_nfcore_phas workflow NFCORE_PHASEIMPUTE { take: - ch_input // channel: samplesheet read in from --input - ch_fasta // channel: reference genome FASTA file with index - ch_panel // channel: reference panel variants file - ch_regions // channel: regions to use [[chr, region], region] - ch_depth // channel: depth of coverage file [[depth], depth] - ch_map // channel: map file for imputation - ch_versions // channel: versions of software used + ch_input // channel: samplesheet read in from --input + ch_input_truth // channel: samplesheet read in from --input-truth + ch_fasta // channel: reference genome FASTA file with index + ch_panel // channel: reference panel variants file + ch_regions // channel: regions to use [[chr, region], region] + ch_depth // channel: depth of coverage file [[depth], depth] + ch_map // channel: map file for imputation + ch_versions // channel: versions of software used main: @@ -57,9 +58,17 @@ workflow NFCORE_PHASEIMPUTE { input_simulate = ch_input } else if (params.step == "validate") { input_validate = ch_input + .combine(ch_regions) + .map { metaI, file, index, metaCR, region -> + [ metaI+metaCR, file, index ] + } + ch_input_truth = ch_input_truth + .combine(ch_regions) + .map { metaI, file, index, metaCR, region -> + [ metaI+metaCR, file, index ] + } } - // // WORKFLOW: Run pipeline // @@ -67,6 +76,7 @@ workflow NFCORE_PHASEIMPUTE { input_impute, input_simulate, input_validate, + ch_input_truth, ch_fasta, ch_panel, ch_regions, @@ -108,6 +118,7 @@ workflow { // NFCORE_PHASEIMPUTE ( PIPELINE_INITIALISATION.out.input, + PIPELINE_INITIALISATION.out.input_truth, PIPELINE_INITIALISATION.out.fasta, PIPELINE_INITIALISATION.out.panel, PIPELINE_INITIALISATION.out.regions, diff --git a/nextflow.config b/nextflow.config index f7d55c55..6d779aab 100644 --- a/nextflow.config +++ b/nextflow.config @@ -43,6 +43,7 @@ params { genotype = null // Validation + input_truth = null bins = "0 0.01 0.05 0.1 0.2 0.5" min_val_gl = 0.9 min_val_dp = 5 diff --git a/nextflow_schema.json b/nextflow_schema.json index b6f09e62..54d8e2ec 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -61,10 +61,21 @@ "description": "Arguments for the concordance analysis of the imputed data", "default": "", "properties": { + "input_truth" :{ + "type": "string", + "format": "file-path", + "exists": true, + "schema": "assets/schema_input.json", + "mimetype": "text/csv", + "pattern": "^\\S+\\.csv$", + "description": "Path to comma-separated file containing information about the samples truth files in the experiment.", + "help_text": "You will need to create a design file with information about the samples in your experiment before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row. See [usage docs](https://nf-co.re/phaseimpute/usage#samplesheet-input).", + "fa_icon": "fas fa-file-csv" + }, "bins": { "type": "string", "description": "User-defined allele count bins used for rsquared computations.", - "pattern": "^(\\d+ )+\\d+$" + "pattern": "^(\\d+(\\.\\d+)? )+(\\d+(\\.\\d+)?)$" }, "min_val_gl": { "type": "number", diff --git a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf index e82c4a67..62d52677 100644 --- a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf @@ -111,10 +111,34 @@ workflow PIPELINE_INITIALISATION { ch_input = Channel .fromSamplesheet("input") .map { - meta, bam, bai -> - [ meta, bam, bai ] + meta, file, index -> + [ meta, file, index ] } + // Check if all extension are identical + ch_input + .map{ it[1].split("\\.").last()} + .distinct() + .view() + /*if (ch_input.map{ it[1].getBaseName().split("\\.").last() }.distinct().size() > 1) { + error "All input files must have the same extension" + } + */ + // + // Create channel from input file provided through params.input_truth + // + ch_input_truth = Channel + .fromSamplesheet("input_truth") + .map { + meta, file, index -> + [ meta, file, index ] + } + /* + // Check if all extension are identical + if (ch_input_truth.map{ it[1].getBaseName().split("\\.").last() }.distinct().size() > 1) { + error "All input files must have the same extension" + }*/ + // // Create channel for panel // @@ -186,7 +210,8 @@ workflow PIPELINE_INITIALISATION { emit: - input = ch_input // [ [meta], bam, bai ] + input = ch_input // [ [meta], file, index ] + input_truth = ch_input_truth // [ [meta], file, index ] fasta = ch_ref_gen // [ [genome], fasta, fai ] panel = ch_panel // [ [panel, chr], vcf, index ] depth = ch_depth // [ [depth], depth ] diff --git a/subworkflows/local/vcf_concordance_glimpse2/main.nf b/subworkflows/local/vcf_concordance_glimpse2/main.nf index a8b1f0e7..9e22930e 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/main.nf +++ b/subworkflows/local/vcf_concordance_glimpse2/main.nf @@ -17,18 +17,18 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { ch_concordance = ch_vcf_emul .map{ metaICRPST, vcf, csi -> - [metaICRPST.subMap(["id", "chr", "region", "panel"]), metaICRPST, vcf, csi] + [metaICRPST.subMap(["id", "chr", "region"]), metaICRPST, vcf, csi] } .combine(ch_vcf_truth, by:0) - .map{metaICRP, metaIPCRTS, emul, e_csi, truth, t_csi -> - [metaICRP.subMap(["chr"]), metaIPCRTS, emul, e_csi, truth, t_csi] + .map{metaICR, metaIPCRTS, emul, e_csi, truth, t_csi -> + [metaICR.subMap(["chr"]), metaIPCRTS, emul, e_csi, truth, t_csi] } .combine(ch_vcf_freq.map{metaCRP, vcf, csi -> [metaCRP.subMap(["chr"]), metaCRP, vcf, csi]}, by:0) .map{metaC, metaIPCRTS, emul, e_csi, truth, t_csi, metaCRP, freq, f_csi -> [metaIPCRTS, emul, e_csi, truth, t_csi, freq, f_csi, [], metaIPCRTS.region] - } + }.view() GLIMPSE2_CONCORDANCE ( ch_concordance, @@ -40,14 +40,14 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { CONCATENATE( ADD_COLUMNS.out.txt - .map{meta, txt -> [["id":"TestQuality"], txt]} - .groupTuple(), + .map{meta, txt -> [["id":"TestQuality"], txt]} + .groupTuple(), Channel.of( '(NR == 1) || (FNR > 1)' ).collectFile(name:"program.txt") ) emit: - stats = CONCATENATE.out.txt // [ meta, txt ] + stats = CONCATENATE.out.output // [ meta, txt ] versions = ch_versions // channel: [ versions.yml ] } diff --git a/tests/csv/sample_bam.csv b/tests/csv/sample_bam.csv index 78269414..17e3a87e 100644 --- a/tests/csv/sample_bam.csv +++ b/tests/csv/sample_bam.csv @@ -1,4 +1,4 @@ -sample,bam,bai +sample,file,index NA12878,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.1x.bam,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.1x.bam.bai NA19401,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.1x.bam,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.1x.bam.bai NA20359,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.1x.bam,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.1x.bam.bai diff --git a/tests/csv/sample_sim.csv b/tests/csv/sample_sim.csv index 7e614856..cb6be1c1 100644 --- a/tests/csv/sample_sim.csv +++ b/tests/csv/sample_sim.csv @@ -1,4 +1,4 @@ -sample,bam,bai +sample,file,index NA12878,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.bam,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.bam.bai NA19401,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.bam,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.bam.bai NA20359,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.bam,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.bam.bai diff --git a/tests/csv/sample_sim_full.csv b/tests/csv/sample_sim_full.csv index c334c666..592282cc 100644 --- a/tests/csv/sample_sim_full.csv +++ b/tests/csv/sample_sim_full.csv @@ -1,2 +1,2 @@ -sample,bam,bai +sample,file,index #TODO find bam not in 1000G panel diff --git a/tests/csv/sample_validate_imputed.csv b/tests/csv/sample_validate_imputed.csv new file mode 100644 index 00000000..3f3da2e2 --- /dev/null +++ b/tests/csv/sample_validate_imputed.csv @@ -0,0 +1,4 @@ +sample,file,index +NA12878,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s_imputed.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s_imputed.bcf.csi +NA19401,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s_imputed.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s_imputed.bcf.csi +NA20359,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s_imputed.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s_imputed.bcf.csi diff --git a/tests/csv/sample_validate.csv b/tests/csv/sample_validate_truth.csv similarity index 97% rename from tests/csv/sample_validate.csv rename to tests/csv/sample_validate_truth.csv index ad25d415..828bad9c 100644 --- a/tests/csv/sample_validate.csv +++ b/tests/csv/sample_validate_truth.csv @@ -1,4 +1,4 @@ -sample,vcf,csi +sample,file,index NA12878,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.bcf.csi NA19401,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.bcf.csi NA20359,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.bcf.csi diff --git a/tests/csv/sample_vcf.csv b/tests/csv/sample_vcf.csv index 14558dc1..e1e92a6c 100644 --- a/tests/csv/sample_vcf.csv +++ b/tests/csv/sample_vcf.csv @@ -1,4 +1,4 @@ -sample,vcf,csi +sample,file,index NA12878,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.1x.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA12878/NA12878.s.1x.bcf.csi NA19401,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.1x.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA19401/NA19401.s.1x.bcf.csi NA20359,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.1x.bcf,https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/individuals/NA20359/NA20359.s.1x.bcf.csi diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index e50afcd1..00a3e83b 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -35,22 +35,21 @@ include { GET_PANEL } from '../../subworkflows/ workflow PHASEIMPUTE { take: - ch_input_impute // channel: input file [ [id, chr], bam, bai ] - ch_input_sim // channel: input file [ [id, chr], bam, bai ] - ch_input_validate // channel: input file [ [id, chr], bam, bai ] - ch_fasta // channel: fasta file [ [genome], fasta, fai ] - ch_panel // channel: panel file [ [id, chr], chr, vcf, index ] - ch_region // channel: region to use [ [chr, region], region] - ch_depth // channel: depth select [ [depth], depth ] - ch_map // channel: genetic map [ [chr], map] - ch_versions // channel: versions of software used + ch_input_impute // channel: input file [ [id], file, index ] + ch_input_sim // channel: input file [ [id], file, index ] + ch_input_validate // channel: input file [ [id], file, index ] + ch_input_validate_truth // channel: truth file [ [id], file, index ] + ch_fasta // channel: fasta file [ [genome], fasta, fai ] + ch_panel // channel: panel file [ [id, chr], chr, vcf, index ] + ch_region // channel: region to use [ [chr, region], region] + ch_depth // channel: depth select [ [depth], depth ] + ch_map // channel: genetic map [ [chr], map] + ch_versions // channel: versions of software used main: ch_multiqc_files = Channel.empty() - ch_validate_truth = Channel.empty() - // // Simulate data if asked // @@ -73,8 +72,8 @@ workflow PHASEIMPUTE { ) ch_versions = ch_versions.mix(BAM_DOWNSAMPLE.out.versions.first()) - ch_input_impute = ch_input_impute.mix(BAM_DOWNSAMPLE.out.bam_emul) - ch_validate_truth = ch_validate_truth.mix(BAM_REGION.out.bam_region) + ch_input_impute = ch_input_impute.mix(BAM_DOWNSAMPLE.out.bam_emul) + ch_input_validate_truth = ch_input_validate_truth.mix(BAM_REGION.out.bam_region) } if (params.genotype) { @@ -85,7 +84,7 @@ workflow PHASEIMPUTE { // // Prepare panel // - if (params.step == 'impute' || params.step == 'panel_prep' || params.step == 'all') { + if (params.step == 'impute' || params.step == 'panel_prep' || params.step == 'validate' || params.step == 'all') { // Remove if necessary "chr" VCF_CHR_CHECK(ch_panel, ch_fasta) ch_versions = ch_versions.mix(VCF_CHR_CHECK.out.versions.first()) @@ -157,17 +156,30 @@ workflow PHASEIMPUTE { } if (params.step == 'validate' || params.step == 'all') { - // Compute truth genotypes likelihoods - GL_TRUTH( - ch_validate_truth, - ch_panel_sites_tsv, - ch_fasta - ) - + ch_truth_vcf = Channel.empty() + // Check if all files are bam + /* + ch_input_validate_truth + .map{it[1].getBaseName().split("\\.").last() == "bam"} + .view() + + if (ch_input_validate_truth.map{it[1].getBaseName().split("\\.").last() == "bam"}.toSet() == true) { + // Compute truth genotypes likelihoods + GL_TRUTH( + ch_input_validate_truth, + ch_panel_sites_tsv, + ch_fasta + ) + ch_multiqc_files = ch_multiqc_files.mix(GL_TRUTH.out.multiqc_files) + ch_truth_vcf = GL_TRUTH.out.vcf + } else { + ch_truth_vcf = ch_input_validate_truth + }*/ + ch_truth_vcf = ch_input_validate_truth // Compute concordance analysis VCF_CONCORDANCE_GLIMPSE2( ch_input_validate, - GL_TRUTH.out.vcf, + ch_truth_vcf, ch_panel_sites ) } From 5bba2341c23953f9efc7c9582cc7478818b3fd8a Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Fri, 12 Apr 2024 13:23:44 +0200 Subject: [PATCH 17/65] Update vcf_concordance_glimpse2 tests --- .../local/vcf_concordance_glimpse2/main.nf | 2 +- .../tests/main.nf.test | 61 ++++++++++++++++++- .../tests/main.nf.test.snap | 37 ++++++++++- .../tests/nextflow.config | 9 +++ 4 files changed, 105 insertions(+), 4 deletions(-) diff --git a/subworkflows/local/vcf_concordance_glimpse2/main.nf b/subworkflows/local/vcf_concordance_glimpse2/main.nf index 9e22930e..09818e20 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/main.nf +++ b/subworkflows/local/vcf_concordance_glimpse2/main.nf @@ -28,7 +28,7 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { by:0) .map{metaC, metaIPCRTS, emul, e_csi, truth, t_csi, metaCRP, freq, f_csi -> [metaIPCRTS, emul, e_csi, truth, t_csi, freq, f_csi, [], metaIPCRTS.region] - }.view() + } GLIMPSE2_CONCORDANCE ( ch_concordance, diff --git a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test index 5b75c20d..d5db8efb 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test @@ -63,7 +63,6 @@ nextflow_workflow { when { workflow { """ - allele_freq = Channel.of([ [panel:'1000GP', chr:'21'], // meta map file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.sites.vcf.gz",checkIfExists:true), @@ -94,4 +93,64 @@ nextflow_workflow { } } + + test("vcf_concordance_glimpse2 direct") { + when { + workflow { + """ + allele_freq = Channel.fromList([ + [ + [panel:'1000GP', chr:'21'], // meta map + file(params.phaseimpute_testdata_path + "panel/21/1000GP.chr21.s.norel.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "panel/21/1000GP.chr21.s.norel.bcf.csi",checkIfExists:true) + ], + [ + [panel:'1000GP', chr:'22'], // meta map + file(params.phaseimpute_testdata_path + "panel/22/1000GP.chr22.s.norel.sites.vcf.gz",checkIfExists:true), + file(params.phaseimpute_testdata_path + "panel/22/1000GP.chr22.s.norel.sites.vcf.gz.csi",checkIfExists:true) + ] + ]) + truth = Channel.fromList([ + [[id:'NA12878', chr:'21', region:'chr21:16650000-16700000'], // meta map + file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s.bcf.csi",checkIfExists:true)], + [[id:'NA12878', chr:'22', region:'chr22:16650000-16700000'], // meta map + file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s.bcf.csi",checkIfExists:true)], + [[id:'NA19401', chr:'21', region:'chr21:16650000-16700000'], // meta map + file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s.bcf.csi",checkIfExists:true)], + [[id:'NA19401', chr:'22', region:'chr22:16650000-16700000'], // meta map + file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s.bcf.csi",checkIfExists:true)] + ]) + estimate = Channel.fromList([ + [[id:'NA12878', chr:'21', region:'chr21:16650000-16700000'], // meta map + file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s_imputed.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s_imputed.bcf.csi",checkIfExists:true)], + [[id:'NA12878', chr:'22', region:'chr22:16650000-16700000'], // meta map + file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s_imputed.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s_imputed.bcf.csi",checkIfExists:true)], + [[id:'NA19401', chr:'21', region:'chr21:16650000-16700000'], // meta map + file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s_imputed.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s_imputed.bcf.csi",checkIfExists:true)], + [[id:'NA19401', chr:'22', region:'chr22:16650000-16700000'], // meta map + file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s_imputed.bcf",checkIfExists:true), + file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s_imputed.bcf.csi",checkIfExists:true)] + ]) + input[0] = estimate + input[1] = truth + input[2] = allele_freq + """ + } + } + + then { + assertAll( + { assert workflow.success }, + { assert snapshot(workflow.out).match() } + ) + } + + } } diff --git a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap index c1e6a6dc..ef151fc8 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap @@ -1,5 +1,5 @@ { - "vcf_concordance_glimpse2": { + "vcf_concordance_glimpse2 direct": { "content": [ { "0": [ @@ -20,6 +20,39 @@ "nf-test": "0.8.4", "nextflow": "23.10.1" }, - "timestamp": "2024-03-26T10:07:33.413253799" + "timestamp": "2024-04-12T12:40:51.934575693" + }, + "vcf_concordance_glimpse2": { + "content": [ + { + "0": [ + [ + { + "id": "TestQuality" + }, + "TestQuality.txt:md5,b8ae89fd25598adc1f96b011a2a79646" + ] + ], + "1": [ + + ], + "stats": [ + [ + { + "id": "TestQuality" + }, + "TestQuality.txt:md5,b8ae89fd25598adc1f96b011a2a79646" + ] + ], + "versions": [ + + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-04-12T12:31:24.442985966" } } \ No newline at end of file diff --git a/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config b/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config index 227aed3d..b02ab26b 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config @@ -1,3 +1,12 @@ params { max_memory = '7.GB' + withName: CONCATENATE { + ext.suffix = "txt" + } + withName: GAWK { + ext.suffix = "txt" + } + withName: 'VCF_CONCORDANCE_GLIMPSE2:CONCATENATE' { + ext.suffix = "txt" + } } From c723060fa17c8bd5eba5d801f8cd115f59ed09d5 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Fri, 12 Apr 2024 18:29:24 +0200 Subject: [PATCH 18/65] Update config for all steps to spearate results files in corresponding folders --- conf/{modules => steps}/imputation.config | 7 +-- .../initialisation.config} | 44 +++++++++---------- conf/{modules => steps}/panel_prep.config | 11 +++-- conf/steps/simulation.config | 38 ++++++++++++++++ conf/{modules => steps}/validation.config | 12 ++++- nextflow.config | 11 +++-- 6 files changed, 86 insertions(+), 37 deletions(-) rename conf/{modules => steps}/imputation.config (85%) rename conf/{modules/simulation.config => steps/initialisation.config} (67%) rename conf/{modules => steps}/panel_prep.config (87%) create mode 100644 conf/steps/simulation.config rename conf/{modules => steps}/validation.config (78%) diff --git a/conf/modules/imputation.config b/conf/steps/imputation.config similarity index 85% rename from conf/modules/imputation.config rename to conf/steps/imputation.config index c183ea15..d463afba 100644 --- a/conf/modules/imputation.config +++ b/conf/steps/imputation.config @@ -12,9 +12,9 @@ process { // Configuration for the glimpse1 imputation subworkflow - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:.*' { publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" }, + path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode ] ext.args = [ @@ -30,10 +30,11 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:*' { publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" }, + path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode ] } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { ext.args = [ "--window-size 200000", diff --git a/conf/modules/simulation.config b/conf/steps/initialisation.config similarity index 67% rename from conf/modules/simulation.config rename to conf/steps/initialisation.config index df09ad74..4fef4265 100644 --- a/conf/modules/simulation.config +++ b/conf/steps/initialisation.config @@ -1,24 +1,20 @@ -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Config file for defining DSL2 per module options and publishing paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Available keys to override module options: - ext.args = Additional arguments appended to command in module. - ext.args2 = Second set of arguments appended to command in module (multi-tool modules). - ext.args3 = Third set of arguments appended to command in module (multi-tool modules). - ext.prefix = File name prefix for output files. ----------------------------------------------------------------------------------------- -*/ - -process { - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_REGION:SAMTOOLS_VIEW' { - ext.args = [ - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region}" } - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:SAMTOOLS_VIEW' { - ext.args = [ - ].join(' ') - ext.prefix = { "${meta.id}_D${meta.depth}" } - } -} +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + withName: 'PIPELINE_INITIALISATION:.*' { + publishDir = [ + path: { "${params.outdir}/initialisation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode + ] + } +} diff --git a/conf/modules/panel_prep.config b/conf/steps/panel_prep.config similarity index 87% rename from conf/modules/panel_prep.config rename to conf/steps/panel_prep.config index d55b7182..fe71fd1a 100644 --- a/conf/modules/panel_prep.config +++ b/conf/steps/panel_prep.config @@ -11,11 +11,14 @@ */ process { - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CHR_CHECK:VCF_CHR_RENAME:BCFTOOLS_ANNOTATE' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CHR_CHECK:.*' { publishDir = [ - path: { "${params.outdir}/prep_panel" }, + path: { "${params.outdir}/prep_panel/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CHR_CHECK:VCF_CHR_RENAME:BCFTOOLS_ANNOTATE' { ext.args = [ "-Oz", "--no-version" @@ -23,9 +26,9 @@ process { ext.prefix = { "${meta.id}_chrrename" } } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:*' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:.*' { publishDir = [ - path: { "${params.outdir}/prep_panel" }, + path: { "${params.outdir}/prep_panel/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode ] } diff --git a/conf/steps/simulation.config b/conf/steps/simulation.config new file mode 100644 index 00000000..582b0ef3 --- /dev/null +++ b/conf/steps/simulation.config @@ -0,0 +1,38 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_REGION:.*' { + publishDir = [ + path: { "${params.outdir}/simulation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:.*' { + publishDir = [ + path: { "${params.outdir}/simulation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_REGION:SAMTOOLS_VIEW' { + ext.args = [ + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region}" } + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:SAMTOOLS_VIEW' { + ext.args = [ + ].join(' ') + ext.prefix = { "${meta.id}_D${meta.depth}" } + } +} diff --git a/conf/modules/validation.config b/conf/steps/validation.config similarity index 78% rename from conf/modules/validation.config rename to conf/steps/validation.config index 875195c1..1dd8867b 100644 --- a/conf/modules/validation.config +++ b/conf/steps/validation.config @@ -13,6 +13,10 @@ process { // Configuration for the validation step withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:BCFTOOLS_MPILEUP' { + publishDir = [ + path: { "${params.outdir}/validation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode + ] ext.args = [ "-I", "-E", @@ -24,9 +28,9 @@ process { ].join(' ') } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:*' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:.*' { publishDir = [ - path: { "${params.outdir}/validation" }, + path: { "${params.outdir}/validation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode ] } @@ -35,6 +39,10 @@ process { ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}" } } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:CONCATENATE' { + ext.suffix = { "txt" } + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:ADD_COLUMNS' { ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}_SNP" } } diff --git a/nextflow.config b/nextflow.config index 6d779aab..d3e0a380 100644 --- a/nextflow.config +++ b/nextflow.config @@ -269,17 +269,20 @@ manifest { // Load modules.config for DSL2 module specific options includeConfig 'conf/modules.config' +// initialisation step +includeConfig 'conf/steps/initialisation.config' + // simulation step -includeConfig 'conf/modules/simulation.config' +includeConfig 'conf/steps/simulation.config' // panel_prep step -includeConfig 'conf/modules/panel_prep.config' +includeConfig 'conf/steps/panel_prep.config' // imputation step -includeConfig 'conf/modules/imputation.config' +includeConfig 'conf/steps/imputation.config' // validation step -includeConfig 'conf/modules/validation.config' +includeConfig 'conf/steps/validation.config' // Function to ensure that resource requirements don't go beyond // a maximum limit From cca8155a6e967a09212cb91f01a0140f34085e84 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Fri, 12 Apr 2024 18:29:50 +0200 Subject: [PATCH 19/65] Add test for concordance analysis --- .../vcf_concordance_glimpse2/tests/main.nf.test | 12 ++++++------ .../vcf_concordance_glimpse2/tests/main.nf.test.snap | 8 ++++---- .../vcf_concordance_glimpse2/tests/nextflow.config | 9 +++------ tests/config/nf-test.config | 1 + 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test index d5db8efb..e1aca9a5 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test @@ -101,8 +101,8 @@ nextflow_workflow { allele_freq = Channel.fromList([ [ [panel:'1000GP', chr:'21'], // meta map - file(params.phaseimpute_testdata_path + "panel/21/1000GP.chr21.s.norel.bcf",checkIfExists:true), - file(params.phaseimpute_testdata_path + "panel/21/1000GP.chr21.s.norel.bcf.csi",checkIfExists:true) + file(params.phaseimpute_testdata_path + "panel/21/1000GP.chr21.s.norel.sites.vcf.gz",checkIfExists:true), + file(params.phaseimpute_testdata_path + "panel/21/1000GP.chr21.s.norel.sites.vcf.gz.csi",checkIfExists:true) ], [ [panel:'1000GP', chr:'22'], // meta map @@ -111,16 +111,16 @@ nextflow_workflow { ] ]) truth = Channel.fromList([ - [[id:'NA12878', chr:'21', region:'chr21:16650000-16700000'], // meta map + [[id:'NA12878', chr:'21', region:'chr21:16570000-16610000'], // meta map file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s.bcf",checkIfExists:true), file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s.bcf.csi",checkIfExists:true)], - [[id:'NA12878', chr:'22', region:'chr22:16650000-16700000'], // meta map + [[id:'NA12878', chr:'22', region:'chr22:16570000-16610000'], // meta map file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s.bcf",checkIfExists:true), file(params.phaseimpute_testdata_path + "individuals/NA12878/NA12878.s.bcf.csi",checkIfExists:true)], - [[id:'NA19401', chr:'21', region:'chr21:16650000-16700000'], // meta map + [[id:'NA19401', chr:'21', region:'chr21:16570000-16610000'], // meta map file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s.bcf",checkIfExists:true), file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s.bcf.csi",checkIfExists:true)], - [[id:'NA19401', chr:'22', region:'chr22:16650000-16700000'], // meta map + [[id:'NA19401', chr:'22', region:'chr22:16570000-16610000'], // meta map file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s.bcf",checkIfExists:true), file(params.phaseimpute_testdata_path + "individuals/NA19401/NA19401.s.bcf.csi",checkIfExists:true)] ]) diff --git a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap index ef151fc8..880fa8a2 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap @@ -20,7 +20,7 @@ "nf-test": "0.8.4", "nextflow": "23.10.1" }, - "timestamp": "2024-04-12T12:40:51.934575693" + "timestamp": "2024-04-12T16:24:33.217544644" }, "vcf_concordance_glimpse2": { "content": [ @@ -30,7 +30,7 @@ { "id": "TestQuality" }, - "TestQuality.txt:md5,b8ae89fd25598adc1f96b011a2a79646" + "TestQuality.txt:md5,865f1cf1a32256467010c10bfef1fa04" ] ], "1": [ @@ -41,7 +41,7 @@ { "id": "TestQuality" }, - "TestQuality.txt:md5,b8ae89fd25598adc1f96b011a2a79646" + "TestQuality.txt:md5,865f1cf1a32256467010c10bfef1fa04" ] ], "versions": [ @@ -53,6 +53,6 @@ "nf-test": "0.8.4", "nextflow": "23.10.1" }, - "timestamp": "2024-04-12T12:31:24.442985966" + "timestamp": "2024-04-12T16:22:59.476875738" } } \ No newline at end of file diff --git a/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config b/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config index b02ab26b..8b9e3c3f 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config @@ -1,11 +1,8 @@ params { max_memory = '7.GB' - withName: CONCATENATE { - ext.suffix = "txt" - } - withName: GAWK { - ext.suffix = "txt" - } +} + +process { withName: 'VCF_CONCORDANCE_GLIMPSE2:CONCATENATE' { ext.suffix = "txt" } diff --git a/tests/config/nf-test.config b/tests/config/nf-test.config index 417172e2..32ca7b47 100644 --- a/tests/config/nf-test.config +++ b/tests/config/nf-test.config @@ -3,6 +3,7 @@ params { singularity_pull_docker_container = false test_data_base = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules' modules_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/' + phaseimpute_testdata_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/' } process { From c91da9b43fc77f5791d38cf65ee6a430c70f7bfa Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Fri, 12 Apr 2024 18:34:05 +0200 Subject: [PATCH 20/65] Fix linting --- CHANGELOG.md | 2 +- conf/steps/initialisation.config | 40 ++++++++++++++++---------------- nextflow_schema.json | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f595d37..6ada2601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ Initial release of nf-core/phaseimpute, created with the [nf-core](https://nf-co - [#19](https://github.com/nf-core/phaseimpute/pull/19) - Changed reference panel to accept a csv, update modules and subworkflows (glimpse1/2 and shapeit5) - [#20](https://github.com/nf-core/phaseimpute/pull/20) - Added automatic detection of vcf contigs for the reference panel and automatic renaming available - [#22](https://github.com/nf-core/phaseimpute/pull/20) - Add validation step for concordance analysis. Input channels changed to - match inputs steps. + match inputs steps. Outdir folder organised by steps. ### `Fixed` diff --git a/conf/steps/initialisation.config b/conf/steps/initialisation.config index 4fef4265..6af04ad7 100644 --- a/conf/steps/initialisation.config +++ b/conf/steps/initialisation.config @@ -1,20 +1,20 @@ -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Config file for defining DSL2 per module options and publishing paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Available keys to override module options: - ext.args = Additional arguments appended to command in module. - ext.args2 = Second set of arguments appended to command in module (multi-tool modules). - ext.args3 = Third set of arguments appended to command in module (multi-tool modules). - ext.prefix = File name prefix for output files. ----------------------------------------------------------------------------------------- -*/ - -process { - withName: 'PIPELINE_INITIALISATION:.*' { - publishDir = [ - path: { "${params.outdir}/initialisation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode - ] - } -} +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + withName: 'PIPELINE_INITIALISATION:.*' { + publishDir = [ + path: { "${params.outdir}/initialisation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode + ] + } +} diff --git a/nextflow_schema.json b/nextflow_schema.json index 54d8e2ec..0fc57560 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -61,7 +61,7 @@ "description": "Arguments for the concordance analysis of the imputed data", "default": "", "properties": { - "input_truth" :{ + "input_truth": { "type": "string", "format": "file-path", "exists": true, From c5f9fdf101adf6087a98e1acace97beb533d9c6d Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Fri, 12 Apr 2024 19:18:54 +0200 Subject: [PATCH 21/65] Add check for extension (needs to be improved) --- .../utils_nfcore_phaseimpute_pipeline/main.nf | 47 +++++++++++++------ workflows/phaseimpute/main.nf | 12 ++--- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf index 62d52677..440e312b 100644 --- a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf @@ -116,28 +116,45 @@ workflow PIPELINE_INITIALISATION { } // Check if all extension are identical - ch_input + all_ext_input = ch_input .map{ it[1].split("\\.").last()} .distinct() - .view() - /*if (ch_input.map{ it[1].getBaseName().split("\\.").last() }.distinct().size() > 1) { + .toList() + .size() + println(all_ext_input.getClass()) + println(all_ext_input == 1) + /* + if (all_ext_input.size() != 1) { error "All input files must have the same extension" - } - */ + }*/ // // Create channel from input file provided through params.input_truth // - ch_input_truth = Channel - .fromSamplesheet("input_truth") - .map { - meta, file, index -> - [ meta, file, index ] + if (params.input_truth) { + if (params.input_truth.endsWith("csv")) { + ch_input_truth = Channel + .fromSamplesheet("input_truth") + .map { + meta, file, index -> + [ meta, file, index ] + } + // Check if all extension are identical + all_ext_input_truth = ch_input_truth + .map{ it[1].split("\\.").last()} + .distinct() + .collect() + .toList() + /* + if (all_ext_input_truth.size() > 1) { + error "All input truth files must have the same extension" + }*/ + } else { + // #TODO Wait for `oneOf()` to be supported in the nextflow_schema.json + error "Panel file provided is of another format than CSV (not yet supported). Please separate your panel by chromosome and use the samplesheet format." } - /* - // Check if all extension are identical - if (ch_input_truth.map{ it[1].getBaseName().split("\\.").last() }.distinct().size() > 1) { - error "All input files must have the same extension" - }*/ + } else { + ch_input_truth = Channel.of([[],[],[]]) + } // // Create channel for panel diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 00a3e83b..4d46e12b 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -158,12 +158,12 @@ workflow PHASEIMPUTE { if (params.step == 'validate' || params.step == 'all') { ch_truth_vcf = Channel.empty() // Check if all files are bam - /* - ch_input_validate_truth - .map{it[1].getBaseName().split("\\.").last() == "bam"} - .view() + all_ext_input_truth = ch_input_validate_truth + .map{ it[1].split("\\.").last()} + .distinct() + .collect() - if (ch_input_validate_truth.map{it[1].getBaseName().split("\\.").last() == "bam"}.toSet() == true) { + if (all_ext_input_truth == "bam") { // Compute truth genotypes likelihoods GL_TRUTH( ch_input_validate_truth, @@ -174,7 +174,7 @@ workflow PHASEIMPUTE { ch_truth_vcf = GL_TRUTH.out.vcf } else { ch_truth_vcf = ch_input_validate_truth - }*/ + } ch_truth_vcf = ch_input_validate_truth // Compute concordance analysis VCF_CONCORDANCE_GLIMPSE2( From 859b6e3b4dc5200b4272cf169c4fa99a77db9f16 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Tue, 16 Apr 2024 11:22:57 +0200 Subject: [PATCH 22/65] Add GL computation for bamg --- workflows/phaseimpute/main.nf | 41 +++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 4d46e12b..c6067bc3 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -12,6 +12,7 @@ include { paramsSummaryMap } from 'plugin/nf-validation' include { paramsSummaryMultiqc } from '../../subworkflows/nf-core/utils_nfcore_pipeline' include { softwareVersionsToYAML } from '../../subworkflows/nf-core/utils_nfcore_pipeline' include { methodsDescriptionText } from '../../subworkflows/local/utils_nfcore_phaseimpute_pipeline' +include { getAllFilesExtension } from '../../subworkflows/local/utils_nfcore_phaseimpute_pipeline' // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules @@ -157,25 +158,27 @@ workflow PHASEIMPUTE { if (params.step == 'validate' || params.step == 'all') { ch_truth_vcf = Channel.empty() - // Check if all files are bam - all_ext_input_truth = ch_input_validate_truth - .map{ it[1].split("\\.").last()} - .distinct() - .collect() - - if (all_ext_input_truth == "bam") { - // Compute truth genotypes likelihoods - GL_TRUTH( - ch_input_validate_truth, - ch_panel_sites_tsv, - ch_fasta - ) - ch_multiqc_files = ch_multiqc_files.mix(GL_TRUTH.out.multiqc_files) - ch_truth_vcf = GL_TRUTH.out.vcf - } else { - ch_truth_vcf = ch_input_validate_truth - } - ch_truth_vcf = ch_input_validate_truth + // Get extension of input files + truth_ext = getAllFilesExtension(ch_input_validate_truth) + + // Channels for branching + ch_input_validate_truth + .combine(truth_ext) + .branch { + bam: it[2] == 'bam' + vcf: it[2] == 'vcf' + } set { ch_truth } + + GL_TRUTH( + ch_truth.bam.map { [it[0], it[1], it[2]] }, + ch_panel_sites_tsv, + ch_fasta + ) + ch_multiqc_files = ch_multiqc_files.mix(GL_TRUTH.out.multiqc_files) + ch_truth_vcf = ch_truth.vcf + .map { [it[0], it[1], it[2]] } + .mix(GL_TRUTH.out.vcf) + // Compute concordance analysis VCF_CONCORDANCE_GLIMPSE2( ch_input_validate, From 4ee301e39c7fb078b7383236731586c611f6d507 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Tue, 16 Apr 2024 15:06:10 +0200 Subject: [PATCH 23/65] Add multiqc --- conf/steps/imputation.config | 4 +- conf/steps/validation.config | 5 +- nextflow.config | 1 + subworkflows/local/compute_gl/main.nf | 6 +- .../utils_nfcore_phaseimpute_pipeline/main.nf | 56 ++++++++++++------- .../local/vcf_concordance_glimpse2/main.nf | 23 ++++++-- workflows/phaseimpute/main.nf | 16 ++++-- 7 files changed, 78 insertions(+), 33 deletions(-) diff --git a/conf/steps/imputation.config b/conf/steps/imputation.config index d463afba..8b485e92 100644 --- a/conf/steps/imputation.config +++ b/conf/steps/imputation.config @@ -17,6 +17,8 @@ process { path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { ext.args = [ "-I", "-E", @@ -28,7 +30,7 @@ process { ].join(' ') } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:*' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { publishDir = [ path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode diff --git a/conf/steps/validation.config b/conf/steps/validation.config index 1dd8867b..7da707d7 100644 --- a/conf/steps/validation.config +++ b/conf/steps/validation.config @@ -12,11 +12,13 @@ process { // Configuration for the validation step - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:BCFTOOLS_MPILEUP' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:.*' { publishDir = [ path: { "${params.outdir}/validation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:BCFTOOLS_MPILEUP' { ext.args = [ "-I", "-E", @@ -37,6 +39,7 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}" } + ext.args = "--out-r2-per-site" } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:CONCATENATE' { diff --git a/nextflow.config b/nextflow.config index d3e0a380..6f456064 100644 --- a/nextflow.config +++ b/nextflow.config @@ -199,6 +199,7 @@ profiles { test_full { includeConfig 'conf/test_full.config' } test_sim { includeConfig 'conf/test_sim.config' } test_validate { includeConfig 'conf/test_validate.config' } + test_all { includeConfig 'conf/test_all.config' } } // Set default registry for Apptainer, Docker, Podman and Singularity independent of -profile diff --git a/subworkflows/local/compute_gl/main.nf b/subworkflows/local/compute_gl/main.nf index ba266f74..4f571159 100644 --- a/subworkflows/local/compute_gl/main.nf +++ b/subworkflows/local/compute_gl/main.nf @@ -14,10 +14,12 @@ workflow COMPUTE_GL { ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() - ch_mpileup = ch_input.map{metaICR, bam, bai -> [metaICR.subMap("chr"), metaICR, bam, bai]} + ch_mpileup = ch_input + .map{metaICR, bam, bai -> [metaICR.subMap("chr"), metaICR, bam, bai]} .combine(ch_target.map{metaPC, sites, tsv -> [metaPC.subMap("chr"), metaPC, sites, tsv]}, by:0) .map{metaC, metaICR, bam, bai, metaPC, sites, tsv -> - [metaICR + metaPC, bam, sites, tsv]} + [metaICR + metaPC, bam, sites, tsv] + } BCFTOOLS_MPILEUP( ch_mpileup, diff --git a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf index 440e312b..1515ae05 100644 --- a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf @@ -116,17 +116,7 @@ workflow PIPELINE_INITIALISATION { } // Check if all extension are identical - all_ext_input = ch_input - .map{ it[1].split("\\.").last()} - .distinct() - .toList() - .size() - println(all_ext_input.getClass()) - println(all_ext_input == 1) - /* - if (all_ext_input.size() != 1) { - error "All input files must have the same extension" - }*/ + getAllFilesExtension(ch_input) // // Create channel from input file provided through params.input_truth // @@ -139,21 +129,13 @@ workflow PIPELINE_INITIALISATION { [ meta, file, index ] } // Check if all extension are identical - all_ext_input_truth = ch_input_truth - .map{ it[1].split("\\.").last()} - .distinct() - .collect() - .toList() - /* - if (all_ext_input_truth.size() > 1) { - error "All input truth files must have the same extension" - }*/ + getAllFilesExtension(ch_input_truth) } else { // #TODO Wait for `oneOf()` to be supported in the nextflow_schema.json error "Panel file provided is of another format than CSV (not yet supported). Please separate your panel by chromosome and use the samplesheet format." } } else { - ch_input_truth = Channel.of([[],[],[]]) + ch_input_truth = Channel.empty() } // @@ -297,6 +279,38 @@ def validateInputParameters() { } } +// +// Check if all input files have the same extension +// +def getAllFilesExtension(ch_input) { + files_ext = ch_input + .map { + if (it[1] instanceof String) { + return it[1].split("\\.").last() + } else if (it[1] instanceof Path) { + return it[1].getName().split("\\.").last() + } else if (it[1] instanceof ArrayList) { + if (it[1] == []) { + return null + } else { + error "Array not supported" + } + } else { + println it[1].getClass() + error "Type not supported" + } + } // Extract files extensions + .toList() // Collect extensions into a list + .map { extensions -> + if (extensions.unique().size() != 1) { + println "Extensions: ${extensions}" + error "All input files must have the same extension" + } + return extensions[0] + } +} + + // // Validate channels from input samplesheet // diff --git a/subworkflows/local/vcf_concordance_glimpse2/main.nf b/subworkflows/local/vcf_concordance_glimpse2/main.nf index 09818e20..0d704726 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/main.nf +++ b/subworkflows/local/vcf_concordance_glimpse2/main.nf @@ -12,14 +12,20 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { main: - ch_versions = Channel.empty() + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() ch_concordance = ch_vcf_emul .map{ metaICRPST, vcf, csi -> [metaICRPST.subMap(["id", "chr", "region"]), metaICRPST, vcf, csi] } - .combine(ch_vcf_truth, by:0) + .combine(ch_vcf_truth.map{ + metaICRP, vcf, csi -> + [metaICRP.subMap(["id", "chr", "region"]), vcf, csi] + }, + by:0 + ) .map{metaICR, metaIPCRTS, emul, e_csi, truth, t_csi -> [metaICR.subMap(["chr"]), metaIPCRTS, emul, e_csi, truth, t_csi] } @@ -35,6 +41,14 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { [[], [], params.bins, [], []], params.min_val_gl, params.min_val_dp ) + + ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.errors_cal.map{meta, txt -> [txt]}) + ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.errors_grp.map{meta, txt -> [txt]}) + ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.errors_spl.map{meta, txt -> [txt]}) + ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.rsquare_grp.map{meta, txt -> [txt]}) + ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.rsquare_spl.map{meta, txt -> [txt]}) + ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.rsquare_per_site.map{meta, txt -> [txt]}) + GUNZIP(GLIMPSE2_CONCORDANCE.out.errors_grp) ADD_COLUMNS(GUNZIP.out.gunzip) @@ -48,6 +62,7 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { ) emit: - stats = CONCATENATE.out.output // [ meta, txt ] - versions = ch_versions // channel: [ versions.yml ] + stats = CONCATENATE.out.output // [ meta, txt ] + versions = ch_versions // channel: [ versions.yml ] + multiqc_files = ch_multiqc_files } diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index c6067bc3..39e7a81d 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -121,6 +121,7 @@ workflow PHASEIMPUTE { ch_fasta ) ch_multiqc_files = ch_multiqc_files.mix(GL_INPUT.out.multiqc_files) + ch_versions = ch_versions.mix(GL_INPUT.out.versions.first()) impute_input = GL_INPUT.out.vcf // [metaIPC, vcf, index] .map {metaIPC, vcf, index -> [metaIPC.subMap("panel", "chr"), metaIPC, vcf, index] } @@ -142,6 +143,8 @@ workflow PHASEIMPUTE { .combine(VCF_IMPUTE_GLIMPSE1.out.merged_variants_index, by: 0) .map{ metaIPCR, vcf, csi -> [metaIPCR + [tools: "Glimpse1"], vcf, csi] } ch_impute_output = ch_impute_output.mix(output_glimpse1) + ch_multiqc_files = ch_multiqc_files.mix(VCF_IMPUTE_GLIMPSE1.out.chunk_chr.map{ [it[1]]}) + ch_versions = ch_versions.mix(VCF_IMPUTE_GLIMPSE1.out.versions.first()) } if (params.tools.contains("glimpse2")) { error "Glimpse2 not yet implemented" @@ -162,12 +165,12 @@ workflow PHASEIMPUTE { truth_ext = getAllFilesExtension(ch_input_validate_truth) // Channels for branching - ch_input_validate_truth + ch_truth = ch_input_validate_truth .combine(truth_ext) .branch { - bam: it[2] == 'bam' - vcf: it[2] == 'vcf' - } set { ch_truth } + bam: it[3] == 'bam' + vcf: it[3] =~ 'vcf|bcf' + } GL_TRUTH( ch_truth.bam.map { [it[0], it[1], it[2]] }, @@ -175,6 +178,9 @@ workflow PHASEIMPUTE { ch_fasta ) ch_multiqc_files = ch_multiqc_files.mix(GL_TRUTH.out.multiqc_files) + ch_versions = ch_versions.mix(GL_TRUTH.out.versions.first()) + + // Mix the original vcf and the computed vcf ch_truth_vcf = ch_truth.vcf .map { [it[0], it[1], it[2]] } .mix(GL_TRUTH.out.vcf) @@ -185,6 +191,8 @@ workflow PHASEIMPUTE { ch_truth_vcf, ch_panel_sites ) + ch_multiqc_files = ch_multiqc_files.mix(VCF_CONCORDANCE_GLIMPSE2.out.multiqc_files) + ch_versions = ch_versions.mix(VCF_CONCORDANCE_GLIMPSE2.out.versions.first()) } if (params.step == 'refine') { From 4f792701fef9d38c0784d93b443103ec7e5a4dc0 Mon Sep 17 00:00:00 2001 From: Louis LE NEZET <58640615+LouisLeNezet@users.noreply.github.com> Date: Wed, 17 Apr 2024 09:54:59 +0200 Subject: [PATCH 24/65] Update CHANGELOG.md Co-authored-by: Anabella Trigila <18577080+atrigila@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ada2601..0af049a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ Initial release of nf-core/phaseimpute, created with the [nf-core](https://nf-co - [#19](https://github.com/nf-core/phaseimpute/pull/19) - Changed reference panel to accept a csv, update modules and subworkflows (glimpse1/2 and shapeit5) - [#20](https://github.com/nf-core/phaseimpute/pull/20) - Added automatic detection of vcf contigs for the reference panel and automatic renaming available - [#22](https://github.com/nf-core/phaseimpute/pull/20) - Add validation step for concordance analysis. Input channels changed to - match inputs steps. Outdir folder organised by steps. + match inputs steps. Outdir folder organised by steps. Modules config by subworkflows. ### `Fixed` From e0a0673ae0e3a8ea88337d01a7aa0b449c8c22e9 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 16:18:53 +0200 Subject: [PATCH 25/65] Remove glimpse concordance --- modules.json | 141 +++++++++++++----- .../glimpse/concordance/environment.yml | 7 - modules/nf-core/glimpse/concordance/main.nf | 65 -------- modules/nf-core/glimpse/concordance/meta.yml | 85 ----------- .../glimpse/concordance/tests/main.nf.test | 86 ----------- .../concordance/tests/main.nf.test.snap | 99 ------------ .../glimpse/concordance/tests/tags.yml | 2 - 7 files changed, 102 insertions(+), 383 deletions(-) delete mode 100644 modules/nf-core/glimpse/concordance/environment.yml delete mode 100644 modules/nf-core/glimpse/concordance/main.nf delete mode 100644 modules/nf-core/glimpse/concordance/meta.yml delete mode 100644 modules/nf-core/glimpse/concordance/tests/main.nf.test delete mode 100644 modules/nf-core/glimpse/concordance/tests/main.nf.test.snap delete mode 100644 modules/nf-core/glimpse/concordance/tests/tags.yml diff --git a/modules.json b/modules.json index 43bfae97..e9976b30 100644 --- a/modules.json +++ b/modules.json @@ -8,147 +8,198 @@ "bcftools/annotate": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": ["modules"], + "installed_by": [ + "modules" + ], "patch": "modules/nf-core/bcftools/annotate/bcftools-annotate.diff" }, "bcftools/index": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": ["multiple_impute_glimpse2", "vcf_impute_glimpse", "vcf_phase_shapeit5"] + "installed_by": [ + "multiple_impute_glimpse2", + "vcf_impute_glimpse", + "vcf_phase_shapeit5" + ] }, "bcftools/mpileup": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": ["modules"], + "installed_by": [ + "modules" + ], "patch": "modules/nf-core/bcftools/mpileup/bcftools-mpileup.diff" }, "bcftools/norm": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "bcftools/query": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "bcftools/view": { "branch": "master", "git_sha": "1013101da4252623fd7acf19cc581bae91d4f839", - "installed_by": ["modules"], + "installed_by": [ + "modules" + ], "patch": "modules/nf-core/bcftools/view/bcftools-view.diff" }, "bedtools/makewindows": { "branch": "master", "git_sha": "3b248b84694d1939ac4bb33df84bf6233a34d668", - "installed_by": ["vcf_phase_shapeit5"] + "installed_by": [ + "vcf_phase_shapeit5" + ] }, "custom/dumpsoftwareversions": { "branch": "master", "git_sha": "de45447d060b8c8b98575bc637a4a575fd0638e1", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "gawk": { "branch": "master", "git_sha": "da4d05d04e65227d4307e87940842f1a14de62c7", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "glimpse/chunk": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": ["vcf_impute_glimpse"] - }, - "glimpse/concordance": { - "branch": "master", - "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": ["modules"] + "installed_by": [ + "vcf_impute_glimpse" + ] }, "glimpse/ligate": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": ["vcf_impute_glimpse"] + "installed_by": [ + "vcf_impute_glimpse" + ] }, "glimpse/phase": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": ["vcf_impute_glimpse"] + "installed_by": [ + "vcf_impute_glimpse" + ] }, "glimpse2/chunk": { "branch": "master", "git_sha": "14ba46490cae3c78ed8e8f48d2c0f8f3be1e7c03", - "installed_by": ["multiple_impute_glimpse2"] + "installed_by": [ + "multiple_impute_glimpse2" + ] }, "glimpse2/concordance": { "branch": "master", "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "glimpse2/ligate": { "branch": "master", "git_sha": "ee7fee68281944b002bd27a8ff3f19200b4d3fad", - "installed_by": ["multiple_impute_glimpse2"] + "installed_by": [ + "multiple_impute_glimpse2" + ] }, "glimpse2/phase": { "branch": "master", "git_sha": "9c71d32e372650e8bb3e1fb15339017aad5e3f7f", - "installed_by": ["multiple_impute_glimpse2"] + "installed_by": [ + "multiple_impute_glimpse2" + ] }, "glimpse2/splitreference": { "branch": "master", "git_sha": "fa12139827a18b324bd63fce654818586a8e9cc7", - "installed_by": ["multiple_impute_glimpse2"] + "installed_by": [ + "multiple_impute_glimpse2" + ] }, "gunzip": { "branch": "master", "git_sha": "3a5fef109d113b4997c9822198664ca5f2716208", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "multiqc": { "branch": "master", "git_sha": "b7ebe95761cd389603f9cc0e0dc384c0f663815a", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "samtools/coverage": { "branch": "master", "git_sha": "38afbe42f7db7f19c7a89607c0a71c68f3be3131", - "installed_by": ["modules"], + "installed_by": [ + "modules" + ], "patch": "modules/nf-core/samtools/coverage/samtools-coverage.diff" }, "samtools/faidx": { "branch": "master", "git_sha": "f153f1f10e1083c49935565844cccb7453021682", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "samtools/index": { "branch": "master", "git_sha": "f4596fe0bdc096cf53ec4497e83defdb3a94ff62", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "samtools/view": { "branch": "master", "git_sha": "0bd7d2333a88483aa0476acea172e9f5f6dd83bb", - "installed_by": ["modules"], + "installed_by": [ + "modules" + ], "patch": "modules/nf-core/samtools/view/samtools-view.diff" }, "shapeit5/ligate": { "branch": "master", "git_sha": "dcf17cc0ed8fd5ea57e61a13e0147cddb5c1ee30", - "installed_by": ["vcf_phase_shapeit5"] + "installed_by": [ + "vcf_phase_shapeit5" + ] }, "shapeit5/phasecommon": { "branch": "master", "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", - "installed_by": ["vcf_phase_shapeit5"] + "installed_by": [ + "vcf_phase_shapeit5" + ] }, "tabix/bgzip": { "branch": "master", "git_sha": "09d3c8c29b31a2dfd610305b10550f0e1dbcd4a9", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] }, "tabix/tabix": { "branch": "master", "git_sha": "9502adb23c0b97ed8e616bbbdfa73b4585aec9a1", - "installed_by": ["modules"] + "installed_by": [ + "modules" + ] } } }, @@ -157,35 +208,47 @@ "multiple_impute_glimpse2": { "branch": "master", "git_sha": "cfd937a668919d948f6fcbf4218e79de50c2f36f", - "installed_by": ["subworkflows"] + "installed_by": [ + "subworkflows" + ] }, "utils_nextflow_pipeline": { "branch": "master", "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", - "installed_by": ["subworkflows"] + "installed_by": [ + "subworkflows" + ] }, "utils_nfcore_pipeline": { "branch": "master", "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", - "installed_by": ["subworkflows"] + "installed_by": [ + "subworkflows" + ] }, "utils_nfvalidation_plugin": { "branch": "master", "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", - "installed_by": ["subworkflows"] + "installed_by": [ + "subworkflows" + ] }, "vcf_impute_glimpse": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": ["subworkflows"] + "installed_by": [ + "subworkflows" + ] }, "vcf_phase_shapeit5": { "branch": "master", "git_sha": "dcf17cc0ed8fd5ea57e61a13e0147cddb5c1ee30", - "installed_by": ["subworkflows"] + "installed_by": [ + "subworkflows" + ] } } } } } -} +} \ No newline at end of file diff --git a/modules/nf-core/glimpse/concordance/environment.yml b/modules/nf-core/glimpse/concordance/environment.yml deleted file mode 100644 index 739ab78d..00000000 --- a/modules/nf-core/glimpse/concordance/environment.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: glimpse_concordance -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::glimpse-bio=1.1.1 diff --git a/modules/nf-core/glimpse/concordance/main.nf b/modules/nf-core/glimpse/concordance/main.nf deleted file mode 100644 index 48785dd3..00000000 --- a/modules/nf-core/glimpse/concordance/main.nf +++ /dev/null @@ -1,65 +0,0 @@ -process GLIMPSE_CONCORDANCE { - tag "$meta.id" - label 'process_low' - - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/glimpse-bio:1.1.1--hce55b13_1': - 'biocontainers/glimpse-bio:1.1.1--hce55b13_1' }" - - input: - tuple val(meta), path(estimate), path(estimate_index), path(freq), path(freq_index), path(truth), path(truth_index), val(region) - val(min_prob) - val(min_dp) - val(bins) - - output: - tuple val(meta), path("*.error.cal.txt.gz") , emit: errors_cal - tuple val(meta), path("*.error.grp.txt.gz") , emit: errors_grp - tuple val(meta), path("*.error.spl.txt.gz") , emit: errors_spl - tuple val(meta), path("*.rsquare.grp.txt.gz"), emit: rsquare_grp - tuple val(meta), path("*.rsquare.spl.txt.gz"), emit: rsquare_spl - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - def min_prob_cmd = min_prob ? "--minPROB ${min_prob}" : "--minPROB 0.9999" - def min_dp_cmd = min_dp ? "--minDP ${min_dp}" : "--minDP 8" - def bins_cmd = bins ? "--bins ${bins}" : "--bins 0.00000 0.00100 0.00200 0.00500 0.01000 0.05000 0.10000 0.20000 0.50000" - """ - echo $region $freq $truth $estimate > input.txt - GLIMPSE_concordance \\ - $args \\ - --input input.txt \\ - --thread $task.cpus \\ - --output ${prefix} \\ - $min_prob_cmd \\ - $min_dp_cmd \\ - $bins_cmd - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - glimpse: "\$(GLIMPSE_concordance --help | sed -nr '/Version/p' | grep -o -E '([0-9]+.){1,2}[0-9]')" - END_VERSIONS - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - def args = task.ext.args ?: "" - """ - touch ${prefix}.error.cal.txt.gz - touch ${prefix}.error.grp.txt.gz - touch ${prefix}.error.spl.txt.gz - touch ${prefix}.rsquare.grp.txt.gz - touch ${prefix}.rsquare.spl.txt.gz - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - glimpse: "\$(GLIMPSE_concordance --help | sed -nr '/Version/p' | grep -o -E '([0-9]+.){1,2}[0-9]')" - END_VERSIONS - """ -} diff --git a/modules/nf-core/glimpse/concordance/meta.yml b/modules/nf-core/glimpse/concordance/meta.yml deleted file mode 100644 index 2b2d7195..00000000 --- a/modules/nf-core/glimpse/concordance/meta.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: "glimpse_concordance" -description: Compute the r2 correlation between imputed dosages (in MAF bins) and highly-confident genotype calls from the high-coverage dataset. -keywords: - - concordance - - low-coverage - - glimpse - - imputation -tools: - - "glimpse": - description: "GLIMPSE is a phasing and imputation method for large-scale low-coverage sequencing studies." - homepage: "https://odelaneau.github.io/GLIMPSE" - documentation: "https://odelaneau.github.io/GLIMPSE/commands.html" - tool_dev_url: "https://github.com/odelaneau/GLIMPSE" - doi: "10.1038/s41588-020-00756-0" - licence: ["MIT"] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - region: - type: string - description: Target region used for imputation, including left and right buffers (e.g. chr20:1000000-2000000). - pattern: "chrXX:leftBufferPosition-rightBufferPosition" - - freq: - type: file - description: File containing allele frequencies at each site. - pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" - - truth: - type: file - description: Validation dataset called at the same positions as the imputed file. - pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" - - estimate: - type: file - description: Imputed data. - pattern: "*.{vcf,bcf,vcf.gz,bcf.gz}" - - min_prob: - type: float - description: Minimum posterior probability P(G|R) in validation data - - min_dp: - type: integer - description: | - Minimum coverage in validation data. - If FORMAT/DP is missing and --minDP > 0, the program exits with an error. - - bins: - type: string - description: | - Allele frequency bins used for rsquared computations. - By default they should as MAF bins [0-0.5], while - they should take the full range [0-1] if --use-ref-alt is used. -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" - - errors_cal: - type: file - description: Calibration correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. - pattern: "*.errors.cal.txt.gz" - - errors_grp: - type: file - description: Groups correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. - pattern: "*.errors.grp.txt.gz" - - errors_spl: - type: file - description: Samples correlation errors between imputed dosages (in MAF bins) and highly-confident genotype. - pattern: "*.errors.spl.txt.gz" - - rsquared_grp: - type: file - description: Groups r-squared correlation between imputed dosages (in MAF bins) and highly-confident genotype. - pattern: "*.rsquare.grp.txt.gz" - - rsquared_spl: - type: file - description: Samples r-squared correlation between imputed dosages (in MAF bins) and highly-confident genotype. - pattern: "*.rsquare.spl.txt.gz" -authors: - - "@louislenezet" -maintainers: - - "@louislenezet" diff --git a/modules/nf-core/glimpse/concordance/tests/main.nf.test b/modules/nf-core/glimpse/concordance/tests/main.nf.test deleted file mode 100644 index 7e850535..00000000 --- a/modules/nf-core/glimpse/concordance/tests/main.nf.test +++ /dev/null @@ -1,86 +0,0 @@ -nextflow_process { - - name "Test Process GLIMPSE_CONCORDANCE" - script "../main.nf" - process "GLIMPSE_CONCORDANCE" - - tag "modules" - tag "modules_nfcore" - tag "glimpse" - tag "glimpse/concordance" - tag "glimpse/phase" - tag "bcftools/index" - - test("test_glimpse_concordance") { - setup { - run("GLIMPSE_PHASE") { - script "../../phase/main.nf" - process { - """ - ch_sample = Channel.of('NA12878 2').collectFile(name: 'sampleinfos.txt') - region = Channel.fromList([ - ["chr21:16600000-16750000","chr21:16650000-16700000"] - ]) - input_vcf = Channel.of([ - [ id:'input'], // meta map - file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.1x.vcf.gz", checkIfExists: true), - file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.1x.vcf.gz.csi", checkIfExists: true) - ]) - ref_panel = Channel.of([ - file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.bcf", checkIfExists: true), - file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.bcf.csi", checkIfExists: true) - ]) - ch_map = Channel.of([ - file(params.modules_testdata_base_path + "delete_me/glimpse/chr21.b38.gmap.gz", checkIfExists: true), - ]) - - input[0] = input_vcf - | combine(ch_sample) - | combine(region) - | combine(ref_panel) - | combine(ch_map) - """ - } - } - run("BCFTOOLS_INDEX") { - script "../../../bcftools/index/main.nf" - process { - """ - input[0] = GLIMPSE_PHASE.out.phased_variants - """ - } - } - } - when { - process { - """ - allele_freq = Channel.fromList([ - file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.sites.vcf.gz",checkIfExists:true), - file(params.modules_testdata_base_path + "delete_me/glimpse/1000GP.chr21.noNA12878.s.sites.vcf.gz.csi",checkIfExists:true) - ]).collect() - truth = Channel.fromList([ - file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.bcf",checkIfExists:true), - file(params.modules_testdata_base_path + "delete_me/glimpse/NA12878.chr21.s.bcf.csi",checkIfExists:true) - ]).collect() - estimate = GLIMPSE_PHASE.out.phased_variants - | join (BCFTOOLS_INDEX.out.csi) - input[0] = estimate - | combine (allele_freq) - | combine (truth) - | combine (["chr21"]) - input[1] = [] - input[2] = [] - input[3] = [] - """ - } - } - - then { - assertAll( - { assert process.success }, - { assert snapshot(process.out).match() } - ) - } - - } -} diff --git a/modules/nf-core/glimpse/concordance/tests/main.nf.test.snap b/modules/nf-core/glimpse/concordance/tests/main.nf.test.snap deleted file mode 100644 index c0838446..00000000 --- a/modules/nf-core/glimpse/concordance/tests/main.nf.test.snap +++ /dev/null @@ -1,99 +0,0 @@ -{ - "test_glimpse_concordance": { - "content": [ - { - "0": [ - [ - { - "id": "input" - }, - "input.error.cal.txt.gz:md5,15c6a120d9fd3ac8c0ff6a6aedc76571" - ] - ], - "1": [ - [ - { - "id": "input" - }, - "input.error.grp.txt.gz:md5,532bec52c03f16dcd6cc6d2b7c26673b" - ] - ], - "2": [ - [ - { - "id": "input" - }, - "input.error.spl.txt.gz:md5,35cb463e8db41e2180f21941ab0324e0" - ] - ], - "3": [ - [ - { - "id": "input" - }, - "input.rsquare.grp.txt.gz:md5,15bc7bf7980fd63e0f09bd267e548b57" - ] - ], - "4": [ - [ - { - "id": "input" - }, - "input.rsquare.spl.txt.gz:md5,55659f466775d828ee1ba723464bb460" - ] - ], - "5": [ - "versions.yml:md5,f79c864118d03a4afa93082c46c0d608" - ], - "errors_cal": [ - [ - { - "id": "input" - }, - "input.error.cal.txt.gz:md5,15c6a120d9fd3ac8c0ff6a6aedc76571" - ] - ], - "errors_grp": [ - [ - { - "id": "input" - }, - "input.error.grp.txt.gz:md5,532bec52c03f16dcd6cc6d2b7c26673b" - ] - ], - "errors_spl": [ - [ - { - "id": "input" - }, - "input.error.spl.txt.gz:md5,35cb463e8db41e2180f21941ab0324e0" - ] - ], - "rsquare_grp": [ - [ - { - "id": "input" - }, - "input.rsquare.grp.txt.gz:md5,15bc7bf7980fd63e0f09bd267e548b57" - ] - ], - "rsquare_spl": [ - [ - { - "id": "input" - }, - "input.rsquare.spl.txt.gz:md5,55659f466775d828ee1ba723464bb460" - ] - ], - "versions": [ - "versions.yml:md5,f79c864118d03a4afa93082c46c0d608" - ] - } - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-03-18T23:37:00.537398654" - } -} \ No newline at end of file diff --git a/modules/nf-core/glimpse/concordance/tests/tags.yml b/modules/nf-core/glimpse/concordance/tests/tags.yml deleted file mode 100644 index e636bb70..00000000 --- a/modules/nf-core/glimpse/concordance/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -glimpse/concordance: - - modules/nf-core/glimpse/concordance/** From 3417a62bc87c6281fa0fa0abab573e97ee2e324c Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 16:19:08 +0200 Subject: [PATCH 26/65] Check works done --- docs/development.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/development.md b/docs/development.md index d28de870..6c4b74a1 100644 --- a/docs/development.md +++ b/docs/development.md @@ -5,8 +5,8 @@ - [x] Add automatic detection of chromosome name to create a renaming file for the vcf files - [] Add automatic detection of chromosome name to create a renaming file for the bam files - [] Make the different tests workflows work - - [] Simulation - - [] Validation + - [x] Simulation + - [x] Validation - [] Preprocessing - [x] Imputation - [] Validation @@ -15,7 +15,7 @@ - [] Add nf-test for all modules and subworkflows - [] Remove all TODOs - [] Check if panel is necessary depending on the tool selected -- [] Set modules configuration as full path workflow:subworkflow:module +- [x] Set modules configuration as full path workflow:subworkflow:module - [] Where should the map file go (separate csv or in panel csv) ## Run tests @@ -23,6 +23,8 @@ ```bash nextflow run main.nf -profile singularity,test --outdir results -resume nextflow run main.nf -profile singularity,test_sim --outdir results -resume +nextflow run main.nf -profile singularity,test_validate --outdir results -resume +nextflow run main.nf -profile singularity,test_all --outdir results -resume ``` ## Problematic From fa71cd26fc14d4bb44bab7adf3ac603e29e13c3d Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 16:19:50 +0200 Subject: [PATCH 27/65] Remove panel index Add default values to schema --- nextflow.config | 9 ++++----- nextflow_schema.json | 15 ++++++--------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/nextflow.config b/nextflow.config index 6f456064..6285d2f9 100644 --- a/nextflow.config +++ b/nextflow.config @@ -20,7 +20,6 @@ params { // Panel preparation panel = null - panel_index = null phased = null rename_chr = false @@ -195,11 +194,11 @@ profiles { executor.cpus = 4 executor.memory = 8.GB } - test { includeConfig 'conf/test.config' } - test_full { includeConfig 'conf/test_full.config' } - test_sim { includeConfig 'conf/test_sim.config' } + test { includeConfig 'conf/test.config' } + test_full { includeConfig 'conf/test_full.config' } + test_sim { includeConfig 'conf/test_sim.config' } test_validate { includeConfig 'conf/test_validate.config' } - test_all { includeConfig 'conf/test_all.config' } + test_all { includeConfig 'conf/test_all.config' } } // Set default registry for Apptainer, Docker, Podman and Singularity independent of -profile diff --git a/nextflow_schema.json b/nextflow_schema.json index 0fc57560..47a63f44 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -41,13 +41,6 @@ "pattern": "^\\S+\\.(csv|tsv|txt)$", "mimetype": "text/csv" }, - "panel_index": { - "type": "string", - "description": "Path to the reference panel index file", - "fa_icon": "far fa-file-code", - "format": "file-path", - "pattern": "^\\S+\\.(vcf|bcf)(\\.gz)?\\.(csi|tbi)$" - }, "phased": { "description": "Is the reference panel phased", "type": "boolean", @@ -75,16 +68,19 @@ "bins": { "type": "string", "description": "User-defined allele count bins used for rsquared computations.", + "default": "0 0.01 0.05 0.1 0.2 0.5", "pattern": "^(\\d+(\\.\\d+)? )+(\\d+(\\.\\d+)?)$" }, "min_val_gl": { "type": "number", "description": "Minimum genotype likelihood probability P(G|R) in validation data. Set to zero to have no filter of if using –gt-validation", + "default": 0.9, "pattern": "^\\d+(\\.\\d+)?$" }, "min_val_dp": { - "description": "Minimum coverage in validation data. If FORMAT/DP is missing and –min_val_dp > 0, the program exits with an error. Set to zero to have no filter of if using –gt-validation", "type": "integer", + "description": "Minimum coverage in validation data. If FORMAT/DP is missing and –min_val_dp > 0, the program exits with an error. Set to zero to have no filter of if using –gt-validation", + "default": 5, "pattern": "^\\d+$" } } @@ -109,8 +105,9 @@ }, "input_region": { "type": "string", - "description": "Region of the genome to use, can be the entire genome (i.e. 'all') or a specific chromosome or region (e.g. 'chr1', 'chr1:1000-2000'). You can also specify a file containing a list of regions to process, one per line. The file should be a comma-separated file with 3 columns, and a header row.", + "description": "Region of the genome to use (optional: if no file given, the whole genome will be used). The file should be a comma-separated file with 3 columns, and a header row.", "schema": "assets/schema_input_region.json", + "default": null, "format": "file-path", "pattern": "^\\S+\\.csv$" }, From dcda44a298754662914a689d81d541513628279c Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 16:20:29 +0200 Subject: [PATCH 28/65] Fix regions usage with null values for all genome analysis --- main.nf | 4 ++++ .../local/utils_nfcore_phaseimpute_pipeline/main.nf | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/main.nf b/main.nf index 4593f061..319c2f5c 100644 --- a/main.nf +++ b/main.nf @@ -54,6 +54,10 @@ workflow NFCORE_PHASEIMPUTE { if (params.step == "impute") { input_impute = ch_input + .combine(ch_regions) + .map { metaI, file, index, metaCR, region -> + [ metaI+metaCR, file, index ] + } } else if (params.step == "simulate" || params.step == "all") { input_simulate = ch_input } else if (params.step == "validate") { diff --git a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf index 1515ae05..3d5c3ca4 100644 --- a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf @@ -162,16 +162,16 @@ workflow PIPELINE_INITIALISATION { ch_regions = Channel.fromSamplesheet("input_region") .map{ chr, start, end -> [["chr": chr], chr + ":" + start + "-" + end]} .map{ metaC, region -> [metaC + ["region": region], region]} - } else { - error "Region file provided is of another format than CSV (not yet supported). Please separate your reference genome by chromosome and use the samplesheet format." - /* #TODO Wait for `oneOf()` to be supported in the nextflow_schema.json + } else if (params.input_region == null){ + // #TODO Add support for string input GET_REGION ( - params.input_region, + "all", ch_ref_gen ) ch_versions = ch_versions.mix(GET_REGION.out.versions.first()) ch_regions = GET_REGION.out.regions - */ + } else { + error "Region file provided is of another format than CSV (not yet supported). Please separate your reference genome by chromosome and use the samplesheet format." } // From afe3a956a39f86f02f684933729a20d45abd7b2d Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 18:28:15 +0200 Subject: [PATCH 29/65] Disable unecessary output results --- conf/steps/initialisation.config | 3 ++- conf/steps/panel_prep.config | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/conf/steps/initialisation.config b/conf/steps/initialisation.config index 6af04ad7..7cc8daf5 100644 --- a/conf/steps/initialisation.config +++ b/conf/steps/initialisation.config @@ -14,7 +14,8 @@ process { withName: 'PIPELINE_INITIALISATION:.*' { publishDir = [ path: { "${params.outdir}/initialisation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode + mode: params.publish_dir_mode, + enabled: false ] } } diff --git a/conf/steps/panel_prep.config b/conf/steps/panel_prep.config index fe71fd1a..2814b96d 100644 --- a/conf/steps/panel_prep.config +++ b/conf/steps/panel_prep.config @@ -14,7 +14,8 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CHR_CHECK:.*' { publishDir = [ path: { "${params.outdir}/prep_panel/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode + mode: params.publish_dir_mode, + enabled: false ] } @@ -29,7 +30,8 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:.*' { publishDir = [ path: { "${params.outdir}/prep_panel/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode + mode: params.publish_dir_mode, + enabled: false ] } @@ -81,11 +83,14 @@ process { ext.prefix = { "${meta.id}_SITES_TSV" } } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_PHASE_SHAPEIT5:BEDTOOLS_MAKEWINDOWS' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:VCF_PHASE_SHAPEIT5:BEDTOOLS_MAKEWINDOWS' { ext.args = [ '-w 60000', '-s 40000' ].join(' ') ext.prefix = { "${meta.id}_chunks" } + publishDir = [ + enabled: false + ] } } From 6b208cd222d179fc0ce8c9419a9cd3d38433854a Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 18:28:36 +0200 Subject: [PATCH 30/65] Simplify channel --- subworkflows/local/get_panel/main.nf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/subworkflows/local/get_panel/main.nf b/subworkflows/local/get_panel/main.nf index d4a03a33..b1c2bf92 100644 --- a/subworkflows/local/get_panel/main.nf +++ b/subworkflows/local/get_panel/main.nf @@ -72,8 +72,7 @@ workflow GET_PANEL { ch_panel_phased = VCF_PHASE_SHAPEIT5.out.variants_phased .combine(VCF_PHASE_SHAPEIT5.out.variants_index, by: 0) } else { - ch_panel_phased = VIEW_VCF_SNPS.out.vcf - .combine(VCF_INDEX3.out.csi, by: 0) + ch_panel_phased = vcf_region } ch_panel = ch_panel_norm From 559f63a982ad2f468d92e973d6528fc2ef6bd8d3 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 18:29:06 +0200 Subject: [PATCH 31/65] Test for only bam files for simulation process --- workflows/phaseimpute/main.nf | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 39e7a81d..bfc0257a 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -58,6 +58,14 @@ workflow PHASEIMPUTE { // Output channel of simulate process ch_sim_output = Channel.empty() + // Test if the input are all bam files + input_ext = getAllFilesExtension(ch_input_sim) + + // Channels for branching + if (input_ext.size() != 1 && input_ext[0] != 'bam') { + error "All input files must be in BAM format to perform simulation" + } + // Split the bam into the region specified BAM_REGION(ch_input_sim, ch_region, ch_fasta) @@ -72,9 +80,8 @@ workflow PHASEIMPUTE { ch_fasta ) ch_versions = ch_versions.mix(BAM_DOWNSAMPLE.out.versions.first()) - - ch_input_impute = ch_input_impute.mix(BAM_DOWNSAMPLE.out.bam_emul) - ch_input_validate_truth = ch_input_validate_truth.mix(BAM_REGION.out.bam_region) + ch_input_impute = BAM_DOWNSAMPLE.out.bam_emul + ch_input_validate_truth = BAM_REGION.out.bam_region } if (params.genotype) { @@ -113,7 +120,6 @@ workflow PHASEIMPUTE { ch_impute_output = Channel.empty() if (params.tools.contains("glimpse1")) { println "Impute with Glimpse1" - // Glimpse1 subworkflow GL_INPUT( // Compute GL for input data once per panel ch_input_impute, From 4aa7c550cd8b4e24d870596d29754a2690a399ee Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 18:29:17 +0200 Subject: [PATCH 32/65] Update tools --- modules.json | 136 +++++++++++++-------------------------------------- 1 file changed, 34 insertions(+), 102 deletions(-) diff --git a/modules.json b/modules.json index e9976b30..ae854fb5 100644 --- a/modules.json +++ b/modules.json @@ -8,198 +8,142 @@ "bcftools/annotate": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": [ - "modules" - ], + "installed_by": ["modules"], "patch": "modules/nf-core/bcftools/annotate/bcftools-annotate.diff" }, "bcftools/index": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": [ - "multiple_impute_glimpse2", - "vcf_impute_glimpse", - "vcf_phase_shapeit5" - ] + "installed_by": ["multiple_impute_glimpse2", "vcf_impute_glimpse", "vcf_phase_shapeit5"] }, "bcftools/mpileup": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": [ - "modules" - ], + "installed_by": ["modules"], "patch": "modules/nf-core/bcftools/mpileup/bcftools-mpileup.diff" }, "bcftools/norm": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "bcftools/query": { "branch": "master", "git_sha": "44096c08ffdbc694f5f92ae174ea0f7ba0f37e09", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "bcftools/view": { "branch": "master", "git_sha": "1013101da4252623fd7acf19cc581bae91d4f839", - "installed_by": [ - "modules" - ], + "installed_by": ["modules"], "patch": "modules/nf-core/bcftools/view/bcftools-view.diff" }, "bedtools/makewindows": { "branch": "master", "git_sha": "3b248b84694d1939ac4bb33df84bf6233a34d668", - "installed_by": [ - "vcf_phase_shapeit5" - ] + "installed_by": ["vcf_phase_shapeit5"] }, "custom/dumpsoftwareversions": { "branch": "master", "git_sha": "de45447d060b8c8b98575bc637a4a575fd0638e1", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "gawk": { "branch": "master", "git_sha": "da4d05d04e65227d4307e87940842f1a14de62c7", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "glimpse/chunk": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": [ - "vcf_impute_glimpse" - ] + "installed_by": ["vcf_impute_glimpse"] }, "glimpse/ligate": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": [ - "vcf_impute_glimpse" - ] + "installed_by": ["vcf_impute_glimpse"] }, "glimpse/phase": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": [ - "vcf_impute_glimpse" - ] + "installed_by": ["vcf_impute_glimpse"] }, "glimpse2/chunk": { "branch": "master", "git_sha": "14ba46490cae3c78ed8e8f48d2c0f8f3be1e7c03", - "installed_by": [ - "multiple_impute_glimpse2" - ] + "installed_by": ["multiple_impute_glimpse2"] }, "glimpse2/concordance": { "branch": "master", "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "glimpse2/ligate": { "branch": "master", "git_sha": "ee7fee68281944b002bd27a8ff3f19200b4d3fad", - "installed_by": [ - "multiple_impute_glimpse2" - ] + "installed_by": ["multiple_impute_glimpse2"] }, "glimpse2/phase": { "branch": "master", "git_sha": "9c71d32e372650e8bb3e1fb15339017aad5e3f7f", - "installed_by": [ - "multiple_impute_glimpse2" - ] + "installed_by": ["multiple_impute_glimpse2"] }, "glimpse2/splitreference": { "branch": "master", "git_sha": "fa12139827a18b324bd63fce654818586a8e9cc7", - "installed_by": [ - "multiple_impute_glimpse2" - ] + "installed_by": ["multiple_impute_glimpse2"] }, "gunzip": { "branch": "master", "git_sha": "3a5fef109d113b4997c9822198664ca5f2716208", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "multiqc": { "branch": "master", "git_sha": "b7ebe95761cd389603f9cc0e0dc384c0f663815a", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "samtools/coverage": { "branch": "master", "git_sha": "38afbe42f7db7f19c7a89607c0a71c68f3be3131", - "installed_by": [ - "modules" - ], + "installed_by": ["modules"], "patch": "modules/nf-core/samtools/coverage/samtools-coverage.diff" }, "samtools/faidx": { "branch": "master", "git_sha": "f153f1f10e1083c49935565844cccb7453021682", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "samtools/index": { "branch": "master", "git_sha": "f4596fe0bdc096cf53ec4497e83defdb3a94ff62", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "samtools/view": { "branch": "master", "git_sha": "0bd7d2333a88483aa0476acea172e9f5f6dd83bb", - "installed_by": [ - "modules" - ], + "installed_by": ["modules"], "patch": "modules/nf-core/samtools/view/samtools-view.diff" }, "shapeit5/ligate": { "branch": "master", "git_sha": "dcf17cc0ed8fd5ea57e61a13e0147cddb5c1ee30", - "installed_by": [ - "vcf_phase_shapeit5" - ] + "installed_by": ["vcf_phase_shapeit5"] }, "shapeit5/phasecommon": { "branch": "master", "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", - "installed_by": [ - "vcf_phase_shapeit5" - ] + "installed_by": ["vcf_phase_shapeit5"] }, "tabix/bgzip": { "branch": "master", "git_sha": "09d3c8c29b31a2dfd610305b10550f0e1dbcd4a9", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] }, "tabix/tabix": { "branch": "master", "git_sha": "9502adb23c0b97ed8e616bbbdfa73b4585aec9a1", - "installed_by": [ - "modules" - ] + "installed_by": ["modules"] } } }, @@ -208,47 +152,35 @@ "multiple_impute_glimpse2": { "branch": "master", "git_sha": "cfd937a668919d948f6fcbf4218e79de50c2f36f", - "installed_by": [ - "subworkflows" - ] + "installed_by": ["subworkflows"] }, "utils_nextflow_pipeline": { "branch": "master", "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", - "installed_by": [ - "subworkflows" - ] + "installed_by": ["subworkflows"] }, "utils_nfcore_pipeline": { "branch": "master", "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", - "installed_by": [ - "subworkflows" - ] + "installed_by": ["subworkflows"] }, "utils_nfvalidation_plugin": { "branch": "master", "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", - "installed_by": [ - "subworkflows" - ] + "installed_by": ["subworkflows"] }, "vcf_impute_glimpse": { "branch": "master", "git_sha": "7e56daae390ff896b292ddc70823447683a79936", - "installed_by": [ - "subworkflows" - ] + "installed_by": ["subworkflows"] }, "vcf_phase_shapeit5": { "branch": "master", "git_sha": "dcf17cc0ed8fd5ea57e61a13e0147cddb5c1ee30", - "installed_by": [ - "subworkflows" - ] + "installed_by": ["subworkflows"] } } } } } -} \ No newline at end of file +} From a965506350d3fba3bea69d7721c92c9203938bb5 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 18:29:33 +0200 Subject: [PATCH 33/65] Fix changelog --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0af049a3..c4d47b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,7 @@ Initial release of nf-core/phaseimpute, created with the [nf-core](https://nf-co - Test impute and test sim works - [#19](https://github.com/nf-core/phaseimpute/pull/19) - Changed reference panel to accept a csv, update modules and subworkflows (glimpse1/2 and shapeit5) - [#20](https://github.com/nf-core/phaseimpute/pull/20) - Added automatic detection of vcf contigs for the reference panel and automatic renaming available -- [#22](https://github.com/nf-core/phaseimpute/pull/20) - Add validation step for concordance analysis. Input channels changed to - match inputs steps. Outdir folder organised by steps. Modules config by subworkflows. +- [#22](https://github.com/nf-core/phaseimpute/pull/20) - Add validation step for concordance analysis. Input channels changed to match inputs steps. Outdir folder organised by steps. Modules config by subworkflows. ### `Fixed` From 95df09c231e1bb2737b7df2cbf668419e04efbbd Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 18:39:12 +0200 Subject: [PATCH 34/65] Fix bam detection --- workflows/phaseimpute/main.nf | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index bfc0257a..839513a6 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -59,12 +59,10 @@ workflow PHASEIMPUTE { ch_sim_output = Channel.empty() // Test if the input are all bam files - input_ext = getAllFilesExtension(ch_input_sim) - - // Channels for branching - if (input_ext.size() != 1 && input_ext[0] != 'bam') { - error "All input files must be in BAM format to perform simulation" - } + getAllFilesExtension(ch_input_sim) + .map{ if (it != "bam") { + error "All input files must be in BAM format to perform simulation" + } } // Split the bam into the region specified BAM_REGION(ch_input_sim, ch_region, ch_fasta) From a906cf6f3e13da3bcde4e5b12fb385b9952a79b2 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 20:02:40 +0200 Subject: [PATCH 35/65] Add coverage to multiqc --- subworkflows/local/bam_downsample/main.nf | 7 ++++--- workflows/phaseimpute/main.nf | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/bam_downsample/main.nf b/subworkflows/local/bam_downsample/main.nf index 4a700bcb..106cf2a3 100644 --- a/subworkflows/local/bam_downsample/main.nf +++ b/subworkflows/local/bam_downsample/main.nf @@ -10,7 +10,7 @@ workflow BAM_DOWNSAMPLE { ch_fasta // channel: [ [genome], fasta, fai ] main: - ch_versions = Channel.empty() + ch_versions = Channel.empty() // Add region to channel ch_coverage = ch_bam @@ -60,6 +60,7 @@ workflow BAM_DOWNSAMPLE { .combine(SAMTOOLS_INDEX.out.bai, by:0) emit: - bam_emul = ch_bam_emul // channel: [ [id, genome, chr, region, depth], bam, bai ] - versions = ch_versions // channel: [ versions.yml ] + bam_emul = ch_bam_emul // channel: [ [id, genome, chr, region, depth], bam, bai ] + coverage = SAMTOOLS_COVERAGE.out.coverage // channel: [ [id, genome, chr, region, depth], txt ] + versions = ch_versions // channel: [ versions.yml ] } diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 839513a6..af61f266 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -66,6 +66,7 @@ workflow PHASEIMPUTE { // Split the bam into the region specified BAM_REGION(ch_input_sim, ch_region, ch_fasta) + ch_versions = ch_versions.mix(BAM_REGION.out.versions.first()) // Initialize channel to impute ch_bam_to_impute = Channel.empty() @@ -77,7 +78,8 @@ workflow PHASEIMPUTE { ch_depth, ch_fasta ) - ch_versions = ch_versions.mix(BAM_DOWNSAMPLE.out.versions.first()) + ch_versions = ch_versions.mix(BAM_DOWNSAMPLE.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix(BAM_DOWNSAMPLE.out.coverage.map{ [it[1]] }) ch_input_impute = BAM_DOWNSAMPLE.out.bam_emul ch_input_validate_truth = BAM_REGION.out.bam_region } @@ -182,7 +184,7 @@ workflow PHASEIMPUTE { ch_fasta ) ch_multiqc_files = ch_multiqc_files.mix(GL_TRUTH.out.multiqc_files) - ch_versions = ch_versions.mix(GL_TRUTH.out.versions.first()) + ch_versions = ch_versions.mix(GL_TRUTH.out.versions.first()) // Mix the original vcf and the computed vcf ch_truth_vcf = ch_truth.vcf From 76ec2cbcec41903e5e067ff00187e1b0df2e1817 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 17 Apr 2024 22:38:58 +0200 Subject: [PATCH 36/65] Remove version.yml export Rename export files Fix versions export of BCFTOOLS_QUERY and VCFCHREXTRACT Add all versions to multiqc --- conf/steps/imputation.config | 32 +++++++++++++++---- conf/steps/panel_prep.config | 9 +++--- conf/steps/simulation.config | 20 ++++++++---- conf/steps/validation.config | 25 ++++++++++++--- modules/local/vcfchrextract/main.nf | 2 +- subworkflows/local/get_panel/main.nf | 5 ++- .../utils_nfcore_phaseimpute_pipeline/main.nf | 2 +- subworkflows/local/vcf_chr_check/main.nf | 6 ++-- workflows/phaseimpute/main.nf | 18 +++++------ 9 files changed, 80 insertions(+), 39 deletions(-) diff --git a/conf/steps/imputation.config b/conf/steps/imputation.config index 8b485e92..93337985 100644 --- a/conf/steps/imputation.config +++ b/conf/steps/imputation.config @@ -15,7 +15,9 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:.*' { publishDir = [ path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: false ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { @@ -28,12 +30,14 @@ process { "-Aim", "-C alleles" ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.call" } } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { publishDir = [ - path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode + path: { "${params.outdir}/imputation/glimpse1/" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -42,18 +46,34 @@ process { "--window-size 200000", "--buffer-size 20000" ].join(' ') - ext.prefix = { "${meta.id}" } + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.chunk" } + publishDir = [ + enabled: false + ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { ext.args = [ "--impute-reference-only-variants" ].join(' ') - ext.prefix = { "${meta.id}" } + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.phase" } ext.suffix = "bcf" + publishDir = [ + enabled: false + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_PHASE' { + publishDir = [ + enabled: false + ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}" } + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.ligate" } + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1/glimpse" } + ] } } diff --git a/conf/steps/panel_prep.config b/conf/steps/panel_prep.config index 2814b96d..19c2e726 100644 --- a/conf/steps/panel_prep.config +++ b/conf/steps/panel_prep.config @@ -13,7 +13,7 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CHR_CHECK:.*' { publishDir = [ - path: { "${params.outdir}/prep_panel/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + path: { "${params.outdir}/prep_panel/" }, mode: params.publish_dir_mode, enabled: false ] @@ -29,9 +29,10 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:.*' { publishDir = [ - path: { "${params.outdir}/prep_panel/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + path: { "${params.outdir}/prep_panel/" }, mode: params.publish_dir_mode, - enabled: false + enabled: true, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } @@ -69,7 +70,7 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:BCFTOOLS_QUERY' { ext.args = [ - "-f'%CHROM\t%POS\t%REF,%ALT\n'", + "-f'%CHROM\t%POS\t%REF,%ALT\\n'", ].join(' ') ext.prefix = { "${meta.id}_SITES_TSV" } } diff --git a/conf/steps/simulation.config b/conf/steps/simulation.config index 582b0ef3..412c82a4 100644 --- a/conf/steps/simulation.config +++ b/conf/steps/simulation.config @@ -13,26 +13,32 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_REGION:.*' { publishDir = [ - path: { "${params.outdir}/simulation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + path: { "${params.outdir}/simulation/" }, mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: false ] } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_REGION:SAMTOOLS_VIEW' { + ext.args = [ + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}" } + } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:.*' { publishDir = [ - path: { "${params.outdir}/simulation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + path: { "${params.outdir}/simulation/" }, mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, ] } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_REGION:SAMTOOLS_VIEW' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:SAMTOOLS_COVERAGE' { ext.args = [ ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region}" } + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.stats" } } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:SAMTOOLS_VIEW' { ext.args = [ ].join(' ') - ext.prefix = { "${meta.id}_D${meta.depth}" } + ext.prefix = { "${meta.id}_D${meta.depth}_R${meta.region.replace(':','_')}" } } } diff --git a/conf/steps/validation.config b/conf/steps/validation.config index 7da707d7..d429bfed 100644 --- a/conf/steps/validation.config +++ b/conf/steps/validation.config @@ -14,8 +14,9 @@ process { // Configuration for the validation step withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:.*' { publishDir = [ - path: { "${params.outdir}/validation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode + path: { "${params.outdir}/validation/truth" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:BCFTOOLS_MPILEUP' { @@ -28,25 +29,39 @@ process { "-Aim", "-C alleles" ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}_truth.call" } } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:.*' { publishDir = [ - path: { "${params.outdir}/validation/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode + path: { "${params.outdir}/validation/" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}" } + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.concordance" } ext.args = "--out-r2-per-site" + publishDir = [ + enabled: false + ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:CONCATENATE' { ext.suffix = { "txt" } } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GUNZIP' { + publishDir = [ + enabled: false + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:ADD_COLUMNS' { ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}_SNP" } + publishDir = [ + enabled: false + ] } } diff --git a/modules/local/vcfchrextract/main.nf b/modules/local/vcfchrextract/main.nf index b458bb0e..6283e4eb 100644 --- a/modules/local/vcfchrextract/main.nf +++ b/modules/local/vcfchrextract/main.nf @@ -30,7 +30,7 @@ process VCFCHREXTRACT { cat <<-END_VERSIONS > versions.yml "${task.process}": bcftools: \$( bcftools --version |& sed '1!d; s/^.*bcftools //' ) - grep: \$( grep --help |& grep -o -E '[0-9]+\\.[0-9]+\\.[0-9]+' ) + grep: \$( grep --version |& grep -o -E '[0-9]+\\.[0-9]+' ) END_VERSIONS """ diff --git a/subworkflows/local/get_panel/main.nf b/subworkflows/local/get_panel/main.nf index b1c2bf92..2f40dfd6 100644 --- a/subworkflows/local/get_panel/main.nf +++ b/subworkflows/local/get_panel/main.nf @@ -48,8 +48,7 @@ workflow GET_PANEL { .combine(VCF_INDEX4.out.csi, by:0) // Convert to TSV - BCFTOOLS_QUERY(ch_panel_sites, - [], [], []) + BCFTOOLS_QUERY(ch_panel_sites, [], [], []) ch_versions = ch_versions.mix(BCFTOOLS_QUERY.out.versions.first()) TABIX_BGZIP(BCFTOOLS_QUERY.out.output) @@ -68,7 +67,7 @@ workflow GET_PANEL { Channel.of([[],[],[]]).collect(), Channel.of([[],[],[]]).collect(), Channel.of([[],[]]).collect()) - ch_versions = ch_versions.mix(VCF_PHASE_SHAPEIT5.out.versions.first()) + ch_versions = ch_versions.mix(VCF_PHASE_SHAPEIT5.out.versions) ch_panel_phased = VCF_PHASE_SHAPEIT5.out.variants_phased .combine(VCF_PHASE_SHAPEIT5.out.variants_index, by: 0) } else { diff --git a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf index 3d5c3ca4..a41fbf67 100644 --- a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf @@ -168,7 +168,7 @@ workflow PIPELINE_INITIALISATION { "all", ch_ref_gen ) - ch_versions = ch_versions.mix(GET_REGION.out.versions.first()) + ch_versions = ch_versions.mix(GET_REGION.out.versions) ch_regions = GET_REGION.out.regions } else { error "Region file provided is of another format than CSV (not yet supported). Please separate your reference genome by chromosome and use the samplesheet format." diff --git a/subworkflows/local/vcf_chr_check/main.nf b/subworkflows/local/vcf_chr_check/main.nf index db5960b8..3c9d5f79 100644 --- a/subworkflows/local/vcf_chr_check/main.nf +++ b/subworkflows/local/vcf_chr_check/main.nf @@ -13,7 +13,7 @@ workflow VCF_CHR_CHECK { // Get contig names from the VCF VCFCHRBFR(ch_vcf.map{ metaV, vcf, csi -> [metaV, vcf] }) - ch_versions = ch_versions.mix(VCFCHRBFR.out.versions.first()) + ch_versions = ch_versions.mix(VCFCHRBFR.out.versions) // Check if the contig names are the same as the reference chr_disjoint = check_chr(VCFCHRBFR.out.chr, ch_vcf, ch_fasta) @@ -24,11 +24,11 @@ workflow VCF_CHR_CHECK { chr_disjoint.to_rename.map{meta, vcf, index, nb -> [meta, vcf, index]}, ch_fasta ) - ch_versions = ch_versions.mix(VCF_CHR_RENAME.out.versions.first()) + ch_versions = ch_versions.mix(VCF_CHR_RENAME.out.versions) // Check if modification has solved the problem VCFCHRAFT(VCF_CHR_RENAME.out.vcf_renamed.map{ metaV, vcf, csi -> [metaV, vcf] }) - ch_versions = ch_versions.mix(VCFCHRAFT.out.versions.first()) + ch_versions = ch_versions.mix(VCFCHRAFT.out.versions) chr_disjoint_after = check_chr(VCFCHRAFT.out.chr, VCF_CHR_RENAME.out.vcf_renamed, ch_fasta) diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index af61f266..14cc2b53 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -66,7 +66,7 @@ workflow PHASEIMPUTE { // Split the bam into the region specified BAM_REGION(ch_input_sim, ch_region, ch_fasta) - ch_versions = ch_versions.mix(BAM_REGION.out.versions.first()) + ch_versions = ch_versions.mix(BAM_REGION.out.versions) // Initialize channel to impute ch_bam_to_impute = Channel.empty() @@ -78,7 +78,7 @@ workflow PHASEIMPUTE { ch_depth, ch_fasta ) - ch_versions = ch_versions.mix(BAM_DOWNSAMPLE.out.versions.first()) + ch_versions = ch_versions.mix(BAM_DOWNSAMPLE.out.versions) ch_multiqc_files = ch_multiqc_files.mix(BAM_DOWNSAMPLE.out.coverage.map{ [it[1]] }) ch_input_impute = BAM_DOWNSAMPLE.out.bam_emul ch_input_validate_truth = BAM_REGION.out.bam_region @@ -95,11 +95,11 @@ workflow PHASEIMPUTE { if (params.step == 'impute' || params.step == 'panel_prep' || params.step == 'validate' || params.step == 'all') { // Remove if necessary "chr" VCF_CHR_CHECK(ch_panel, ch_fasta) - ch_versions = ch_versions.mix(VCF_CHR_CHECK.out.versions.first()) + ch_versions = ch_versions.mix(VCF_CHR_CHECK.out.versions) // Prepare the panel GET_PANEL(VCF_CHR_CHECK.out.vcf, ch_fasta) - ch_versions = ch_versions.mix(GET_PANEL.out.versions.first()) + ch_versions = ch_versions.mix(GET_PANEL.out.versions) ch_panel_sites_tsv = GET_PANEL.out.panel .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index -> [metaPC, sites, tsv] @@ -113,7 +113,7 @@ workflow PHASEIMPUTE { -> [metaPC, phased, p_index] } - ch_versions = ch_versions.mix(GET_PANEL.out.versions.first()) + ch_versions = ch_versions.mix(GET_PANEL.out.versions) if (params.step == 'impute' || params.step == 'all') { // Output channel of input process @@ -127,7 +127,7 @@ workflow PHASEIMPUTE { ch_fasta ) ch_multiqc_files = ch_multiqc_files.mix(GL_INPUT.out.multiqc_files) - ch_versions = ch_versions.mix(GL_INPUT.out.versions.first()) + ch_versions = ch_versions.mix(GL_INPUT.out.versions) impute_input = GL_INPUT.out.vcf // [metaIPC, vcf, index] .map {metaIPC, vcf, index -> [metaIPC.subMap("panel", "chr"), metaIPC, vcf, index] } @@ -150,7 +150,7 @@ workflow PHASEIMPUTE { .map{ metaIPCR, vcf, csi -> [metaIPCR + [tools: "Glimpse1"], vcf, csi] } ch_impute_output = ch_impute_output.mix(output_glimpse1) ch_multiqc_files = ch_multiqc_files.mix(VCF_IMPUTE_GLIMPSE1.out.chunk_chr.map{ [it[1]]}) - ch_versions = ch_versions.mix(VCF_IMPUTE_GLIMPSE1.out.versions.first()) + ch_versions = ch_versions.mix(VCF_IMPUTE_GLIMPSE1.out.versions) } if (params.tools.contains("glimpse2")) { error "Glimpse2 not yet implemented" @@ -184,7 +184,7 @@ workflow PHASEIMPUTE { ch_fasta ) ch_multiqc_files = ch_multiqc_files.mix(GL_TRUTH.out.multiqc_files) - ch_versions = ch_versions.mix(GL_TRUTH.out.versions.first()) + ch_versions = ch_versions.mix(GL_TRUTH.out.versions) // Mix the original vcf and the computed vcf ch_truth_vcf = ch_truth.vcf @@ -198,7 +198,7 @@ workflow PHASEIMPUTE { ch_panel_sites ) ch_multiqc_files = ch_multiqc_files.mix(VCF_CONCORDANCE_GLIMPSE2.out.multiqc_files) - ch_versions = ch_versions.mix(VCF_CONCORDANCE_GLIMPSE2.out.versions.first()) + ch_versions = ch_versions.mix(VCF_CONCORDANCE_GLIMPSE2.out.versions) } if (params.step == 'refine') { From 90c89f7bac56f674b31177ab03861b818adaf728 Mon Sep 17 00:00:00 2001 From: Louis LE NEZET <58640615+LouisLeNezet@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:39:58 +0200 Subject: [PATCH 37/65] Update conf/steps/imputation.config Co-authored-by: Anabella Trigila <18577080+atrigila@users.noreply.github.com> --- conf/steps/imputation.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/steps/imputation.config b/conf/steps/imputation.config index 93337985..9ad7fcce 100644 --- a/conf/steps/imputation.config +++ b/conf/steps/imputation.config @@ -73,7 +73,7 @@ process { } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { publishDir = [ - path: { "${params.outdir}/imputation/glimpse1/glimpse" } + path: { "${params.outdir}/imputation/glimpse1" } ] } } From 5ff551eb47222b3f36289e58454c7b6956a66e46 Mon Sep 17 00:00:00 2001 From: Louis LE NEZET <58640615+LouisLeNezet@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:41:14 +0200 Subject: [PATCH 38/65] Update conf/steps/panel_prep.config Co-authored-by: Anabella Trigila <18577080+atrigila@users.noreply.github.com> --- conf/steps/panel_prep.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/steps/panel_prep.config b/conf/steps/panel_prep.config index 19c2e726..3bfd8818 100644 --- a/conf/steps/panel_prep.config +++ b/conf/steps/panel_prep.config @@ -53,7 +53,7 @@ process { "--output-type z", "--no-version" ].join(' ') - ext.prefix = { "${meta.id}_SPNS" } + ext.prefix = { "${meta.id}_SNPS" } } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:VIEW_VCF_SITES' { From 072e9e132d6f72ffdf9ea0070f7380cce29e23c0 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 10:39:55 +0200 Subject: [PATCH 39/65] Delete old module --- modules/local/concatenate/main.nf | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 modules/local/concatenate/main.nf diff --git a/modules/local/concatenate/main.nf b/modules/local/concatenate/main.nf deleted file mode 100644 index 77a179c6..00000000 --- a/modules/local/concatenate/main.nf +++ /dev/null @@ -1,25 +0,0 @@ -process CONCATENATE { - label 'process_single' - - input: - tuple val(meta), path(input) - - output: - tuple val(meta), path('*.txt'), emit: txt - path "versions.yml", emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def prefix = task.ext.prefix ?: "${meta.id}" - """ - awk '(NR == 1) || (FNR > 1)' $input > ${prefix}.txt - - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - awk: \$(awk --version | head -1 | grep -o -E '([0-9]+.){1,2}[0-9]') - END_VERSIONS - """ -} From 9bfc777671252dd3707f7265f937fee924ac0a51 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 10:46:54 +0200 Subject: [PATCH 40/65] Add gwak environment to add columns --- modules/local/addcolumns/environment.yml | 7 +++++++ modules/local/addcolumns/main.nf | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 modules/local/addcolumns/environment.yml diff --git a/modules/local/addcolumns/environment.yml b/modules/local/addcolumns/environment.yml new file mode 100644 index 00000000..34513c7f --- /dev/null +++ b/modules/local/addcolumns/environment.yml @@ -0,0 +1,7 @@ +name: gawk +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - anaconda::gawk=5.1.0 diff --git a/modules/local/addcolumns/main.nf b/modules/local/addcolumns/main.nf index 2fbe882c..71b41487 100644 --- a/modules/local/addcolumns/main.nf +++ b/modules/local/addcolumns/main.nf @@ -1,6 +1,11 @@ process ADD_COLUMNS { label 'process_single' + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/gawk:5.1.0' : + 'biocontainers/gawk:5.1.0' }" + input: tuple val(meta), path(input) From 3ae3747c14e47397fad015513edf228fb625b55f Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 10:47:12 +0200 Subject: [PATCH 41/65] Update running command --- conf/test_all.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test_all.config b/conf/test_all.config index f007a039..3e95cb32 100644 --- a/conf/test_all.config +++ b/conf/test_all.config @@ -5,7 +5,7 @@ Defines input files and everything required to run a fast and simple pipeline test. Use as follows: - nextflow run nf-core/phaseimpute -profile test_sim, --outdir + nextflow run nf-core/phaseimpute -profile test_all, --outdir ---------------------------------------------------------------------------------------- */ From 27f8f045371c6dc409674066ae734c1329d0cbf0 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 10:49:17 +0200 Subject: [PATCH 42/65] Delete trailing spaces --- modules/local/addcolumns/main.nf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/local/addcolumns/main.nf b/modules/local/addcolumns/main.nf index 71b41487..4d4a4c9c 100644 --- a/modules/local/addcolumns/main.nf +++ b/modules/local/addcolumns/main.nf @@ -1,10 +1,10 @@ process ADD_COLUMNS { label 'process_single' - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/gawk:5.1.0' : - 'biocontainers/gawk:5.1.0' }" + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/gawk:5.1.0' : + 'biocontainers/gawk:5.1.0' }" input: tuple val(meta), path(input) From f639aabeed1d4f3800dc7ad335819e59b3d901dc Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 10:58:52 +0200 Subject: [PATCH 43/65] Add versions --- .../vcf_concatenate_bcftools.nf | 14 +++++++++----- .../local/vcf_concordance_glimpse2/main.nf | 4 ++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf b/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf index 6653d765..79d8ba23 100644 --- a/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf +++ b/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf @@ -8,6 +8,8 @@ workflow VCF_CONCATENATE_BCFTOOLS { main: + ch_versions = Channel.empty() + // Remove chromosome from meta ch_vcf_tbi_grouped = ch_vcf_tbi.map{ meta, vcf, tbi -> return [['id' : meta.id], vcf, tbi] @@ -16,15 +18,17 @@ workflow VCF_CONCATENATE_BCFTOOLS { ch_vcf_tbi_grouped = ch_vcf_tbi_grouped.groupTuple( by:[0] ) // Ligate and concatenate chunks - BCFTOOLS_CONCAT(ch_vcf_tbi_grouped) + BCFTOOLS_CONCAT(ch_vcf_tbi_grouped)* + ch_versions = ch_versions.mix(BCFTOOLS_CONCAT.out.versions.first()) // Index concatenated VCF BCFTOOLS_INDEX(BCFTOOLS_CONCAT.out.vcf) + ch_versions = ch_versions.mix(BCFTOOLS_INDEX.out.versions.first()) // Join VCFs and TBIs - ch_imputed_vcf_tbi = BCFTOOLS_CONCAT.out.vcf.join(BCFTOOLS_INDEX.out.tbi) + ch_vcf_tbi_join = BCFTOOLS_CONCAT.out.vcf.join(BCFTOOLS_INDEX.out.tbi) emit: - ch_imputed_vcf_tbi // channel: [ meta, vcf, tbi ] - - } + vcf_tbi_join = ch_vcf_tbi_joi // channel: [ meta, vcf, tbi ] + versions = ch_versions // channel: [ versions.yml ] +} diff --git a/subworkflows/local/vcf_concordance_glimpse2/main.nf b/subworkflows/local/vcf_concordance_glimpse2/main.nf index 0d704726..a3f964e8 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/main.nf +++ b/subworkflows/local/vcf_concordance_glimpse2/main.nf @@ -41,6 +41,7 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { [[], [], params.bins, [], []], params.min_val_gl, params.min_val_dp ) + ch_versions = ch_versions.mix(GLIMPSE2_CONCORDANCE.out.versions.first()) ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.errors_cal.map{meta, txt -> [txt]}) ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.errors_grp.map{meta, txt -> [txt]}) @@ -50,7 +51,9 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { ch_multiqc_files = ch_multiqc_files.mix(GLIMPSE2_CONCORDANCE.out.rsquare_per_site.map{meta, txt -> [txt]}) GUNZIP(GLIMPSE2_CONCORDANCE.out.errors_grp) + ch_versions = ch_versions.mix(GUNZIP.out.versions.first()) ADD_COLUMNS(GUNZIP.out.gunzip) + ch_versions = ch_versions.mix(ADD_COLUMNS.out.versions.first()) CONCATENATE( ADD_COLUMNS.out.txt @@ -60,6 +63,7 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { '(NR == 1) || (FNR > 1)' ).collectFile(name:"program.txt") ) + ch_versions = ch_versions.mix(CONCATENATE.out.versions.first()) emit: stats = CONCATENATE.out.output // [ meta, txt ] From 8c84aa4a289c9f8b9f592e4a2b48d5e5723e9792 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 10:59:15 +0200 Subject: [PATCH 44/65] Concatenate by chromosomes --- workflows/phaseimpute/main.nf | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index e89ca864..8187f683 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -153,9 +153,13 @@ workflow PHASEIMPUTE { output_glimpse1 = VCF_IMPUTE_GLIMPSE1.out.merged_variants .combine(VCF_IMPUTE_GLIMPSE1.out.merged_variants_index, by: 0) .map{ metaIPCR, vcf, csi -> [metaIPCR + [tools: "Glimpse1"], vcf, csi] } - ch_impute_output = ch_impute_output.mix(output_glimpse1) ch_multiqc_files = ch_multiqc_files.mix(VCF_IMPUTE_GLIMPSE1.out.chunk_chr.map{ [it[1]]}) ch_versions = ch_versions.mix(VCF_IMPUTE_GLIMPSE1.out.versions) + + VCF_CONCATENATE_BCFTOOLS(output_glimpse1) + ch_impute_output = ch_impute_output.mix(VCF_CONCATENATE_BCFTOOLS.out.vcf_tbi_join) + ch_versions = ch_versions.mix(VCF_CONCATENATE_BCFTOOLS.out.versions) + } if (params.tools.contains("glimpse2")) { error "Glimpse2 not yet implemented" @@ -187,7 +191,6 @@ workflow PHASEIMPUTE { // Concatenate results VCF_CONCATENATE_BCFTOOLS(IMPUTE_QUILT.out.ch_vcf_tbi) - } ch_input_validate = ch_input_validate.mix(ch_impute_output) } From 483df50fa1bfcce98e0af8300205eff99a3d1331 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 14:43:54 +0200 Subject: [PATCH 45/65] Reorganize config --- conf/steps/imputation.config | 66 +++------------- conf/steps/imputation_glimpse1.config | 79 +++++++++++++++++++ .../imputation_quilt.config} | 36 +-------- conf/steps/panel_prep.config | 22 +++++- conf/steps/validation.config | 24 +++++- 5 files changed, 134 insertions(+), 93 deletions(-) create mode 100644 conf/steps/imputation_glimpse1.config rename conf/{quilt_subworkflow.config => steps/imputation_quilt.config} (72%) diff --git a/conf/steps/imputation.config b/conf/steps/imputation.config index 9ad7fcce..93a9776c 100644 --- a/conf/steps/imputation.config +++ b/conf/steps/imputation.config @@ -11,69 +11,23 @@ */ process { - // Configuration for the glimpse1 imputation subworkflow - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:.*' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_IMPUT:.*' { publishDir = [ - path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: false - ] - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { - ext.args = [ - "-I", - "-E", - "-a 'FORMAT/DP'" - ].join(' ') - ext.args2 = [ - "-Aim", - "-C alleles" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.call" } - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1/" }, + path: { "${params.outdir}/impute/concat" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] + ext.prefix = { "${meta.id}_impute_concat" } } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { - ext.args = [ - "--window-size 200000", - "--buffer-size 20000" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.chunk" } - publishDir = [ - enabled: false - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { - ext.args = [ - "--impute-reference-only-variants" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.phase" } - ext.suffix = "bcf" - publishDir = [ - enabled: false - ] - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_PHASE' { - publishDir = [ - enabled: false - ] + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_IMPUT:BCFTOOLS_CONCAT' { + ext.args = {[ + "--ligate", + "--output-type z", + ].join(" ").trim()} } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.ligate" } - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" } - ] + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_IMPUT:BCFTOOLS_INDEX' { + ext.args = "--tbi" } } diff --git a/conf/steps/imputation_glimpse1.config b/conf/steps/imputation_glimpse1.config new file mode 100644 index 00000000..b400ecb4 --- /dev/null +++ b/conf/steps/imputation_glimpse1.config @@ -0,0 +1,79 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + // Configuration for the glimpse1 imputation subworkflow + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:.*' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: false + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { + ext.args = [ + "-I", + "-E", + "-a 'FORMAT/DP'" + ].join(' ') + ext.args2 = [ + "-Aim", + "-C alleles" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.call" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1/" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { + ext.args = [ + "--window-size 200000", + "--buffer-size 20000" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.chunk" } + publishDir = [ + enabled: false + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { + ext.args = [ + "--impute-reference-only-variants" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.phase" } + ext.suffix = "bcf" + publishDir = [ + enabled: false + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_PHASE' { + publishDir = [ + enabled: false + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.ligate" } + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" } + ] + } +} \ No newline at end of file diff --git a/conf/quilt_subworkflow.config b/conf/steps/imputation_quilt.config similarity index 72% rename from conf/quilt_subworkflow.config rename to conf/steps/imputation_quilt.config index 6f237032..6f255ff6 100644 --- a/conf/quilt_subworkflow.config +++ b/conf/steps/imputation_quilt.config @@ -26,7 +26,7 @@ process { publishDir = [ [ - path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}_chunk" }, + path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}_chunk" }, mode: params.publish_dir_mode, ], @@ -88,7 +88,7 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:QUILT_QUILT' { publishDir = [ [ - path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, mode: params.publish_dir_mode, ], ] @@ -100,36 +100,4 @@ process { ].join(" ").trim()} } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCATENATE_BCFTOOLS:BCFTOOLS_CONCAT' { - ext.args = {[ - "--ligate", - "--output-type z", - ].join(" ").trim()} - - cpus = 2 - memory = 1.GB - maxRetries = 2 - - publishDir = [ - [ - path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}/concat" }, - mode: params.publish_dir_mode, - ], - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCATENATE_BCFTOOLS:BCFTOOLS_INDEX' { - ext.args = {[ - "--tbi", - ].join(" ").trim()} - - publishDir = [ - [ - path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}/concat" }, - mode: params.publish_dir_mode, - ], - ] - } - } diff --git a/conf/steps/panel_prep.config b/conf/steps/panel_prep.config index 3bfd8818..5eec78ce 100644 --- a/conf/steps/panel_prep.config +++ b/conf/steps/panel_prep.config @@ -65,7 +65,7 @@ process { "--output-type z", "--no-version" ].join(' ') - ext.prefix = { "${meta.id}_SITES" } + ext.prefix = { "${meta.id}_C${meta.chr}_SITES" } } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GET_PANEL:BCFTOOLS_QUERY' { @@ -94,4 +94,24 @@ process { enabled: false ] } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_PANEL:.*' { + publishDir = [ + path: { "${params.outdir}/prep_panel/concat" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + ext.prefix = { "${meta.id}_sites_concat" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_PANEL:BCFTOOLS_CONCAT' { + ext.args = {[ + "--ligate", + "--output-type z", + ].join(" ").trim()} + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_PANEL:BCFTOOLS_INDEX' { + ext.args = "--tbi" + } } diff --git a/conf/steps/validation.config b/conf/steps/validation.config index d429bfed..6481309c 100644 --- a/conf/steps/validation.config +++ b/conf/steps/validation.config @@ -32,6 +32,26 @@ process { ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}_truth.call" } } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_TRUTH:.*' { + publishDir = [ + path: { "${params.outdir}/validation/concat" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + ext.prefix = { "${meta.id}_truth_concat" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_TRUTH:BCFTOOLS_CONCAT' { + ext.args = {[ + "--ligate", + "--output-type z", + ].join(" ").trim()} + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_TRUTH:BCFTOOLS_INDEX' { + ext.args = "--tbi" + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:.*' { publishDir = [ path: { "${params.outdir}/validation/" }, @@ -41,7 +61,7 @@ process { } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.concordance" } + ext.prefix = { "${meta.id}.concordance" } ext.args = "--out-r2-per-site" publishDir = [ enabled: false @@ -59,7 +79,7 @@ process { } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:ADD_COLUMNS' { - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region.replace(':','_')}_SNP" } + ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_SNP" } publishDir = [ enabled: false ] From 95a8e55ebffc8f043d1facd211dd71cd97b35903 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 14:44:11 +0200 Subject: [PATCH 46/65] Rename concatenation script to main --- .../{vcf_concatenate_bcftools.nf => main.nf} | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename subworkflows/local/vcf_concatenate_bcftools/{vcf_concatenate_bcftools.nf => main.nf} (64%) diff --git a/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf b/subworkflows/local/vcf_concatenate_bcftools/main.nf similarity index 64% rename from subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf rename to subworkflows/local/vcf_concatenate_bcftools/main.nf index 79d8ba23..bc85d146 100644 --- a/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf +++ b/subworkflows/local/vcf_concatenate_bcftools/main.nf @@ -1,5 +1,5 @@ -include { BCFTOOLS_CONCAT } from '../../../modules/nf-core/bcftools/concat/main' -include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index/main' +include { BCFTOOLS_CONCAT } from '../../../modules/nf-core/bcftools/concat' +include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index' workflow VCF_CONCATENATE_BCFTOOLS { @@ -11,14 +11,14 @@ workflow VCF_CONCATENATE_BCFTOOLS { ch_versions = Channel.empty() // Remove chromosome from meta - ch_vcf_tbi_grouped = ch_vcf_tbi.map{ meta, vcf, tbi -> - return [['id' : meta.id], vcf, tbi] - } + ch_vcf_tbi_grouped = ch_vcf_tbi + .map{ meta, vcf, tbi -> [['id' : meta.id], vcf, tbi] } + // Group by ID - ch_vcf_tbi_grouped = ch_vcf_tbi_grouped.groupTuple( by:[0] ) + ch_vcf_tbi_grouped = ch_vcf_tbi_grouped.groupTuple( by:0 ) // Ligate and concatenate chunks - BCFTOOLS_CONCAT(ch_vcf_tbi_grouped)* + BCFTOOLS_CONCAT(ch_vcf_tbi_grouped) ch_versions = ch_versions.mix(BCFTOOLS_CONCAT.out.versions.first()) // Index concatenated VCF @@ -29,6 +29,6 @@ workflow VCF_CONCATENATE_BCFTOOLS { ch_vcf_tbi_join = BCFTOOLS_CONCAT.out.vcf.join(BCFTOOLS_INDEX.out.tbi) emit: - vcf_tbi_join = ch_vcf_tbi_joi // channel: [ meta, vcf, tbi ] - versions = ch_versions // channel: [ versions.yml ] + vcf_tbi_join = ch_vcf_tbi_join // channel: [ meta, vcf, tbi ] + versions = ch_versions // channel: [ versions.yml ] } From b1ca5e5ad176a3ff23b0511c7dce75556d05d132 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 14:44:37 +0200 Subject: [PATCH 47/65] Change version emission of addcolumns --- modules/local/addcolumns/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/addcolumns/main.nf b/modules/local/addcolumns/main.nf index 4d4a4c9c..7789f76d 100644 --- a/modules/local/addcolumns/main.nf +++ b/modules/local/addcolumns/main.nf @@ -36,7 +36,7 @@ process ADD_COLUMNS { cat <<-END_VERSIONS > versions.yml "${task.process}": - awk: \$(awk --version | head -1 | grep -o -E '([0-9]+.){1,2}[0-9]') + gawk: \$(awk -Wversion | sed '1!d; s/.*Awk //; s/,.*//') END_VERSIONS """ } From 80af504a4434922e40a52d6cb24c995e16042baa Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 14:45:22 +0200 Subject: [PATCH 48/65] Simplify concordance channel management --- .../local/vcf_concordance_glimpse2/main.nf | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/subworkflows/local/vcf_concordance_glimpse2/main.nf b/subworkflows/local/vcf_concordance_glimpse2/main.nf index a3f964e8..bc76d0f2 100644 --- a/subworkflows/local/vcf_concordance_glimpse2/main.nf +++ b/subworkflows/local/vcf_concordance_glimpse2/main.nf @@ -9,6 +9,7 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { ch_vcf_emul // VCF file with imputed genotypes [[id, chr, region, panel, simulate, tools], vcf, csi] ch_vcf_truth // VCF file with truth genotypes [[id, chr, region], vcf, csi] ch_vcf_freq // VCF file with panel frequencies [[panel, chr], vcf, csi] + ch_region // Regions to process [[chr, region], region] main: @@ -16,24 +17,11 @@ workflow VCF_CONCORDANCE_GLIMPSE2 { ch_multiqc_files = Channel.empty() ch_concordance = ch_vcf_emul - .map{ - metaICRPST, vcf, csi -> - [metaICRPST.subMap(["id", "chr", "region"]), metaICRPST, vcf, csi] - } - .combine(ch_vcf_truth.map{ - metaICRP, vcf, csi -> - [metaICRP.subMap(["id", "chr", "region"]), vcf, csi] - }, - by:0 - ) - .map{metaICR, metaIPCRTS, emul, e_csi, truth, t_csi -> - [metaICR.subMap(["chr"]), metaIPCRTS, emul, e_csi, truth, t_csi] - } - .combine(ch_vcf_freq.map{metaCRP, vcf, csi -> - [metaCRP.subMap(["chr"]), metaCRP, vcf, csi]}, - by:0) - .map{metaC, metaIPCRTS, emul, e_csi, truth, t_csi, metaCRP, freq, f_csi -> - [metaIPCRTS, emul, e_csi, truth, t_csi, freq, f_csi, [], metaIPCRTS.region] + .join(ch_vcf_truth) + .combine(ch_vcf_freq) + .combine(ch_region.map{[it[1]]}.collect().toList()) + .map{metaI, emul, e_csi, truth, t_csi, metaP, freq, f_csi, regions -> + [metaI, emul, e_csi, truth, t_csi, freq, f_csi, [], regions] } GLIMPSE2_CONCORDANCE ( From a6a48e19b67ef177b165455fe023a1be48de7650 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 14:45:35 +0200 Subject: [PATCH 49/65] Add configurations --- nextflow.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 257cffc4..ccc6b0dd 100644 --- a/nextflow.config +++ b/nextflow.config @@ -274,7 +274,6 @@ manifest { // Load modules.config for DSL2 module specific options includeConfig 'conf/modules.config' -includeConfig 'conf/quilt_subworkflow.config' // initialisation step includeConfig 'conf/steps/initialisation.config' @@ -287,6 +286,8 @@ includeConfig 'conf/steps/panel_prep.config' // imputation step includeConfig 'conf/steps/imputation.config' +includeConfig 'conf/steps/imputation_glimpse1.config' +includeConfig 'conf/steps/imputation_quilt.config' // validation step includeConfig 'conf/steps/validation.config' From 22f1ea5b0d16a30359c681d6757902f10d49e2e7 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 14:46:04 +0200 Subject: [PATCH 50/65] Concat by chromosomes --- workflows/phaseimpute/main.nf | 42 ++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 8187f683..d4718d91 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -28,9 +28,11 @@ include { VCF_CHR_CHECK } from '../../subworkflows/ include { GET_PANEL } from '../../subworkflows/local/get_panel' -include { MAKE_CHUNKS } from '../../subworkflows/local/make_chunks/make_chunks' -include { IMPUTE_QUILT } from '../../subworkflows/local/impute_quilt/impute_quilt' -include { VCF_CONCATENATE_BCFTOOLS } from '../../subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools' +include { MAKE_CHUNKS } from '../../subworkflows/local/make_chunks/make_chunks' +include { IMPUTE_QUILT } from '../../subworkflows/local/impute_quilt/impute_quilt' +include { VCF_CONCATENATE_BCFTOOLS as CONCAT_IMPUT } from '../../subworkflows/local/vcf_concatenate_bcftools' +include { VCF_CONCATENATE_BCFTOOLS as CONCAT_TRUTH } from '../../subworkflows/local/vcf_concatenate_bcftools' +include { VCF_CONCATENATE_BCFTOOLS as CONCAT_PANEL } from '../../subworkflows/local/vcf_concatenate_bcftools' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -109,10 +111,14 @@ workflow PHASEIMPUTE { .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index -> [metaPC, sites, tsv] } - ch_panel_sites = GET_PANEL.out.panel + CONCAT_PANEL(GET_PANEL.out.panel .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index - -> [metaPC, sites, s_index] + -> [[id:metaPC.panel], sites, s_index] } + ) + ch_panel_sites = CONCAT_PANEL.out.vcf_tbi_join + ch_versions = ch_versions.mix(CONCAT_PANEL.out.versions) + ch_panel_phased = GET_PANEL.out.panel .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index -> [metaPC, phased, p_index] @@ -156,10 +162,8 @@ workflow PHASEIMPUTE { ch_multiqc_files = ch_multiqc_files.mix(VCF_IMPUTE_GLIMPSE1.out.chunk_chr.map{ [it[1]]}) ch_versions = ch_versions.mix(VCF_IMPUTE_GLIMPSE1.out.versions) - VCF_CONCATENATE_BCFTOOLS(output_glimpse1) - ch_impute_output = ch_impute_output.mix(VCF_CONCATENATE_BCFTOOLS.out.vcf_tbi_join) - ch_versions = ch_versions.mix(VCF_CONCATENATE_BCFTOOLS.out.versions) - + // Add to output channel + ch_impute_output = ch_impute_output.mix(output_glimpse1) } if (params.tools.contains("glimpse2")) { error "Glimpse2 not yet implemented" @@ -187,12 +191,15 @@ workflow PHASEIMPUTE { // Impute BAMs with QUILT IMPUTE_QUILT(MAKE_CHUNKS.out.ch_hap_legend, ch_input_quilt, MAKE_CHUNKS.out.ch_chunks) + ch_versions = ch_versions.mix(IMPUTE_QUILT.out.versions) - // Concatenate results - VCF_CONCATENATE_BCFTOOLS(IMPUTE_QUILT.out.ch_vcf_tbi) - + // Add to output channel + ch_impute_output = ch_impute_output.mix(IMPUTE_QUILT.out.ch_vcf_tbi) } - ch_input_validate = ch_input_validate.mix(ch_impute_output) + // Concatenate by chromosomes + CONCAT_IMPUT(ch_impute_output) + ch_versions = ch_versions.mix(CONCAT_IMPUT.out.versions) + ch_input_validate = ch_input_validate.mix(CONCAT_IMPUT.out.vcf_tbi_join) } } @@ -223,11 +230,16 @@ workflow PHASEIMPUTE { .map { [it[0], it[1], it[2]] } .mix(GL_TRUTH.out.vcf) + // Concatenate by chromosomes + CONCAT_TRUTH(ch_truth_vcf) + ch_versions = ch_versions.mix(CONCAT_TRUTH.out.versions) + // Compute concordance analysis VCF_CONCORDANCE_GLIMPSE2( ch_input_validate, - ch_truth_vcf, - ch_panel_sites + CONCAT_TRUTH.out.vcf_tbi_join, + ch_panel_sites, + ch_region ) ch_multiqc_files = ch_multiqc_files.mix(VCF_CONCORDANCE_GLIMPSE2.out.multiqc_files) ch_versions = ch_versions.mix(VCF_CONCORDANCE_GLIMPSE2.out.versions) From 9443cc172cab318b21bd2ebb00b0de2e0732f88e Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Mon, 22 Apr 2024 14:51:06 +0200 Subject: [PATCH 51/65] Add sbwf config files --- conf/steps/imputation_glimpse1.config | 156 +++++++++---------- conf/steps/imputation_quilt.config | 206 +++++++++++++------------- 2 files changed, 181 insertions(+), 181 deletions(-) diff --git a/conf/steps/imputation_glimpse1.config b/conf/steps/imputation_glimpse1.config index b400ecb4..6299e63e 100644 --- a/conf/steps/imputation_glimpse1.config +++ b/conf/steps/imputation_glimpse1.config @@ -1,79 +1,79 @@ -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Config file for defining DSL2 per module options and publishing paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Available keys to override module options: - ext.args = Additional arguments appended to command in module. - ext.args2 = Second set of arguments appended to command in module (multi-tool modules). - ext.args3 = Third set of arguments appended to command in module (multi-tool modules). - ext.prefix = File name prefix for output files. ----------------------------------------------------------------------------------------- -*/ - -process { - // Configuration for the glimpse1 imputation subworkflow - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:.*' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: false - ] - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { - ext.args = [ - "-I", - "-E", - "-a 'FORMAT/DP'" - ].join(' ') - ext.args2 = [ - "-Aim", - "-C alleles" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.call" } - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1/" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { - ext.args = [ - "--window-size 200000", - "--buffer-size 20000" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.chunk" } - publishDir = [ - enabled: false - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { - ext.args = [ - "--impute-reference-only-variants" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.phase" } - ext.suffix = "bcf" - publishDir = [ - enabled: false - ] - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_PHASE' { - publishDir = [ - enabled: false - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.ligate" } - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" } - ] - } +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + // Configuration for the glimpse1 imputation subworkflow + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:.*' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: false + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { + ext.args = [ + "-I", + "-E", + "-a 'FORMAT/DP'" + ].join(' ') + ext.args2 = [ + "-Aim", + "-C alleles" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.call" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1/" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { + ext.args = [ + "--window-size 200000", + "--buffer-size 20000" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.chunk" } + publishDir = [ + enabled: false + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { + ext.args = [ + "--impute-reference-only-variants" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.phase" } + ext.suffix = "bcf" + publishDir = [ + enabled: false + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_PHASE' { + publishDir = [ + enabled: false + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.ligate" } + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" } + ] + } } \ No newline at end of file diff --git a/conf/steps/imputation_quilt.config b/conf/steps/imputation_quilt.config index 6f255ff6..78059339 100644 --- a/conf/steps/imputation_quilt.config +++ b/conf/steps/imputation_quilt.config @@ -1,103 +1,103 @@ -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Config file for defining DSL2 per module options and publishing paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Available keys to override module options: - ext.args = Additional arguments appended to command in module. - ext.args2 = Second set of arguments appended to command in module (multi-tool modules). - ext.args3 = Third set of arguments appended to command in module (multi-tool modules). - ext.prefix = File name prefix for output files. ----------------------------------------------------------------------------------------- -*/ - -process { - - withName: CUSTOM_DUMPSOFTWAREVERSIONS { - publishDir = [ - path: { "${params.outdir}/pipeline_info" }, - mode: params.publish_dir_mode, - pattern: '*_versions.yml' - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:GLIMPSE_CHUNK' { - - ext.prefix = { "${meta.id}_${meta.chr}" } - - publishDir = [ - [ - path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}_chunk" }, - mode: params.publish_dir_mode, - ], - - - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX' { - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX_2' { - ext.args = '--tbi' - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX_3' { - ext.args = '--tbi' - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_VIEW' { - ext.args = '-v snps -Oz' - ext.prefix = { "${meta.id}_${meta.chr}_biallelic" } - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_NORM' { - ext.args = '-m +any --output-type z' - ext.prefix = { "${meta.id}_${meta.chr}_multiallelic" } - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_CONVERT' { - ext.args = '--haplegendsample test' - ext.prefix = { "${meta.id}_${meta.chr}_convert" } - cpus = 2 - memory = 400.MB - maxRetries = 2 - - publishDir = [ - [ - path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}/convert" }, - mode: params.publish_dir_mode, - ], - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:QUILT_QUILT' { - publishDir = [ - [ - path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode, - ], - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:BCFTOOLS_INDEX' { - ext.args = {[ - "--tbi", - ].join(" ").trim()} - } - -} +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + + withName: CUSTOM_DUMPSOFTWAREVERSIONS { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: params.publish_dir_mode, + pattern: '*_versions.yml' + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:GLIMPSE_CHUNK' { + + ext.prefix = { "${meta.id}_${meta.chr}" } + + publishDir = [ + [ + path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}_chunk" }, + mode: params.publish_dir_mode, + ], + + + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX' { + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX_2' { + ext.args = '--tbi' + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX_3' { + ext.args = '--tbi' + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_VIEW' { + ext.args = '-v snps -Oz' + ext.prefix = { "${meta.id}_${meta.chr}_biallelic" } + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_NORM' { + ext.args = '-m +any --output-type z' + ext.prefix = { "${meta.id}_${meta.chr}_multiallelic" } + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_CONVERT' { + ext.args = '--haplegendsample test' + ext.prefix = { "${meta.id}_${meta.chr}_convert" } + cpus = 2 + memory = 400.MB + maxRetries = 2 + + publishDir = [ + [ + path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}/convert" }, + mode: params.publish_dir_mode, + ], + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:QUILT_QUILT' { + publishDir = [ + [ + path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode, + ], + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:BCFTOOLS_INDEX' { + ext.args = {[ + "--tbi", + ].join(" ").trim()} + } + +} From e1525ce9b819eb66ffc602c2632c70a41b2fde4e Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Mon, 22 Apr 2024 14:56:19 +0200 Subject: [PATCH 52/65] Fix linting --- conf/steps/imputation_glimpse1.config | 156 +++++++++---------- conf/steps/imputation_quilt.config | 206 +++++++++++++------------- workflows/phaseimpute/main.nf | 2 +- 3 files changed, 182 insertions(+), 182 deletions(-) diff --git a/conf/steps/imputation_glimpse1.config b/conf/steps/imputation_glimpse1.config index 6299e63e..b400ecb4 100644 --- a/conf/steps/imputation_glimpse1.config +++ b/conf/steps/imputation_glimpse1.config @@ -1,79 +1,79 @@ -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Config file for defining DSL2 per module options and publishing paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Available keys to override module options: - ext.args = Additional arguments appended to command in module. - ext.args2 = Second set of arguments appended to command in module (multi-tool modules). - ext.args3 = Third set of arguments appended to command in module (multi-tool modules). - ext.prefix = File name prefix for output files. ----------------------------------------------------------------------------------------- -*/ - -process { - // Configuration for the glimpse1 imputation subworkflow - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:.*' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - enabled: false - ] - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { - ext.args = [ - "-I", - "-E", - "-a 'FORMAT/DP'" - ].join(' ') - ext.args2 = [ - "-Aim", - "-C alleles" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.call" } - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1/" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { - ext.args = [ - "--window-size 200000", - "--buffer-size 20000" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.chunk" } - publishDir = [ - enabled: false - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { - ext.args = [ - "--impute-reference-only-variants" - ].join(' ') - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.phase" } - ext.suffix = "bcf" - publishDir = [ - enabled: false - ] - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_PHASE' { - publishDir = [ - enabled: false - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { - ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.ligate" } - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { - publishDir = [ - path: { "${params.outdir}/imputation/glimpse1" } - ] - } +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + // Configuration for the glimpse1 imputation subworkflow + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:.*' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + enabled: false + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_MPILEUP' { + ext.args = [ + "-I", + "-E", + "-a 'FORMAT/DP'" + ].join(' ') + ext.args2 = [ + "-Aim", + "-C alleles" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.call" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1/" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_CHUNK' { + ext.args = [ + "--window-size 200000", + "--buffer-size 20000" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.chunk" } + publishDir = [ + enabled: false + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { + ext.args = [ + "--impute-reference-only-variants" + ].join(' ') + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.phase" } + ext.suffix = "bcf" + publishDir = [ + enabled: false + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_PHASE' { + publishDir = [ + enabled: false + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.ligate" } + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { + publishDir = [ + path: { "${params.outdir}/imputation/glimpse1" } + ] + } } \ No newline at end of file diff --git a/conf/steps/imputation_quilt.config b/conf/steps/imputation_quilt.config index 78059339..6f255ff6 100644 --- a/conf/steps/imputation_quilt.config +++ b/conf/steps/imputation_quilt.config @@ -1,103 +1,103 @@ -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Config file for defining DSL2 per module options and publishing paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Available keys to override module options: - ext.args = Additional arguments appended to command in module. - ext.args2 = Second set of arguments appended to command in module (multi-tool modules). - ext.args3 = Third set of arguments appended to command in module (multi-tool modules). - ext.prefix = File name prefix for output files. ----------------------------------------------------------------------------------------- -*/ - -process { - - withName: CUSTOM_DUMPSOFTWAREVERSIONS { - publishDir = [ - path: { "${params.outdir}/pipeline_info" }, - mode: params.publish_dir_mode, - pattern: '*_versions.yml' - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:GLIMPSE_CHUNK' { - - ext.prefix = { "${meta.id}_${meta.chr}" } - - publishDir = [ - [ - path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}_chunk" }, - mode: params.publish_dir_mode, - ], - - - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX' { - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX_2' { - ext.args = '--tbi' - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX_3' { - ext.args = '--tbi' - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_VIEW' { - ext.args = '-v snps -Oz' - ext.prefix = { "${meta.id}_${meta.chr}_biallelic" } - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_NORM' { - ext.args = '-m +any --output-type z' - ext.prefix = { "${meta.id}_${meta.chr}_multiallelic" } - cpus = 2 - memory = 400.MB - maxRetries = 2 - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_CONVERT' { - ext.args = '--haplegendsample test' - ext.prefix = { "${meta.id}_${meta.chr}_convert" } - cpus = 2 - memory = 400.MB - maxRetries = 2 - - publishDir = [ - [ - path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}/convert" }, - mode: params.publish_dir_mode, - ], - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:QUILT_QUILT' { - publishDir = [ - [ - path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode, - ], - ] - } - - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:BCFTOOLS_INDEX' { - ext.args = {[ - "--tbi", - ].join(" ").trim()} - } - -} +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Config file for defining DSL2 per module options and publishing paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available keys to override module options: + ext.args = Additional arguments appended to command in module. + ext.args2 = Second set of arguments appended to command in module (multi-tool modules). + ext.args3 = Third set of arguments appended to command in module (multi-tool modules). + ext.prefix = File name prefix for output files. +---------------------------------------------------------------------------------------- +*/ + +process { + + withName: CUSTOM_DUMPSOFTWAREVERSIONS { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: params.publish_dir_mode, + pattern: '*_versions.yml' + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:GLIMPSE_CHUNK' { + + ext.prefix = { "${meta.id}_${meta.chr}" } + + publishDir = [ + [ + path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}_chunk" }, + mode: params.publish_dir_mode, + ], + + + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX' { + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX_2' { + ext.args = '--tbi' + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX_3' { + ext.args = '--tbi' + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_VIEW' { + ext.args = '-v snps -Oz' + ext.prefix = { "${meta.id}_${meta.chr}_biallelic" } + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_NORM' { + ext.args = '-m +any --output-type z' + ext.prefix = { "${meta.id}_${meta.chr}_multiallelic" } + cpus = 2 + memory = 400.MB + maxRetries = 2 + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_CONVERT' { + ext.args = '--haplegendsample test' + ext.prefix = { "${meta.id}_${meta.chr}_convert" } + cpus = 2 + memory = 400.MB + maxRetries = 2 + + publishDir = [ + [ + path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}/convert" }, + mode: params.publish_dir_mode, + ], + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:QUILT_QUILT' { + publishDir = [ + [ + path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: params.publish_dir_mode, + ], + ] + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:BCFTOOLS_INDEX' { + ext.args = {[ + "--tbi", + ].join(" ").trim()} + } + +} diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index d4718d91..34b8f02d 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -118,7 +118,7 @@ workflow PHASEIMPUTE { ) ch_panel_sites = CONCAT_PANEL.out.vcf_tbi_join ch_versions = ch_versions.mix(CONCAT_PANEL.out.versions) - + ch_panel_phased = GET_PANEL.out.panel .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_index -> [metaPC, phased, p_index] From 234bff9d9502862dea91530806273e7c0f57b32f Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Mon, 22 Apr 2024 14:58:31 +0200 Subject: [PATCH 53/65] Fix linting --- conf/steps/imputation_glimpse1.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/steps/imputation_glimpse1.config b/conf/steps/imputation_glimpse1.config index b400ecb4..9ad7fcce 100644 --- a/conf/steps/imputation_glimpse1.config +++ b/conf/steps/imputation_glimpse1.config @@ -76,4 +76,4 @@ process { path: { "${params.outdir}/imputation/glimpse1" } ] } -} \ No newline at end of file +} From 8cd1a86373455b8e23b67df07dcc40046a7991c5 Mon Sep 17 00:00:00 2001 From: LouisLeNezet Date: Mon, 22 Apr 2024 15:03:38 +0200 Subject: [PATCH 54/65] Fix input channel --- workflows/phaseimpute/main.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 34b8f02d..d8ff0693 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -178,12 +178,12 @@ workflow PHASEIMPUTE { MAKE_CHUNKS(ch_panel, ch_fasta) // Make bamlist from bam input - ch_bamlist = ch_input + ch_bamlist = ch_input_impute .map { it[1].tokenize('/').last() } .collectFile( name: "bamlist.txt", newLine: true, sort: true ) // Create input QUILT - ch_input_quilt = ch_input + ch_input_quilt = ch_input_impute .map { meta, bam, bai -> [["id": "all_samples"], bam, bai] } .groupTuple () .combine ( ch_bamlist ) From 8ba766daca985cde1a105e668c274269807248da Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 16:07:33 +0200 Subject: [PATCH 55/65] Compute bam list from tuple --- modules/nf-core/quilt/quilt/main.nf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/nf-core/quilt/quilt/main.nf b/modules/nf-core/quilt/quilt/main.nf index 3068ba7c..982479b5 100644 --- a/modules/nf-core/quilt/quilt/main.nf +++ b/modules/nf-core/quilt/quilt/main.nf @@ -8,7 +8,7 @@ process QUILT_QUILT { 'biocontainers/r-quilt:1.0.5--r43h06b5641_0' }" input: - tuple val(meta), path(bams), path(bais), path(bamlist), path(reference_haplotype_file), path(reference_legend_file), val(chr), val(regions_start), val(regions_end), val(ngen), val(buffer), path(genetic_map_file) + tuple val(meta), path(bams), path(bais), path(reference_haplotype_file), path(reference_legend_file), val(chr), val(regions_start), val(regions_end), val(ngen), val(buffer), path(genetic_map_file) tuple val(meta2), path(posfile), path(phasefile) tuple val(meta3), path(fasta) @@ -27,18 +27,18 @@ process QUILT_QUILT { def prefix = task.ext.prefix ?: "${meta.id}" def extensions = bams.collect { it.extension } def extension = extensions.flatten().unique() - def list_command = extension == ["bam"] ? "--bamlist=${bamlist}" : - extension == ["cram"] ? "--cramlist=${bamlist} --reference=${fasta}" : "" + def list_command = extension == ["bam"] ? "--bamlist=" : + extension == ["cram"] ? "--reference=${fasta} --cramlist=" : "" def genetic_map_file_command = genetic_map_file ? "--genetic_map_file=${genetic_map_file}" : "" def posfile_command = posfile ? "--posfile=${posfile}" : "" def phasefile_command = phasefile ? "--phasefile=${phasefile}" : "" if (!(args ==~ /.*--seed.*/)) {args += " --seed=1"} """ - + printf "%s\\n" $bams | tr -d '[],' > all_files.txt QUILT.R \\ - $list_command \\ + ${list_command}all_files.txt \\ $genetic_map_file_command \\ $posfile_command \\ $phasefile_command \\ From aad86d56b97aa25ca6f7f67d167dd061b3716bc3 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 16:08:45 +0200 Subject: [PATCH 56/65] Update quilt sbwf to compute bamlist from tuple --- .../local/impute_quilt/impute_quilt.nf | 21 +++++++++---------- workflows/phaseimpute/main.nf | 9 +------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/subworkflows/local/impute_quilt/impute_quilt.nf b/subworkflows/local/impute_quilt/impute_quilt.nf index decc61f2..4dc685b8 100644 --- a/subworkflows/local/impute_quilt/impute_quilt.nf +++ b/subworkflows/local/impute_quilt/impute_quilt.nf @@ -5,9 +5,9 @@ include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index/main' workflow IMPUTE_QUILT { take: - ch_hap_legend // channel: [ val(meta), hap, legend ] - ch_input // channel: [ val(meta), bam, bai ] - ch_chunks // channel: [ val(meta), start_coordinate, end_coordinate, number ] + ch_hap_legend // channel: [ [panel, chr], hap, legend ] + ch_input // channel: [ [id, chr], bam, bai ] + ch_chunks // channel: [ [panel, chr], start_coordinate, end_coordinate, number ] main: @@ -23,7 +23,6 @@ workflow IMPUTE_QUILT { ngen = params.ngen buffer = params.buffer - ch_bam_bamlist = ch_input if (genetic_map_file.isEmpty()) { ch_hap_chunks = ch_hap_legend.combine(ch_chunks, by:0).map { it + ngen + buffer + [[]] } @@ -32,14 +31,14 @@ workflow IMPUTE_QUILT { ch_hap_chunks = ch_hap_legend.join(ch_chunks, by:0).join(genetic_map_file) } - ch_quilt = ch_bam_bamlist.combine(ch_hap_chunks) - ch_quilt_input = ch_quilt.map { it.take(4) + it.drop(5) } + ch_quilt = ch_input.combine(ch_hap_chunks) + ch_quilt_input = ch_quilt.map { it.take(3) + it.drop(4) } // Add metamap with chromosome information ch_quilt_input = ch_quilt_input - .map{ meta, bam, bai, bamlist, hap, legend, chr, start, end, ngen2, buffer2, genetic -> - return [['id': meta.id, 'chr': chr] , bam, bai, bamlist, hap, legend, chr, start, end, ngen2, buffer2, genetic] - } + .map{ meta, bam, bai, hap, legend, chr, start, end, ngen2, buffer2, genetic -> + [['id': meta.id, 'chr': chr], bam, bai, hap, legend, chr, start, end, ngen2, buffer2, genetic] + } // Run QUILT QUILT_QUILT ( ch_quilt_input, posfile_phasefile, fasta ) @@ -51,6 +50,6 @@ workflow IMPUTE_QUILT { ch_vcf_tbi = QUILT_QUILT.out.vcf.join(BCFTOOLS_INDEX.out.tbi) emit: - ch_vcf_tbi // channel: [ meta, vcf, tbi ] - versions = ch_versions // channel: [ versions.yml ] + vcf_tbi = ch_vcf_tbi // channel: [ meta, vcf, tbi ] + versions = ch_versions // channel: [ versions.yml ] } diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index d8ff0693..9be5d32e 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -177,17 +177,10 @@ workflow PHASEIMPUTE { // Create chunks from reference VCF MAKE_CHUNKS(ch_panel, ch_fasta) - // Make bamlist from bam input - ch_bamlist = ch_input_impute - .map { it[1].tokenize('/').last() } - .collectFile( name: "bamlist.txt", newLine: true, sort: true ) - // Create input QUILT ch_input_quilt = ch_input_impute - .map { meta, bam, bai -> [["id": "all_samples"], bam, bai] } + .map { meta, bam, bai -> [["id": "all_samples"] + meta.subMap("chr", "region"), bam, bai] } .groupTuple () - .combine ( ch_bamlist ) - .collect () // Impute BAMs with QUILT IMPUTE_QUILT(MAKE_CHUNKS.out.ch_hap_legend, ch_input_quilt, MAKE_CHUNKS.out.ch_chunks) From e132be571396cc7f5d37b814f91c415aaf603030 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 16:36:52 +0200 Subject: [PATCH 57/65] Join hap and legend by chromosomes --- .../local/impute_quilt/impute_quilt.nf | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/subworkflows/local/impute_quilt/impute_quilt.nf b/subworkflows/local/impute_quilt/impute_quilt.nf index 4dc685b8..a4c1ee66 100644 --- a/subworkflows/local/impute_quilt/impute_quilt.nf +++ b/subworkflows/local/impute_quilt/impute_quilt.nf @@ -31,17 +31,20 @@ workflow IMPUTE_QUILT { ch_hap_chunks = ch_hap_legend.join(ch_chunks, by:0).join(genetic_map_file) } - ch_quilt = ch_input.combine(ch_hap_chunks) - ch_quilt_input = ch_quilt.map { it.take(3) + it.drop(4) } - - // Add metamap with chromosome information - ch_quilt_input = ch_quilt_input - .map{ meta, bam, bai, hap, legend, chr, start, end, ngen2, buffer2, genetic -> - [['id': meta.id, 'chr': chr], bam, bai, hap, legend, chr, start, end, ngen2, buffer2, genetic] + ch_quilt = ch_input + .map{ metaIC, bam, bai -> [metaIC.subMap("chr"), metaIC, bam, bai]} + .join(ch_hap_chunks + .map{ metaIC, hap, legend, chr, start, end, ngen, buffer, gmap -> + [metaIC.subMap("chr"), metaIC, hap, legend, chr, start, end, ngen, buffer, gmap] + } + ) + .map { + metaC, metaIC, bam, bai, metaPC, hap, legend, chr, start, end, ngen, buffer, gmap -> + [metaIC + ["panel": metaPC.id], bam, bai, hap, legend, chr, start, end, ngen, buffer, gmap] } // Run QUILT - QUILT_QUILT ( ch_quilt_input, posfile_phasefile, fasta ) + QUILT_QUILT ( ch_quilt, posfile_phasefile, fasta ) // Index imputed VCF BCFTOOLS_INDEX(QUILT_QUILT.out.vcf) From 9d9cdf2d130f48128277d86b73dd1e6acb9cd5cb Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 22 Apr 2024 16:37:06 +0200 Subject: [PATCH 58/65] Rename output channel --- workflows/phaseimpute/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 9be5d32e..8f51ddf1 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -187,7 +187,7 @@ workflow PHASEIMPUTE { ch_versions = ch_versions.mix(IMPUTE_QUILT.out.versions) // Add to output channel - ch_impute_output = ch_impute_output.mix(IMPUTE_QUILT.out.ch_vcf_tbi) + ch_impute_output = ch_impute_output.mix(IMPUTE_QUILT.out.vcf_tbi) } // Concatenate by chromosomes CONCAT_IMPUT(ch_impute_output) From 01310211787c412261c86777d27b620be0790848 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 23 Apr 2024 18:08:53 +0200 Subject: [PATCH 59/65] Update quilt --- modules/nf-core/quilt/quilt/environment.yml | 3 +- modules/nf-core/quilt/quilt/meta.yml | 11 +- .../nf-core/quilt/quilt/tests/main.nf.test | 132 ++++++ .../quilt/quilt/tests/main.nf.test.snap | 376 ++++++++++++++++++ .../quilt/quilt/tests/quilt_default.config | 6 + .../quilt/quilt/tests/quilt_noseed.config | 6 + .../quilt/quilt/tests/quilt_optional.config | 6 + modules/nf-core/quilt/quilt/tests/tags.yml | 2 + 8 files changed, 535 insertions(+), 7 deletions(-) create mode 100644 modules/nf-core/quilt/quilt/tests/main.nf.test create mode 100644 modules/nf-core/quilt/quilt/tests/main.nf.test.snap create mode 100644 modules/nf-core/quilt/quilt/tests/quilt_default.config create mode 100644 modules/nf-core/quilt/quilt/tests/quilt_noseed.config create mode 100644 modules/nf-core/quilt/quilt/tests/quilt_optional.config create mode 100644 modules/nf-core/quilt/quilt/tests/tags.yml diff --git a/modules/nf-core/quilt/quilt/environment.yml b/modules/nf-core/quilt/quilt/environment.yml index 9872e819..a2161a65 100644 --- a/modules/nf-core/quilt/quilt/environment.yml +++ b/modules/nf-core/quilt/quilt/environment.yml @@ -4,4 +4,5 @@ channels: - bioconda - defaults dependencies: - - bioconda::r-quilt=1.0.5 + - bioconda::r-quilt=1.0.5=r43h06b5641_0 + - r-base=4.3.1 diff --git a/modules/nf-core/quilt/quilt/meta.yml b/modules/nf-core/quilt/quilt/meta.yml index 34c67a79..e4653983 100644 --- a/modules/nf-core/quilt/quilt/meta.yml +++ b/modules/nf-core/quilt/quilt/meta.yml @@ -13,7 +13,7 @@ tools: documentation: "https://github.com/rwdavies/quilt" tool_dev_url: "https://github.com/rwdavies/quilt" doi: "10.1038/s41588-021-00877-0" - licence: "['GPL v3']" + licence: ["GPL v3"] input: - meta: type: map @@ -28,10 +28,6 @@ input: type: file description: (Mandatory) BAM/CRAM index files pattern: "*.{bai}" - - bamlist: - type: file - description: (Mandatory) "Path to file with bam file locations. File is one row per entry, path to bam files. Bam index files should exist in same directory as for each bam, suffixed either .bam.bai or .bai. - pattern: "*.{txt}" - reference_haplotype_file: type: file description: (Mandatory) Reference haplotype file in IMPUTE format (file with no header and no rownames, one row per SNP, one column per reference haplotype, space separated, values must be 0 or 1) @@ -99,9 +95,12 @@ output: type: file description: TBI file of the VCF. pattern: "*.{vcf.gz.tbi}" - - RData: + - rdata: type: directory description: Optional directory path to prepared RData file with reference objects (useful with --save_prepared_reference=TRUE). + - plots: + type: directory + description: Optional directory path to save plots. authors: - "@atrigila" maintainers: diff --git a/modules/nf-core/quilt/quilt/tests/main.nf.test b/modules/nf-core/quilt/quilt/tests/main.nf.test new file mode 100644 index 00000000..2d80516d --- /dev/null +++ b/modules/nf-core/quilt/quilt/tests/main.nf.test @@ -0,0 +1,132 @@ +// Input data +def path = "file('https://github.com/nf-core/test-datasets/raw/modules/data/delete_me/quilt/" +def bam = "[${path}NA12878.haplotagged.1.0.bam', checkIfExists: true), ${path}NA12878.ont.1.0.bam', checkIfExists: true), ${path}NA12878.illumina.1.0.bam', checkIfExists: true)]" +def bai = "[${path}NA12878.haplotagged.1.0.bam.bai', checkIfExists: true), ${path}NA12878.ont.1.0.bam.bai', checkIfExists: true),${path}NA12878.illumina.1.0.bam.bai', checkIfExists: true)]" + +// Input reference data +def reference_haplotype_file = "file('https://github.com/nf-core/test-datasets/raw/modules/data/delete_me/quilt/ALL.chr20_GRCh38.genotypes.20170504.chr20.2000001.2100000.noNA12878.hap.gz', checkIfExists: true)" +def reference_legend_file = "file('https://github.com/nf-core/test-datasets/raw/modules/data/delete_me/quilt/ALL.chr20_GRCh38.genotypes.20170504.chr20.2000001.2100000.noNA12878.legend.gz', checkIfExists: true)" +def genetic_map_file = "file('https://github.com/nf-core/test-datasets/raw/modules/data/delete_me/quilt/CEU-chr20-final.b38.txt.gz', checkIfExists: true)" + +// Parameters +def chr = "'chr20'" +def regions_start = "2000001" +def regions_end = "2100000" +def ngen = "100" +def buffer = "10000" + + +// (optional) input truth data +def posfile = "file('https://github.com/nf-core/test-datasets/raw/modules/data/delete_me/quilt/ALL.chr20_GRCh38.genotypes.20170504.chr20.2000001.2100000.posfile.txt', checkIfExists: true)" +def phasefile = "file('https://github.com/nf-core/test-datasets/raw/modules/data/delete_me/quilt/ALL.chr20_GRCh38.genotypes.20170504.chr20.2000001.2100000.phasefile.txt', checkIfExists: true)" +def posfile_phasefile = "[[ id:'test', chr:'chr20' ], [$posfile], [$phasefile]]" +def fasta = "[[id:'test'], []]" + +// Input channel quilt +def ch_input = "[ id:'test', chr:'chr20' ], $bam, $bai, [$reference_haplotype_file], [$reference_legend_file], $chr, $regions_start, $regions_end, $ngen, $buffer" +def ch_input_gmap = "[$ch_input, [$genetic_map_file]]" +def ch_input_nogmap = "[$ch_input, []]" + +nextflow_process { + + name "Test Process QUILT" + script "../main.nf" + process "QUILT_QUILT" + + tag "modules" + tag "modules_nfcore" + tag "quilt/quilt" + tag "quilt" + + test("QUILT") { + config ("./quilt_default.config") + when { + process { + """ + input[0] = $ch_input_gmap + input[1] = $posfile_phasefile + input[2] = $fasta + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + + test("QUILT no optional files") { + config ("./quilt_default.config") + when { + process { + """ + input[0] = $ch_input_nogmap + input[1] = [[id: null], [], []] + input[2] = $fasta + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + + test("QUILT optional output") { + config ("./quilt_optional.config") + when { + process { + """ + input[0] = $ch_input_gmap + input[1] = $posfile_phasefile + input[2] = $fasta + """ + } + } + + then { + def dir = new File(process.out.plots[0][1]) + def list = [] + dir.eachFileRecurse { file -> list << file.getName() } + assertAll( + { assert process.success }, + { assert snapshot( + process.out.vcf + process.out.tbi + + list.sort() + + process.out.rdata + process.out.versions + ).match() } + ) + } + + } + + test("QUILT no seed") { + config ("./quilt_noseed.config") + when { + process { + """ + input[0] = $ch_input_gmap + input[1] = $posfile_phasefile + input[2] = $fasta + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + +} \ No newline at end of file diff --git a/modules/nf-core/quilt/quilt/tests/main.nf.test.snap b/modules/nf-core/quilt/quilt/tests/main.nf.test.snap new file mode 100644 index 00000000..191b519a --- /dev/null +++ b/modules/nf-core/quilt/quilt/tests/main.nf.test.snap @@ -0,0 +1,376 @@ +{ + "QUILT": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz:md5,32f539c80971e2e8e0c31870be094a25" + ] + ], + "1": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz.tbi:md5,4607cdcb20599cbebd1ccf76d4dc56ae" + ] + ], + "2": [ + [ + { + "id": "test", + "chr": "chr20" + }, + [ + + ] + ] + ], + "3": [ + + ], + "4": [ + "versions.yml:md5,6d07cd60389ff6981a44004872bd16b7" + ], + "plots": [ + + ], + "rdata": [ + [ + { + "id": "test", + "chr": "chr20" + }, + [ + + ] + ] + ], + "tbi": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz.tbi:md5,4607cdcb20599cbebd1ccf76d4dc56ae" + ] + ], + "vcf": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz:md5,32f539c80971e2e8e0c31870be094a25" + ] + ], + "versions": [ + "versions.yml:md5,6d07cd60389ff6981a44004872bd16b7" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-04-23T17:27:54.607934432" + }, + "QUILT no seed": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz:md5,32f539c80971e2e8e0c31870be094a25" + ] + ], + "1": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz.tbi:md5,4607cdcb20599cbebd1ccf76d4dc56ae" + ] + ], + "2": [ + [ + { + "id": "test", + "chr": "chr20" + }, + [ + + ] + ] + ], + "3": [ + + ], + "4": [ + "versions.yml:md5,6d07cd60389ff6981a44004872bd16b7" + ], + "plots": [ + + ], + "rdata": [ + [ + { + "id": "test", + "chr": "chr20" + }, + [ + + ] + ] + ], + "tbi": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz.tbi:md5,4607cdcb20599cbebd1ccf76d4dc56ae" + ] + ], + "vcf": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz:md5,32f539c80971e2e8e0c31870be094a25" + ] + ], + "versions": [ + "versions.yml:md5,6d07cd60389ff6981a44004872bd16b7" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-04-23T17:29:31.357244889" + }, + "QUILT no optional files": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz:md5,3fde483728ef2287416b2340c06aaf85" + ] + ], + "1": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz.tbi:md5,20d9e8cda03fc84482f3aa53a0c94fb6" + ] + ], + "2": [ + [ + { + "id": "test", + "chr": "chr20" + }, + [ + + ] + ] + ], + "3": [ + + ], + "4": [ + "versions.yml:md5,6d07cd60389ff6981a44004872bd16b7" + ], + "plots": [ + + ], + "rdata": [ + [ + { + "id": "test", + "chr": "chr20" + }, + [ + + ] + ] + ], + "tbi": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz.tbi:md5,20d9e8cda03fc84482f3aa53a0c94fb6" + ] + ], + "vcf": [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz:md5,3fde483728ef2287416b2340c06aaf85" + ] + ], + "versions": [ + "versions.yml:md5,6d07cd60389ff6981a44004872bd16b7" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-04-23T17:28:16.39358682" + }, + "QUILT optional output": { + "content": [ + [ + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz:md5,8352fbcabdd102a8ba2c4490e0834287" + ], + [ + { + "id": "test", + "chr": "chr20" + }, + "quilt.chr20.2000001.2100000.vcf.gz.tbi:md5,88d16933f2ac53058b7a5d5c849dc19a" + ], + "haps.NA12878.chr20.2000001.2100000_igs.1.0.truth.png", + "haps.NA12878.chr20.2000001.2100000_igs.1.it1.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.1.it2.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.1.it3.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.2.0.truth.png", + "haps.NA12878.chr20.2000001.2100000_igs.2.it1.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.2.it2.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.2.it3.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.3.0.truth.png", + "haps.NA12878.chr20.2000001.2100000_igs.3.it1.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.3.it2.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.3.it3.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.4.0.truth.png", + "haps.NA12878.chr20.2000001.2100000_igs.4.it1.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.4.it2.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.4.it3.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.5.0.truth.png", + "haps.NA12878.chr20.2000001.2100000_igs.5.it1.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.5.it2.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.5.it3.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.6.0.truth.png", + "haps.NA12878.chr20.2000001.2100000_igs.6.it1.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.6.it2.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.6.it3.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.7.0.truth.png", + "haps.NA12878.chr20.2000001.2100000_igs.7.it1.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.7.it2.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.7.it3.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.8.0.truth.png", + "haps.NA12878.chr20.2000001.2100000_igs.8.it1.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.8.it2.gibbs.png", + "haps.NA12878.chr20.2000001.2100000_igs.8.it3.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.1.0.truth.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.1.it1.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.1.it2.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.1.it3.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.2.0.truth.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.2.it1.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.2.it2.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.2.it3.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.3.0.truth.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.3.it1.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.3.it2.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.3.it3.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.4.0.truth.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.4.it1.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.4.it2.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.4.it3.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.5.0.truth.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.5.it1.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.5.it2.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.5.it3.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.6.0.truth.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.6.it1.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.6.it2.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.6.it3.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.7.0.truth.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.7.it1.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.7.it2.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.7.it3.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.8.0.truth.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.8.it1.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.8.it2.gibbs.png", + "haps.NA12878HT.chr20.2000001.2100000_igs.8.it3.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.1.0.truth.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.1.it1.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.1.it2.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.1.it3.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.2.0.truth.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.2.it1.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.2.it2.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.2.it3.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.3.0.truth.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.3.it1.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.3.it2.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.3.it3.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.4.0.truth.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.4.it1.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.4.it2.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.4.it3.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.5.0.truth.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.5.it1.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.5.it2.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.5.it3.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.6.0.truth.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.6.it1.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.6.it2.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.6.it3.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.7.0.truth.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.7.it1.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.7.it2.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.7.it3.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.8.0.truth.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.8.it1.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.8.it2.gibbs.png", + "haps.NA12878ONT.chr20.2000001.2100000_igs.8.it3.gibbs.png", + [ + { + "id": "test", + "chr": "chr20" + }, + [ + "QUILT_prepared_reference.chr20.2000001.2100000.RData:md5,c2bbcf91085f33536fbaf094b4f0ea05" + ] + ], + "versions.yml:md5,6d07cd60389ff6981a44004872bd16b7" + ] + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-04-23T17:28:59.999377862" + } +} \ No newline at end of file diff --git a/modules/nf-core/quilt/quilt/tests/quilt_default.config b/modules/nf-core/quilt/quilt/tests/quilt_default.config new file mode 100644 index 00000000..87f87b9a --- /dev/null +++ b/modules/nf-core/quilt/quilt/tests/quilt_default.config @@ -0,0 +1,6 @@ +process { + cpus = 1 // More than 1 cpu may lead to different md5sum + withName: QUILT_QUILT { + ext.args = "--seed=1" + } +} diff --git a/modules/nf-core/quilt/quilt/tests/quilt_noseed.config b/modules/nf-core/quilt/quilt/tests/quilt_noseed.config new file mode 100644 index 00000000..e9f81a34 --- /dev/null +++ b/modules/nf-core/quilt/quilt/tests/quilt_noseed.config @@ -0,0 +1,6 @@ +process { + cpus = 1 // More than 1 cpu may lead to different md5sum + withName: QUILT_QUILT { + ext.args = "" + } +} diff --git a/modules/nf-core/quilt/quilt/tests/quilt_optional.config b/modules/nf-core/quilt/quilt/tests/quilt_optional.config new file mode 100644 index 00000000..cfbd1353 --- /dev/null +++ b/modules/nf-core/quilt/quilt/tests/quilt_optional.config @@ -0,0 +1,6 @@ +process { + cpus = 1 // More than 1 cpu may lead to different md5sum + withName: QUILT_QUILT { + ext.args = "--save_prepared_reference=TRUE --make_plots=TRUE --seed=1" + } +} diff --git a/modules/nf-core/quilt/quilt/tests/tags.yml b/modules/nf-core/quilt/quilt/tests/tags.yml new file mode 100644 index 00000000..ac1b9092 --- /dev/null +++ b/modules/nf-core/quilt/quilt/tests/tags.yml @@ -0,0 +1,2 @@ +quilt/quilt: + - "modules/nf-core/quilt/quilt/**" From 35350e52f32be15e60cf3be210f641131b850360 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 23 Apr 2024 18:09:09 +0200 Subject: [PATCH 60/65] Update quilt --- modules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules.json b/modules.json index 18a20a10..02349dbf 100644 --- a/modules.json +++ b/modules.json @@ -121,7 +121,7 @@ }, "quilt/quilt": { "branch": "master", - "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "git_sha": "46265545d61e7f482adf40de941cc9a94e479bbe", "installed_by": ["modules"] }, "samtools/coverage": { From 1dfea310b2f9b6529d87751cdfd00c35bef237e7 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 23 Apr 2024 20:43:41 +0200 Subject: [PATCH 61/65] Add annotation to genotype likelihood to set-id --- conf/steps/imputation_glimpse1.config | 9 +++++++++ conf/steps/validation.config | 9 +++++++++ subworkflows/local/compute_gl/main.nf | 18 +++++++++++++++--- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/conf/steps/imputation_glimpse1.config b/conf/steps/imputation_glimpse1.config index 9ad7fcce..30bd848e 100644 --- a/conf/steps/imputation_glimpse1.config +++ b/conf/steps/imputation_glimpse1.config @@ -33,6 +33,15 @@ process { ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.call" } } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_ANNOTATE' { + ext.args = "--set-id '%CHROM:%POS:%REF:%ALT' -Oz" + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.annotate" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_INPUT:BCFTOOLS_INDEX' { + ext.args = "--tbi" + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:.*' { publishDir = [ path: { "${params.outdir}/imputation/glimpse1/" }, diff --git a/conf/steps/validation.config b/conf/steps/validation.config index 6481309c..14cbf479 100644 --- a/conf/steps/validation.config +++ b/conf/steps/validation.config @@ -32,6 +32,15 @@ process { ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}_truth.call" } } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:BCFTOOLS_ANNOTATE' { + ext.args = "--set-id '%CHROM:%POS:%REF:%ALT' -Oz" + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.annotate" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:BCFTOOLS_INDEX' { + ext.args = "--tbi" + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_TRUTH:.*' { publishDir = [ path: { "${params.outdir}/validation/concat" }, diff --git a/subworkflows/local/compute_gl/main.nf b/subworkflows/local/compute_gl/main.nf index 4f571159..b11623d4 100644 --- a/subworkflows/local/compute_gl/main.nf +++ b/subworkflows/local/compute_gl/main.nf @@ -1,6 +1,6 @@ include { BCFTOOLS_MPILEUP } from '../../../modules/nf-core/bcftools/mpileup/main.nf' include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index/main.nf' - +include { BCFTOOLS_ANNOTATE } from '../../../modules/nf-core/bcftools/annotate/main.nf' workflow COMPUTE_GL { @@ -28,8 +28,20 @@ workflow COMPUTE_GL { ) ch_versions = ch_versions.mix(BCFTOOLS_MPILEUP.out.versions.first()) - ch_output = BCFTOOLS_MPILEUP.out.vcf - .combine(BCFTOOLS_MPILEUP.out.tbi, by:0) + // Annotate the variants + BCFTOOLS_ANNOTATE(BCFTOOLS_MPILEUP.out.vcf + .join(BCFTOOLS_MPILEUP.out.tbi) + .combine(Channel.of([[], [], [], []])) + ) + ch_versions = ch_versions.mix(BCFTOOLS_ANNOTATE.out.versions.first()) + + // Index annotated VCF + BCFTOOLS_INDEX(BCFTOOLS_ANNOTATE.out.vcf) + ch_versions = ch_versions.mix(BCFTOOLS_INDEX.out.versions.first()) + + // Output + ch_output = BCFTOOLS_ANNOTATE.out.vcf + .join(BCFTOOLS_INDEX.out.tbi) ch_multiqc_files = ch_multiqc_files.mix(BCFTOOLS_MPILEUP.out.stats.map{ it[1] }) From 870c63e8c9df419629fe8726e40c0d581457521c Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 23 Apr 2024 20:44:10 +0200 Subject: [PATCH 62/65] Add annotation to quilt to set varians id --- conf/steps/imputation_quilt.config | 16 ++++++++--- .../local/impute_quilt/impute_quilt.nf | 27 ++++++++++++++----- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/conf/steps/imputation_quilt.config b/conf/steps/imputation_quilt.config index 6f255ff6..55069d64 100644 --- a/conf/steps/imputation_quilt.config +++ b/conf/steps/imputation_quilt.config @@ -86,6 +86,7 @@ process { } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:QUILT_QUILT' { + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.impute" } publishDir = [ [ path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, @@ -94,10 +95,17 @@ process { ] } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:BCFTOOLS_INDEX' { - ext.args = {[ - "--tbi", - ].join(" ").trim()} + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:INDEX1' { + ext.args = "--tbi" + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:BCFTOOLS_ANNOTATE' { + ext.args = "--set-id '%CHROM:%POS:%REF:%ALT' -Oz" + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.impute.annotate" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:INDEX2' { + ext.args = "--tbi" } } diff --git a/subworkflows/local/impute_quilt/impute_quilt.nf b/subworkflows/local/impute_quilt/impute_quilt.nf index a4c1ee66..d8231733 100644 --- a/subworkflows/local/impute_quilt/impute_quilt.nf +++ b/subworkflows/local/impute_quilt/impute_quilt.nf @@ -1,5 +1,7 @@ -include { QUILT_QUILT } from '../../../modules/nf-core/quilt/quilt/main' -include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index/main' +include { QUILT_QUILT } from '../../../modules/nf-core/quilt/quilt' +include { BCFTOOLS_ANNOTATE } from '../../../modules/nf-core/bcftools/annotate' +include { BCFTOOLS_INDEX as INDEX1 } from '../../../modules/nf-core/bcftools/index' +include { BCFTOOLS_INDEX as INDEX2 } from '../../../modules/nf-core/bcftools/index' workflow IMPUTE_QUILT { @@ -33,10 +35,10 @@ workflow IMPUTE_QUILT { ch_quilt = ch_input .map{ metaIC, bam, bai -> [metaIC.subMap("chr"), metaIC, bam, bai]} - .join(ch_hap_chunks + .combine(ch_hap_chunks .map{ metaIC, hap, legend, chr, start, end, ngen, buffer, gmap -> [metaIC.subMap("chr"), metaIC, hap, legend, chr, start, end, ngen, buffer, gmap] - } + }, by:0 ) .map { metaC, metaIC, bam, bai, metaPC, hap, legend, chr, start, end, ngen, buffer, gmap -> @@ -45,12 +47,25 @@ workflow IMPUTE_QUILT { // Run QUILT QUILT_QUILT ( ch_quilt, posfile_phasefile, fasta ) + ch_versions = ch_versions.mix(QUILT_QUILT.out.versions.first()) // Index imputed VCF - BCFTOOLS_INDEX(QUILT_QUILT.out.vcf) + INDEX1(QUILT_QUILT.out.vcf) + ch_versions = ch_versions.mix(INDEX1.out.versions.first()) + + // Annotate the variants + BCFTOOLS_ANNOTATE(QUILT_QUILT.out.vcf + .join(INDEX1.out.tbi) + .combine(Channel.of([[], [], [], []])) + ) + ch_versions = ch_versions.mix(BCFTOOLS_ANNOTATE.out.versions.first()) + + // Index imputed annotated VCF + INDEX2(BCFTOOLS_ANNOTATE.out.vcf) + ch_versions = ch_versions.mix(INDEX2.out.versions.first()) // Join VCFs and TBIs - ch_vcf_tbi = QUILT_QUILT.out.vcf.join(BCFTOOLS_INDEX.out.tbi) + ch_vcf_tbi = BCFTOOLS_ANNOTATE.out.vcf.join(INDEX2.out.tbi) emit: vcf_tbi = ch_vcf_tbi // channel: [ meta, vcf, tbi ] From 9d21a5d851a950372ac1721e4acb0563085dd1f4 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 23 Apr 2024 20:44:38 +0200 Subject: [PATCH 63/65] Remove merging of individuals for now and add it to road map --- docs/development.md | 2 ++ workflows/phaseimpute/main.nf | 7 +------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/development.md b/docs/development.md index 6c4b74a1..c2bd3d19 100644 --- a/docs/development.md +++ b/docs/development.md @@ -17,6 +17,7 @@ - [] Check if panel is necessary depending on the tool selected - [x] Set modules configuration as full path workflow:subworkflow:module - [] Where should the map file go (separate csv or in panel csv) +- [] Add support for imputation by individuals or by groups of individuals ## Run tests @@ -25,6 +26,7 @@ nextflow run main.nf -profile singularity,test --outdir results -resume nextflow run main.nf -profile singularity,test_sim --outdir results -resume nextflow run main.nf -profile singularity,test_validate --outdir results -resume nextflow run main.nf -profile singularity,test_all --outdir results -resume +nextflow run main.nf -profile singularity,test_quilt --outdir results -resume ``` ## Problematic diff --git a/workflows/phaseimpute/main.nf b/workflows/phaseimpute/main.nf index 8f51ddf1..2b653676 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -177,13 +177,8 @@ workflow PHASEIMPUTE { // Create chunks from reference VCF MAKE_CHUNKS(ch_panel, ch_fasta) - // Create input QUILT - ch_input_quilt = ch_input_impute - .map { meta, bam, bai -> [["id": "all_samples"] + meta.subMap("chr", "region"), bam, bai] } - .groupTuple () - // Impute BAMs with QUILT - IMPUTE_QUILT(MAKE_CHUNKS.out.ch_hap_legend, ch_input_quilt, MAKE_CHUNKS.out.ch_chunks) + IMPUTE_QUILT(MAKE_CHUNKS.out.ch_hap_legend, ch_input_impute, MAKE_CHUNKS.out.ch_chunks) ch_versions = ch_versions.mix(IMPUTE_QUILT.out.versions) // Add to output channel From 4d02ff0de813b76af19dcb3cfa66c81980e53303 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 24 Apr 2024 12:37:49 +0200 Subject: [PATCH 64/65] Update result directory configuration --- conf/steps/imputation.config | 2 +- conf/steps/imputation_glimpse1.config | 14 +++++--------- conf/steps/imputation_quilt.config | 19 +++++++++++-------- conf/steps/validation.config | 13 ++++--------- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/conf/steps/imputation.config b/conf/steps/imputation.config index 93a9776c..7e859eca 100644 --- a/conf/steps/imputation.config +++ b/conf/steps/imputation.config @@ -13,7 +13,7 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_IMPUT:.*' { publishDir = [ - path: { "${params.outdir}/impute/concat" }, + path: { "${params.outdir}/imputation/concat" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] diff --git a/conf/steps/imputation_glimpse1.config b/conf/steps/imputation_glimpse1.config index 30bd848e..5b3d89cd 100644 --- a/conf/steps/imputation_glimpse1.config +++ b/conf/steps/imputation_glimpse1.config @@ -56,9 +56,7 @@ process { "--buffer-size 20000" ].join(' ') ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.chunk" } - publishDir = [ - enabled: false - ] + publishDir = [ enabled: false ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_PHASE' { @@ -67,19 +65,17 @@ process { ].join(' ') ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.phase" } ext.suffix = "bcf" - publishDir = [ - enabled: false - ] + publishDir = [ enabled: false ] } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_PHASE' { - publishDir = [ - enabled: false - ] + publishDir = [ enabled: false ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:GLIMPSE_LIGATE' { ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.ligate" } } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE1:INDEX_LIGATE' { publishDir = [ path: { "${params.outdir}/imputation/glimpse1" } diff --git a/conf/steps/imputation_quilt.config b/conf/steps/imputation_quilt.config index 55069d64..f75c777e 100644 --- a/conf/steps/imputation_quilt.config +++ b/conf/steps/imputation_quilt.config @@ -20,7 +20,7 @@ process { ] } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:GLIMPSE_CHUNK' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:.*' { ext.prefix = { "${meta.id}_${meta.chr}" } @@ -28,12 +28,17 @@ process { [ path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}_chunk" }, mode: params.publish_dir_mode, + enabled: false ], ] } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:GLIMPSE_CHUNK' { + ext.prefix = { "${meta.id}_${meta.chr}" } + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:BCFTOOLS_INDEX' { cpus = 2 memory = 400.MB @@ -76,10 +81,12 @@ process { cpus = 2 memory = 400.MB maxRetries = 2 + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:.*' { publishDir = [ [ - path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}/convert" }, + path: { "${params.outdir}/imputation/quilt/" }, mode: params.publish_dir_mode, ], ] @@ -87,16 +94,12 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:QUILT_QUILT' { ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.impute" } - publishDir = [ - [ - path: { "${params.outdir}/imputation/quilt/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode, - ], - ] + publishDir = [enabled: false] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:INDEX1' { ext.args = "--tbi" + publishDir = [enabled: false] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:BCFTOOLS_ANNOTATE' { diff --git a/conf/steps/validation.config b/conf/steps/validation.config index 14cbf479..e7a8018a 100644 --- a/conf/steps/validation.config +++ b/conf/steps/validation.config @@ -17,6 +17,7 @@ process { path: { "${params.outdir}/validation/truth" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + enabled: false ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:GL_TRUTH:BCFTOOLS_MPILEUP' { @@ -72,9 +73,7 @@ process { withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GLIMPSE2_CONCORDANCE' { ext.prefix = { "${meta.id}.concordance" } ext.args = "--out-r2-per-site" - publishDir = [ - enabled: false - ] + publishDir = [ enabled: false ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:CONCATENATE' { @@ -82,15 +81,11 @@ process { } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:GUNZIP' { - publishDir = [ - enabled: false - ] + publishDir = [ enabled: false ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_CONCORDANCE_GLIMPSE2:ADD_COLUMNS' { ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_SNP" } - publishDir = [ - enabled: false - ] + publishDir = [ enabled: false ] } } From a89168eb5d1fa0efa8e4a1d95ba768b7fd1929a2 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 24 Apr 2024 12:38:58 +0200 Subject: [PATCH 65/65] Fix config --- conf/steps/validation.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/steps/validation.config b/conf/steps/validation.config index e7a8018a..d84d2c63 100644 --- a/conf/steps/validation.config +++ b/conf/steps/validation.config @@ -16,7 +16,7 @@ process { publishDir = [ path: { "${params.outdir}/validation/truth" }, mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, enabled: false ] }