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 ad998658..87f4e68c 100644 Binary files a/docs/images/metro/Concordance.png and b/docs/images/metro/Concordance.png differ 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') {