diff --git a/CHANGELOG.md b/CHANGELOG.md index b4144627..e5b7e078 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +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. - [#26](https://github.com/nf-core/phaseimpute/pull/26) - Added QUILT method ### `Fixed` diff --git a/assets/schema_input.json b/assets/schema_input.json index aca033f6..971c3fb3 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -13,17 +13,17 @@ "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"] + "required": ["sample", "file", "index"] } } diff --git a/conf/modules.config b/conf/modules.config index 5f03ea9c..a1c706f9 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -34,119 +34,4 @@ process { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] } - - // 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: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_REGION: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: '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:BCFTOOLS_NORM' { - ext.args = [ - "-m", - "-any", - "--no-version" - ].join(' ') - ext.prefix = { "${meta.id}_norm" } - } - 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" } - } - 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_GLIMPSE:GLIMPSE_PHASE' { - ext.args = [ - "--impute-reference-only-variants" - ].join(' ') - ext.prefix = { "${meta.id}" } - ext.suffix = "bcf" - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE:GLIMPSE_CHUNK' { - ext.args = [ - "--window-size 200000", - "--buffer-size 20000" - ].join(' ') - ext.prefix = { "${meta.id}" } - } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:VCF_IMPUTE_GLIMPSE: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: ADD_COLUMNS { - ext.prefix = { "${meta.id}_D${meta.depth}_P${meta.panel}_R${meta.region}_SNP" } - } } diff --git a/conf/steps/imputation.config b/conf/steps/imputation.config new file mode 100644 index 00000000..7e859eca --- /dev/null +++ b/conf/steps/imputation.config @@ -0,0 +1,33 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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:CONCAT_IMPUT:.*' { + publishDir = [ + path: { "${params.outdir}/imputation/concat" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + ext.prefix = { "${meta.id}_impute_concat" } + } + + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:CONCAT_IMPUT:BCFTOOLS_CONCAT' { + ext.args = {[ + "--ligate", + "--output-type z", + ].join(" ").trim()} + } + + 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..5b3d89cd --- /dev/null +++ b/conf/steps/imputation_glimpse1.config @@ -0,0 +1,84 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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: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/" }, + 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" } + ] + } +} diff --git a/conf/quilt_subworkflow.config b/conf/steps/imputation_quilt.config similarity index 64% rename from conf/quilt_subworkflow.config rename to conf/steps/imputation_quilt.config index 6f237032..f75c777e 100644 --- a/conf/quilt_subworkflow.config +++ b/conf/steps/imputation_quilt.config @@ -20,20 +20,25 @@ process { ] } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:GLIMPSE_CHUNK' { + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:MAKE_CHUNKS:.*' { ext.prefix = { "${meta.id}_${meta.chr}" } 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, + 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,60 +81,34 @@ 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, ], ] } withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:QUILT_QUILT' { - publishDir = [ - [ - path: { "${params.outdir}/quilt_impute/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: params.publish_dir_mode, - ], - ] + ext.prefix = { "${meta.id}_R${meta.region.replace(':','_')}.impute" } + publishDir = [enabled: false] } - withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:BCFTOOLS_INDEX' { - ext.args = {[ - "--tbi", - ].join(" ").trim()} + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:INDEX1' { + ext.args = "--tbi" + publishDir = [enabled: false] } - - 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: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: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, - ], - ] + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:IMPUTE_QUILT:INDEX2' { + ext.args = "--tbi" } } diff --git a/conf/steps/initialisation.config b/conf/steps/initialisation.config new file mode 100644 index 00000000..7cc8daf5 --- /dev/null +++ b/conf/steps/initialisation.config @@ -0,0 +1,21 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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, + enabled: false + ] + } +} diff --git a/conf/steps/panel_prep.config b/conf/steps/panel_prep.config new file mode 100644 index 00000000..5eec78ce --- /dev/null +++ b/conf/steps/panel_prep.config @@ -0,0 +1,117 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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:.*' { + publishDir = [ + path: { "${params.outdir}/prep_panel/" }, + mode: params.publish_dir_mode, + enabled: false + ] + } + + 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:.*' { + publishDir = [ + path: { "${params.outdir}/prep_panel/" }, + mode: params.publish_dir_mode, + enabled: true, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + 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}_SNPS" } + } + + 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}_C${meta.chr}_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:GET_PANEL:VCF_PHASE_SHAPEIT5:BEDTOOLS_MAKEWINDOWS' { + ext.args = [ + '-w 60000', + '-s 40000' + ].join(' ') + ext.prefix = { "${meta.id}_chunks" } + publishDir = [ + 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/simulation.config b/conf/steps/simulation.config new file mode 100644 index 00000000..412c82a4 --- /dev/null +++ b/conf/steps/simulation.config @@ -0,0 +1,44 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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/" }, + mode: params.publish_dir_mode, + 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/" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + ] + } + withName: 'NFCORE_PHASEIMPUTE:PHASEIMPUTE:BAM_DOWNSAMPLE:SAMTOOLS_COVERAGE' { + ext.args = [ + ].join(' ') + 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}_R${meta.region.replace(':','_')}" } + } +} diff --git a/conf/steps/validation.config b/conf/steps/validation.config new file mode 100644 index 00000000..d84d2c63 --- /dev/null +++ b/conf/steps/validation.config @@ -0,0 +1,91 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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:.*' { + publishDir = [ + 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' { + ext.args = [ + "-I", + "-E", + "-a 'FORMAT/DP'" + ].join(' ') + ext.args2 = [ + "-Aim", + "-C alleles" + ].join(' ') + 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" }, + 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/" }, + 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}.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}_SNP" } + publishDir = [ enabled: false ] + } +} diff --git a/conf/test_all.config b/conf/test_all.config new file mode 100644 index 00000000..3e95cb32 --- /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_all, --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 8c2bd1f5..6f18229a 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,7 @@ params { input_region = "${projectDir}/tests/csv/region.csv" depth = 1 - 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" + // Genome references + fasta = "https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/reference_genome/21_22/hs38DH.chr21_22.fa" + step = "simulate" } diff --git a/conf/test_validate.config b/conf/test_validate.config new file mode 100644 index 00000000..c92c39bb --- /dev/null +++ b/conf/test_validate.config @@ -0,0 +1,33 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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_validate_imputed.csv" + input_truth = "${projectDir}/tests/csv/sample_validate_truth.csv" + input_region = "${projectDir}/tests/csv/region.csv" + + // 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 5258ca73..c2bd3d19 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,13 +15,18 @@ - [] 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) +- [] Add support for imputation by individuals or by groups of individuals ## Run tests ```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 +nextflow run main.nf -profile singularity,test_quilt --outdir results -resume ``` ## Problematic @@ -40,7 +45,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 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..7f105c75 100644 --- a/docs/images/metro/MetroMap.xml +++ b/docs/images/metro/MetroMap.xml @@ -1,2920 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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 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..319c2f5c 100644 --- a/main.nf +++ b/main.nf @@ -33,20 +33,54 @@ 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: + + // + // Initialise input channels + // + + input_impute = Channel.empty() + input_simulate = Channel.empty() + input_validate = Channel.empty() + + 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") { + 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 // PHASEIMPUTE ( - ch_input, + input_impute, + input_simulate, + input_validate, + ch_input_truth, ch_fasta, ch_panel, ch_regions, @@ -88,6 +122,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/modules.json b/modules.json index 708fac05..02349dbf 100644 --- a/modules.json +++ b/modules.json @@ -66,7 +66,7 @@ }, "gawk": { "branch": "master", - "git_sha": "dc3527855e7358c6d8400828754c0caa5f11698f", + "git_sha": "da4d05d04e65227d4307e87940842f1a14de62c7", "installed_by": ["modules"] }, "glimpse/chunk": { @@ -89,6 +89,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", @@ -104,6 +109,11 @@ "git_sha": "fa12139827a18b324bd63fce654818586a8e9cc7", "installed_by": ["multiple_impute_glimpse2"] }, + "gunzip": { + "branch": "master", + "git_sha": "3a5fef109d113b4997c9822198664ca5f2716208", + "installed_by": ["modules"] + }, "multiqc": { "branch": "master", "git_sha": "b7ebe95761cd389603f9cc0e0dc384c0f663815a", @@ -111,7 +121,7 @@ }, "quilt/quilt": { "branch": "master", - "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "git_sha": "46265545d61e7f482adf40de941cc9a94e479bbe", "installed_by": ["modules"] }, "samtools/coverage": { @@ -122,7 +132,7 @@ }, "samtools/faidx": { "branch": "master", - "git_sha": "aeb02a39d4c463598bfdcb2d964dbb7acbcf1298", + "git_sha": "f153f1f10e1083c49935565844cccb7453021682", "installed_by": ["modules"] }, "samtools/index": { 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 new file mode 100644 index 00000000..7789f76d --- /dev/null +++ b/modules/local/addcolumns/main.nf @@ -0,0 +1,42 @@ +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) + + 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}" + """ + # 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' | \\ + 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}": + gawk: \$(awk -Wversion | sed '1!d; s/.*Awk //; s/,.*//') + END_VERSIONS + """ +} 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/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/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/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/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 \\ 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/**" 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 diff --git a/nextflow.config b/nextflow.config index b459cd67..ccc6b0dd 100644 --- a/nextflow.config +++ b/nextflow.config @@ -20,7 +20,6 @@ params { // Panel preparation panel = null - panel_index = null phased = null rename_chr = false @@ -42,6 +41,12 @@ params { depth = 1 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 + // QUILT ngen = 100 buffer = 10000 @@ -193,9 +198,12 @@ 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_quilt { includeConfig 'conf/test_quilt.config' } } @@ -266,7 +274,23 @@ 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' + +// simulation step +includeConfig 'conf/steps/simulation.config' + +// panel_prep step +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' // Function to ensure that resource requirements don't go beyond // a maximum limit diff --git a/nextflow_schema.json b/nextflow_schema.json index afe40f62..9b1f4015 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": { @@ -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", @@ -55,6 +48,43 @@ } } }, + "validation": { + "title": "Concordance analysis", + "type": "object", + "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.", + "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": { + "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+$" + } + } + }, "input_output_options": { "title": "Input/output options", "type": "object", @@ -75,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$" }, @@ -370,6 +401,9 @@ { "$ref": "#/definitions/panelprep" }, + { + "$ref": "#/definitions/validation" + }, { "$ref": "#/definitions/input_output_options" }, 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/subworkflows/local/compute_gl/main.nf b/subworkflows/local/compute_gl/main.nf index 3e552f64..b11623d4 100644 --- a/subworkflows/local/compute_gl/main.nf +++ b/subworkflows/local/compute_gl/main.nf @@ -1,11 +1,11 @@ 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 { 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] @@ -15,9 +15,11 @@ workflow COMPUTE_GL { ch_multiqc_files = Channel.empty() ch_mpileup = ch_input - .combine(ch_target) - .map{metaI, bam, bai, metaPC, sites, tsv -> - [metaI + metaPC, bam, sites, tsv]} + .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, @@ -26,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] }) diff --git a/subworkflows/local/get_panel/main.nf b/subworkflows/local/get_panel/main.nf index 67904059..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,12 +67,11 @@ 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 { - 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 @@ -85,6 +83,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/impute_quilt/impute_quilt.nf b/subworkflows/local/impute_quilt/impute_quilt.nf index decc61f2..d8231733 100644 --- a/subworkflows/local/impute_quilt/impute_quilt.nf +++ b/subworkflows/local/impute_quilt/impute_quilt.nf @@ -1,13 +1,15 @@ -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 { 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 +25,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,25 +33,41 @@ 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) } - - // 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] - } + ch_quilt = ch_input + .map{ metaIC, bam, bai -> [metaIC.subMap("chr"), metaIC, bam, bai]} + .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 -> + [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 ) + 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: - 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/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf index bbfbf258..77f7fe42 100644 --- a/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_phaseimpute_pipeline/main.nf @@ -111,10 +111,33 @@ 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 + getAllFilesExtension(ch_input) + // + // Create channel from input file provided through params.input_truth + // + 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 + 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.empty() + } + // // Create channel for panel // @@ -175,14 +198,25 @@ 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], 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 ] + regions = ch_regions // [ [chr, region], region ] + map = ch_map // [ [map], map ] + versions = ch_versions } /* @@ -245,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_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/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_concatenate_bcftools/main.nf b/subworkflows/local/vcf_concatenate_bcftools/main.nf new file mode 100644 index 00000000..bc85d146 --- /dev/null +++ b/subworkflows/local/vcf_concatenate_bcftools/main.nf @@ -0,0 +1,34 @@ +include { BCFTOOLS_CONCAT } from '../../../modules/nf-core/bcftools/concat' +include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index' + +workflow VCF_CONCATENATE_BCFTOOLS { + + take: + ch_vcf_tbi // channel: [ val(meta), vcf, tbi ] + + main: + + ch_versions = Channel.empty() + + // Remove chromosome from meta + 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 ) + + // Ligate and concatenate chunks + 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_vcf_tbi_join = BCFTOOLS_CONCAT.out.vcf.join(BCFTOOLS_INDEX.out.tbi) + + emit: + vcf_tbi_join = ch_vcf_tbi_join // channel: [ meta, vcf, tbi ] + versions = ch_versions // channel: [ versions.yml ] +} diff --git a/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf b/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf deleted file mode 100644 index 6653d765..00000000 --- a/subworkflows/local/vcf_concatenate_bcftools/vcf_concatenate_bcftools.nf +++ /dev/null @@ -1,30 +0,0 @@ -include { BCFTOOLS_CONCAT } from '../../../modules/nf-core/bcftools/concat/main' -include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index/main' - -workflow VCF_CONCATENATE_BCFTOOLS { - - take: - ch_vcf_tbi // channel: [ val(meta), vcf, tbi ] - - main: - - // Remove chromosome from meta - ch_vcf_tbi_grouped = ch_vcf_tbi.map{ meta, vcf, tbi -> - return [['id' : meta.id], vcf, tbi] - } - // Group by ID - ch_vcf_tbi_grouped = ch_vcf_tbi_grouped.groupTuple( by:[0] ) - - // Ligate and concatenate chunks - BCFTOOLS_CONCAT(ch_vcf_tbi_grouped) - - // Index concatenated VCF - BCFTOOLS_INDEX(BCFTOOLS_CONCAT.out.vcf) - - // Join VCFs and TBIs - ch_imputed_vcf_tbi = BCFTOOLS_CONCAT.out.vcf.join(BCFTOOLS_INDEX.out.tbi) - - emit: - ch_imputed_vcf_tbi // channel: [ meta, vcf, tbi ] - - } diff --git a/subworkflows/local/vcf_concordance_glimpse2/main.nf b/subworkflows/local/vcf_concordance_glimpse2/main.nf new file mode 100644 index 00000000..bc76d0f2 --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse2/main.nf @@ -0,0 +1,60 @@ +include { GLIMPSE2_CONCORDANCE } from '../../../modules/nf-core/glimpse2/concordance' +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_GLIMPSE2 { + + 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] + ch_region // Regions to process [[chr, region], region] + + main: + + ch_versions = Channel.empty() + ch_multiqc_files = Channel.empty() + + ch_concordance = ch_vcf_emul + .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 ( + ch_concordance, + [[], [], 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]}) + 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) + 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 + .map{meta, txt -> [["id":"TestQuality"], txt]} + .groupTuple(), + Channel.of( + '(NR == 1) || (FNR > 1)' + ).collectFile(name:"program.txt") + ) + ch_versions = ch_versions.mix(CONCATENATE.out.versions.first()) + + emit: + stats = CONCATENATE.out.output // [ meta, txt ] + versions = ch_versions // channel: [ versions.yml ] + multiqc_files = ch_multiqc_files +} diff --git a/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test new file mode 100644 index 00000000..e1aca9a5 --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test @@ -0,0 +1,156 @@ +nextflow_workflow { + + name "Test Subworkflow VCF_CONCORDANCE_GLIMPSE2" + script "../main.nf" + config "./nextflow.config" + + workflow "VCF_CONCORDANCE_GLIMPSE2" + + tag "subworkflows" + tag "subworkflows_local" + tag "subworkflows/vcf_concordance_glimpse2" + tag "vcf_concordance_glimpse2" + + tag "bcftools" + tag "bcftools/index" + tag "glimpse" + tag "glimpse/phase" + tag "glimpse/concordance" + + test("vcf_concordance_glimpse2") { + 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() } + ) + } + + } + + 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.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 + 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: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: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: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: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)] + ]) + 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 new file mode 100644 index 00000000..880fa8a2 --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/main.nf.test.snap @@ -0,0 +1,58 @@ +{ + "vcf_concordance_glimpse2 direct": { + "content": [ + { + "0": [ + + ], + "1": [ + + ], + "stats": [ + + ], + "versions": [ + + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-04-12T16:24:33.217544644" + }, + "vcf_concordance_glimpse2": { + "content": [ + { + "0": [ + [ + { + "id": "TestQuality" + }, + "TestQuality.txt:md5,865f1cf1a32256467010c10bfef1fa04" + ] + ], + "1": [ + + ], + "stats": [ + [ + { + "id": "TestQuality" + }, + "TestQuality.txt:md5,865f1cf1a32256467010c10bfef1fa04" + ] + ], + "versions": [ + + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "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 new file mode 100644 index 00000000..8b9e3c3f --- /dev/null +++ b/subworkflows/local/vcf_concordance_glimpse2/tests/nextflow.config @@ -0,0 +1,9 @@ +params { + max_memory = '7.GB' +} + +process { + withName: 'VCF_CONCORDANCE_GLIMPSE2:CONCATENATE' { + ext.suffix = "txt" + } +} 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/tests/config/nf-test.config b/tests/config/nf-test.config index 775c5ad7..32ca7b47 100644 --- a/tests/config/nf-test.config +++ b/tests/config/nf-test.config @@ -2,6 +2,8 @@ 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/' + phaseimpute_testdata_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/phaseimpute/data/' } process { 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_truth.csv b/tests/csv/sample_validate_truth.csv new file mode 100644 index 00000000..828bad9c --- /dev/null +++ b/tests/csv/sample_validate_truth.csv @@ -0,0 +1,4 @@ +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 d07a1b8b..2b653676 100644 --- a/workflows/phaseimpute/main.nf +++ b/workflows/phaseimpute/main.nf @@ -12,23 +12,27 @@ 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 // -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_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' -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' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -39,13 +43,16 @@ include { VCF_CONCATENATE_BCFTOOLS } from '../../subworkflows/local/vcf_concat 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], 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: @@ -54,12 +61,19 @@ workflow PHASEIMPUTE { // // 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() + // Test if the input are all bam files + 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, ch_region, ch_fasta) + BAM_REGION(ch_input_sim, ch_region, ch_fasta) + ch_versions = ch_versions.mix(BAM_REGION.out.versions) // Initialize channel to impute ch_bam_to_impute = Channel.empty() @@ -71,9 +85,10 @@ workflow PHASEIMPUTE { ch_depth, ch_fasta ) - ch_versions = ch_versions.mix(BAM_DOWNSAMPLE.out.versions.first()) - - ch_input = ch_input.mix(BAM_DOWNSAMPLE.out.bam_emul) + 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 } if (params.genotype) { @@ -84,37 +99,46 @@ workflow PHASEIMPUTE { // // Prepare panel // - if (params.step == 'impute' || params.step == 'panel_prep') { + 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] + } + CONCAT_PANEL(GET_PANEL.out.panel + .map{ metaPC, norm, n_index, sites, s_index, tsv, t_index, phased, p_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) - // Output channel of input process - ch_impute_output = Channel.empty() + 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] + } + + ch_versions = ch_versions.mix(GET_PANEL.out.versions) - 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 ) ch_multiqc_files = ch_multiqc_files.mix(GL_INPUT.out.multiqc_files) + 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] } @@ -131,9 +155,14 @@ 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 - .map{ metaIPCR, vcf -> [metaIPCR + [tool: "Glimpse1"], vcf] } + 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_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) + + // Add to output channel ch_impute_output = ch_impute_output.mix(output_glimpse1) } if (params.tools.contains("glimpse2")) { @@ -148,33 +177,60 @@ workflow PHASEIMPUTE { // Create chunks from reference VCF MAKE_CHUNKS(ch_panel, ch_fasta) - // Make bamlist from bam input - ch_bamlist = ch_input - .map { it[1].tokenize('/').last() } - .collectFile( name: "bamlist.txt", newLine: true, sort: true ) - - // Create input QUILT - ch_input_quilt = ch_input - .map { meta, bam, bai -> [["id": "all_samples"], 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) - - // Concatenate results - VCF_CONCATENATE_BCFTOOLS(IMPUTE_QUILT.out.ch_vcf_tbi) - + 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 + ch_impute_output = ch_impute_output.mix(IMPUTE_QUILT.out.vcf_tbi) } - + // 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) } } - if (params.step == 'validate') { - error "validate step not yet implemented" + if (params.step == 'validate' || params.step == 'all') { + ch_truth_vcf = Channel.empty() + // Get extension of input files + truth_ext = getAllFilesExtension(ch_input_validate_truth) + + // Channels for branching + ch_truth = ch_input_validate_truth + .combine(truth_ext) + .branch { + bam: it[3] == 'bam' + vcf: it[3] =~ 'vcf|bcf' + } + + 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_versions = ch_versions.mix(GL_TRUTH.out.versions) + + // 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) + + // 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, + 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) } if (params.step == 'refine') {