From 73b4fd3b8afd102a6b257b7c4c5228dcd7af653d Mon Sep 17 00:00:00 2001 From: Laura Blum Date: Thu, 9 Apr 2026 15:46:59 -0400 Subject: [PATCH 1/7] add fgbio_clip_bam module to clip overlapping r1/r2 --- modules/local/fgbio_clip_bam.nf | 29 +++++++++++++++++++++++++++++ nextflow.config | 1 + workflows/emseq_variant_calling.nf | 10 +++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 modules/local/fgbio_clip_bam.nf diff --git a/modules/local/fgbio_clip_bam.nf b/modules/local/fgbio_clip_bam.nf new file mode 100644 index 0000000..998a24c --- /dev/null +++ b/modules/local/fgbio_clip_bam.nf @@ -0,0 +1,29 @@ +process fgbioClipBam { + cpus 6 + memory '100 GB' + conda "bioconda::fgbio=3.1.2 bioconda::samtools=1.19" + publishDir "${params.outdir}/clipped_bams/", mode: 'symlink' + tag { sample_id } + + input: + tuple val(sample_id), path(bam), path(bai) + + output: + tuple val(sample_id), path("${sample_id}.clipped.bam"), path("${sample_id}.clipped.bam.bai") + + script: + """ + export _JAVA_OPTIONS="-Xmx90g" + samtools sort -n -u -@ ${task.cpus} ${bam} | \\ + fgbio ClipBam \\ + --input /dev/stdin \\ + --output /dev/stdout \\ + --ref ${params.fasta} \\ + --clip-overlapping-reads true \\ + --clipping-mode Hard \\ + --sort-order Coordinate | \\ + samtools view -b -@ ${task.cpus} -o ${sample_id}.clipped.bam + + samtools index ${sample_id}.clipped.bam + """ +} diff --git a/nextflow.config b/nextflow.config index 3b8e092..ff437ca 100644 --- a/nextflow.config +++ b/nextflow.config @@ -13,6 +13,7 @@ params { publish_dir_mode = 'copy' outdir = 'results' tracedir = "${params.outdir}/pipeline_info" + run_clip_bam = false run_strelka = true run_freebayes = true freebayes_qual_filter = 15 // default quality filter for freebayes output. diff --git a/workflows/emseq_variant_calling.nf b/workflows/emseq_variant_calling.nf index 163881e..ac47033 100644 --- a/workflows/emseq_variant_calling.nf +++ b/workflows/emseq_variant_calling.nf @@ -33,6 +33,7 @@ happy_bed = Channel.value(params.happy_bed ?: '') // optional regions to a // Local Modules: include { downloadRevelio } from '../modules/local/download_revelio.nf' include { calcMD } from '../modules/local/calc_md.nf' +include { fgbioClipBam } from '../modules/local/fgbio_clip_bam.nf' include { revelio } from '../modules/local/revelio.nf' include { strelka } from '../modules/local/strelka.nf' include { freebayes } from '../modules/local/freebayes.nf' @@ -56,11 +57,18 @@ workflow emseq_variant_calling { index_format ) + // + // Module: Optionally clip overlapping reads with fgbio ClipBam before Revelio + // + if (params.run_clip_bam) { + fgbioClipBam(calcMD.out.calcmd_bam) + } + // // Module: Run Revelio to mask possibly converted bases by setting BQ to 0 // revelio ( - calcMD.out.calcmd_bam, + params.run_clip_bam ? fgbioClipBam.out : calcMD.out.calcmd_bam, downloadRevelio.out, tmpdir ) From 6d6a6cbe47c98dc9ce3b97e232fa91169f39039e Mon Sep 17 00:00:00 2001 From: Laura Blum Date: Wed, 15 Apr 2026 09:19:10 -0400 Subject: [PATCH 2/7] add deepvariant module with ability to customize checkpoint --- .github/workflows/ci.yml | 5 ++++ conf/test.config | 3 ++ modules/local/deepvariant_custom.nf | 46 +++++++++++++++++++++++++++++ nextflow.config | 9 ++++-- tests/main.nf.test | 12 ++++++-- tests/main.nf.test.snap | 36 +++++++++++++++------- workflows/emseq_variant_calling.nf | 29 ++++++++++++++++-- 7 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 modules/local/deepvariant_custom.nf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5767b4..7f4698d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,11 @@ jobs: with: version: 0.9.0 + - name: Install Singularity + uses: eWaterCycle/setup-singularity@v7 + with: + singularity-version: 3.8.3 + - name: Run pipeline with test data run: | nf-test test diff --git a/conf/test.config b/conf/test.config index 2c2d694..2fc7e90 100644 --- a/conf/test.config +++ b/conf/test.config @@ -29,4 +29,7 @@ params { email = "test@email.com" publish_dir_mode = "symlink" tracedir = "test_output/pipeline_info" + + // Use standard WGS model in CI (no local checkpoint available) + deepvariant_checkpoint = null } diff --git a/modules/local/deepvariant_custom.nf b/modules/local/deepvariant_custom.nf new file mode 100644 index 0000000..2a30519 --- /dev/null +++ b/modules/local/deepvariant_custom.nf @@ -0,0 +1,46 @@ +process deepvariantCustom { + label 'process_high' + tag "$library" + container "docker.io/google/deepvariant:1.10.0" + publishDir "${params.outdir}/deepvariantCustom", mode: params.publish_dir_mode + // + // Runs run_deepvariant, optionally with a fine-tuned checkpoint. + // + // When params.deepvariant_checkpoint is set, uses that checkpoint with + // --customized_model and --disable_small_model (required in DV 1.10.0). + // When null, runs with the standard built-in WGS model. + // + // The checkpoint directory must contain: + // - checkpoint files (.index, .data-*) + // - model.example_info.json (required by DV 1.10.0 inference) + // + // export TMPDIR=/tmp: overrides the SGE scratch directory (TMPDIR=/scratch/JOBID) + // which is not visible inside the container; parallel inside run_deepvariant + // inherits this value and uses /tmp instead. + + input: + tuple val(library), path(bam), path(bai) + path(fasta) + path(fai) + + output: + tuple val(library), path("${library}.deepvariant.vcf.gz"), + path("${library}.deepvariant.vcf.gz.tbi"), emit: vcf + + script: + def custom_model_opt = params.deepvariant_checkpoint + ? "--customized_model ${params.deepvariant_checkpoint} --disable_small_model" + : '' + """ + export TMPDIR=/tmp + + run_deepvariant \\ + --model_type WGS \\ + ${custom_model_opt} \\ + --ref ${fasta} \\ + --reads ${bam} \\ + --output_vcf ${library}.deepvariant.vcf.gz \\ + --num_shards ${task.cpus} \\ + + """ +} diff --git a/nextflow.config b/nextflow.config index ff437ca..32e3c8c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -16,6 +16,8 @@ params { run_clip_bam = false run_strelka = true run_freebayes = true + run_deepvariant = true + deepvariant_checkpoint = null freebayes_qual_filter = 15 // default quality filter for freebayes output. run_happy = true target_bed = null @@ -36,7 +38,8 @@ profiles { conda.cacheDir = "${projectDir}/.conda/envs" conda.enabled = true docker.enabled = false - singularity.enabled = false + singularity.enabled = true + singularity.autoMounts = true podman.enabled = false shifter.enabled = false charliecloud.enabled = false @@ -54,10 +57,12 @@ profiles { apptainer.enabled = false } - test { + test { includeConfig 'conf/test.config' conda.enabled = true conda.cacheDir = "${projectDir}/.conda/envs" + singularity.enabled = true + singularity.autoMounts = true } } diff --git a/tests/main.nf.test b/tests/main.nf.test index 2f3c844..8222681 100644 --- a/tests/main.nf.test +++ b/tests/main.nf.test @@ -27,6 +27,8 @@ nextflow_pipeline { def happy_vcf_freebayes = path("${launchDir}/test_output/happyConcordanceFreebayes/200ng-V2-1.md_vs_truth.vcf.happy.vcf.gz").vcf.summary def happy_summary_freebayes = path("${launchDir}/test_output/happyConcordanceFreebayes/200ng-V2-1.md_vs_truth.vcf.happy.summary.csv").text.tokenize('\n') def snp_breakdown = path("${launchDir}/test_output/parseHappyVcf/200ng-V2-1.md.happy.snp_mutations.txt").text.tokenize('\n') + def deepvariant_vcf = path("${launchDir}/test_output/deepvariantCustom/200ng-V2-1.md.deepvariant.vcf.gz").vcf.summary + def happy_vcf_deepvariant = path("${launchDir}/test_output/happyConcordanceDeepvariant/200ng-V2-1.md_vs_truth.vcf.happy.vcf.gz").vcf.summary assertAll( { assert workflow.success }, @@ -40,7 +42,9 @@ nextflow_pipeline { ["happy_summary_strelka", happy_summary_strelka], ["happy_vcf_freebayes", happy_vcf_freebayes], ["happy_summary_freebayes", happy_summary_freebayes], - ["snp_breakdown", snp_breakdown] + ["snp_breakdown", snp_breakdown], + ["deepvariant_vcf", deepvariant_vcf], + ["happy_vcf_deepvariant", happy_vcf_deepvariant] ).match() } ) @@ -72,6 +76,8 @@ nextflow_pipeline { def happy_vcf_freebayes = path("${launchDir}/test_output/happyConcordanceFreebayes/200ng-V2-1.md_vs_truth.vcf.happy.vcf.gz").vcf.summary def happy_summary_freebayes = path("${launchDir}/test_output/happyConcordanceFreebayes/200ng-V2-1.md_vs_truth.vcf.happy.summary.csv").text.tokenize('\n') def snp_breakdown = path("${launchDir}/test_output/parseHappyVcf/200ng-V2-1.md.happy.snp_mutations.txt").text.tokenize('\n') + def deepvariant_vcf = path("${launchDir}/test_output/deepvariantCustom/200ng-V2-1.md.deepvariant.vcf.gz").vcf.summary + def happy_vcf_deepvariant = path("${launchDir}/test_output/happyConcordanceDeepvariant/200ng-V2-1.md_vs_truth.vcf.happy.vcf.gz").vcf.summary assertAll( { assert workflow.success }, @@ -85,7 +91,9 @@ nextflow_pipeline { ["happy_summary_strelka", happy_summary_strelka], ["happy_vcf_freebayes", happy_vcf_freebayes], ["happy_summary_freebayes", happy_summary_freebayes], - ["snp_breakdown", snp_breakdown] + ["snp_breakdown", snp_breakdown], + ["deepvariant_vcf", deepvariant_vcf], + ["happy_vcf_deepvariant", happy_vcf_deepvariant] ).match() } ) diff --git a/tests/main.nf.test.snap b/tests/main.nf.test.snap index e294762..301f7d3 100644 --- a/tests/main.nf.test.snap +++ b/tests/main.nf.test.snap @@ -3,8 +3,8 @@ "content": [ { "tasksFailed": 0, - "tasksCount": 15, - "tasksSucceeded": 15 + "tasksCount": 19, + "tasksSucceeded": 19 }, [ "calcmd_bam", @@ -56,20 +56,28 @@ "Count Ref Alt Type", "1 G C Correct" ] + ], + [ + "deepvariant_vcf", + "VcfFile [chromosomes=[chr21], sampleCount=1, variantCount=60, phased=false, phasedAutodetect=false]" + ], + [ + "happy_vcf_deepvariant", + "VcfFile [chromosomes=[chr21], sampleCount=2, variantCount=13, phased=false, phasedAutodetect=false]" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.04.1" + "nf-test": "0.9.0", + "nextflow": "24.04.2" }, - "timestamp": "2025-05-20T13:36:17.023260625" + "timestamp": "2026-04-14T15:43:10.033366736" }, "Pipeline test without optional inputs": { "content": [ { "tasksFailed": 0, - "tasksCount": 15, - "tasksSucceeded": 15 + "tasksCount": 19, + "tasksSucceeded": 19 }, [ "calcmd_bam", @@ -121,12 +129,20 @@ "Count Ref Alt Type", "1 G C Correct" ] + ], + [ + "deepvariant_vcf", + "VcfFile [chromosomes=[chr21], sampleCount=1, variantCount=60, phased=false, phasedAutodetect=false]" + ], + [ + "happy_vcf_deepvariant", + "VcfFile [chromosomes=[chr21], sampleCount=2, variantCount=13, phased=false, phasedAutodetect=false]" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.04.1" + "nf-test": "0.9.0", + "nextflow": "24.04.2" }, - "timestamp": "2025-05-20T13:39:05.639276115" + "timestamp": "2026-04-14T15:47:22.184920401" } } \ No newline at end of file diff --git a/workflows/emseq_variant_calling.nf b/workflows/emseq_variant_calling.nf index ac47033..e5f57df 100644 --- a/workflows/emseq_variant_calling.nf +++ b/workflows/emseq_variant_calling.nf @@ -38,8 +38,10 @@ include { revelio } from '../modules/local/revelio.nf' include { strelka } from '../modules/local/strelka.nf' include { freebayes } from '../modules/local/freebayes.nf' include { happyConcordance as happyConcordanceStrelka; - happyConcordance as happyConcordanceFreebayes } from '../modules/local/happy_concordance.nf' + happyConcordance as happyConcordanceFreebayes; + happyConcordance as happyConcordanceDeepvariant; } from '../modules/local/happy_concordance.nf' include { parseHappyVcf } from '../modules/local/parse_happy_vcf.nf' +include { deepvariantCustom } from '../modules/local/deepvariant_custom.nf' workflow emseq_variant_calling { @@ -119,7 +121,7 @@ workflow emseq_variant_calling { fai, target_bed // call regions (optional) ) - + // // Module: Run hap.py to compare variants to a "truth" vcf // @@ -135,6 +137,29 @@ workflow emseq_variant_calling { ) } } + + // + // Module: Run DeepVariant with custom model + // + if (params.run_deepvariant) { + deepvariantCustom( + revelio.out.masked, + fasta, + fai + ) + + if (params.run_happy) { + + happyConcordanceDeepvariant( + deepvariantCustom.out.vcf, + happy_bed, // comparison regions (optional) + fasta, + fai, + happy_truth_vcf, + happy_truth_tbi + ) + } + } } workflow { From 8f4b4091fc3eb4e20d0cec17ec8b0f98d887a2d7 Mon Sep 17 00:00:00 2001 From: Laura Blum Date: Wed, 15 Apr 2026 09:46:19 -0400 Subject: [PATCH 3/7] so that singularity can see the checkpoint folder --- modules/local/deepvariant_custom.nf | 10 ++++------ workflows/emseq_variant_calling.nf | 12 +++++++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/local/deepvariant_custom.nf b/modules/local/deepvariant_custom.nf index 2a30519..97dd610 100644 --- a/modules/local/deepvariant_custom.nf +++ b/modules/local/deepvariant_custom.nf @@ -13,23 +13,21 @@ process deepvariantCustom { // The checkpoint directory must contain: // - checkpoint files (.index, .data-*) // - model.example_info.json (required by DV 1.10.0 inference) - // - // export TMPDIR=/tmp: overrides the SGE scratch directory (TMPDIR=/scratch/JOBID) - // which is not visible inside the container; parallel inside run_deepvariant - // inherits this value and uses /tmp instead. input: tuple val(library), path(bam), path(bai) path(fasta) path(fai) + path(checkpoint_dir) // staged dir with .index/.data-*/model.example_info.json; pass [] when unused + val(checkpoint_name) // checkpoint prefix e.g. "checkpoint-16896-0.97789-1"; pass '' when unused output: tuple val(library), path("${library}.deepvariant.vcf.gz"), path("${library}.deepvariant.vcf.gz.tbi"), emit: vcf script: - def custom_model_opt = params.deepvariant_checkpoint - ? "--customized_model ${params.deepvariant_checkpoint} --disable_small_model" + def custom_model_opt = checkpoint_name + ? "--customized_model ${checkpoint_dir}/${checkpoint_name} --disable_small_model" : '' """ export TMPDIR=/tmp diff --git a/workflows/emseq_variant_calling.nf b/workflows/emseq_variant_calling.nf index e5f57df..91490b6 100644 --- a/workflows/emseq_variant_calling.nf +++ b/workflows/emseq_variant_calling.nf @@ -25,6 +25,14 @@ tmpdir = Channel.value(params.tmpdir) // Optional input for regions to call, e.g. for target enrichment target_bed = Channel.value(params.target_bed ?: '') +// DeepVariant checkpoint — split single param into staged dir + name; [] and '' when unused +checkpoint_dir = (params.run_deepvariant && params.deepvariant_checkpoint) + ? Channel.value(file(params.deepvariant_checkpoint).parent) + : Channel.value([]) +checkpoint_name = (params.run_deepvariant && params.deepvariant_checkpoint) + ? Channel.value(file(params.deepvariant_checkpoint).name) + : Channel.value('') + // Hap.py concordance inputs, only if run_happy is true happy_truth_vcf = params.run_happy ? Channel.fromPath(params.happy_truth_vcf, checkIfExists: true) : Channel.empty() happy_truth_tbi = params.run_happy ? Channel.fromPath(params.happy_truth_vcf + '.tbi', checkIfExists: true) : Channel.empty() @@ -145,7 +153,9 @@ workflow emseq_variant_calling { deepvariantCustom( revelio.out.masked, fasta, - fai + fai, + checkpoint_dir, + checkpoint_name ) if (params.run_happy) { From b78534f884765e0114e01feb779d73d28ecd914f Mon Sep 17 00:00:00 2001 From: Laura Blum Date: Fri, 17 Jul 2026 14:13:25 -0400 Subject: [PATCH 4/7] add --exome flag for targeted calling --- modules/local/strelka.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/strelka.nf b/modules/local/strelka.nf index c88fe1b..847ebb6 100644 --- a/modules/local/strelka.nf +++ b/modules/local/strelka.nf @@ -13,7 +13,7 @@ process strelka { tuple val(library), path("*.strelka_vcf.gz"), path("*.strelka_vcf.gz.tbi"), emit: vcf script: - def target_regions_opt = (target_bed && file(target_bed).exists()) ? '--callRegions target.bed.gz' : '' + def target_regions_opt = (target_bed && file(target_bed).exists()) ? '--callRegions target.bed.gz --exome' : '' """ if [ -n "${target_bed}" ] && [ -f "${target_bed}" ]; then ln -s "${target_bed}" target.bed From 5f18748411b48c41ac8ad18bbf96daaa13e94eb5 Mon Sep 17 00:00:00 2001 From: Laura Blum Date: Fri, 17 Jul 2026 17:28:19 -0400 Subject: [PATCH 5/7] add custom models for deepvariant from zenodo --- conf/test.config | 3 +- modules/local/deepvariant_checkpoint.nf | 66 +++++++++++++++++++++++++ modules/local/deepvariant_custom.nf | 16 ++---- nextflow.config | 32 ++++++------ tests/main.nf.test.snap | 10 ++-- workflows/emseq_variant_calling.nf | 13 ++--- 6 files changed, 97 insertions(+), 43 deletions(-) create mode 100644 modules/local/deepvariant_checkpoint.nf diff --git a/conf/test.config b/conf/test.config index 2fc7e90..c22bb46 100644 --- a/conf/test.config +++ b/conf/test.config @@ -30,6 +30,5 @@ params { publish_dir_mode = "symlink" tracedir = "test_output/pipeline_info" - // Use standard WGS model in CI (no local checkpoint available) - deepvariant_checkpoint = null + deepvariant_model_type = 'WGS' } diff --git a/modules/local/deepvariant_checkpoint.nf b/modules/local/deepvariant_checkpoint.nf new file mode 100644 index 0000000..472ba77 --- /dev/null +++ b/modules/local/deepvariant_checkpoint.nf @@ -0,0 +1,66 @@ +process fetchDeepvariantCheckpoint { + label 'process_single' + tag "${model_type}" + conda 'conda-forge::wget=1.20.3' + storeDir "${projectDir}/ref_data/deepvariant_checkpoints" + // + // Pulls the EM-seq DeepVariant fine-tuned model for the requested --model_type (only WGS and WES) from + // Zenodo (doi.org/10.5281/zenodo.21416823) and stages it as a checkpoint directory + // WGS -> wgs.checkpoint.260717.{data-*,index} + // WES -> target_capture.checkpoint.260717.{data-*,index} + // + // The output dir contains the checkpoint (.index/.data-*) plus model.example_info.json. + // DV 1.10.0 inference resolves it by directory: get_model_example_info_json() looks in + // os.path.dirname(--customized_model) for 'model.example_info.json' + + input: + val(model_type) + + output: + tuple val(model_type), path("${label}"), val(prefix), emit: checkpoint + + script: + def spec = [ WGS: ['wgs', 'wgs.checkpoint.260717'], + WES: ['target_capture', 'target_capture.checkpoint.260717'] ][model_type] + if (!spec) + error "No EM-seq Zenodo checkpoint for model_type '${model_type}' (only WGS and WES)." + label = spec[0] + prefix = spec[1] + def base = 'https://zenodo.org/records/21416823/files' + """ + mkdir -p ${label} + + wget -q -O ${label}/${prefix}.data-00000-of-00001 "${base}/${prefix}.data-00000-of-00001?download=1" + wget -q -O ${label}/${prefix}.index "${base}/${prefix}.index?download=1" + wget -q -O ${label}/model.example_info.json "${base}/model.example_info.json?download=1" + """ +} + +workflow resolveDeepvariantCheckpoint { + main: + def mt = params.deepvariant_model_type + def valid = ['WGS', 'WES', 'PACBIO', 'ONT_R104', 'HYBRID_PACBIO_ILLUMINA'] + if (!mt) + error "Please set --deepvariant_model_type (one of: ${valid.join(', ')})." + if (!(mt in valid)) + error "deepvariant_model_type must be one of ${valid.join(', ')}, got '${mt}'." + if (params.deepvariant_custom_model && !(mt in ['WGS', 'WES'])) + error "deepvariant_custom_model is only available for WGS and WES — " + + "model_type '${mt}' has no custom model." + + if (params.run_deepvariant == false) { + ch_checkpoint = Channel.value([[], '', mt]) + } else if (params.deepvariant_checkpoint) { + ch_checkpoint = Channel.value([ file(params.deepvariant_checkpoint).parent, + file(params.deepvariant_checkpoint).name, + mt ]) + } else if (params.deepvariant_custom_model) { + ch_checkpoint = fetchDeepvariantCheckpoint(Channel.value(mt)) + .checkpoint.map { m, dir, prefix -> [dir, prefix, mt] } + } else { + ch_checkpoint = Channel.value([[], '', mt]) + } + + emit: + checkpoint = ch_checkpoint +} diff --git a/modules/local/deepvariant_custom.nf b/modules/local/deepvariant_custom.nf index 97dd610..7816c34 100644 --- a/modules/local/deepvariant_custom.nf +++ b/modules/local/deepvariant_custom.nf @@ -3,23 +3,15 @@ process deepvariantCustom { tag "$library" container "docker.io/google/deepvariant:1.10.0" publishDir "${params.outdir}/deepvariantCustom", mode: params.publish_dir_mode - // - // Runs run_deepvariant, optionally with a fine-tuned checkpoint. - // - // When params.deepvariant_checkpoint is set, uses that checkpoint with - // --customized_model and --disable_small_model (required in DV 1.10.0). - // When null, runs with the standard built-in WGS model. - // - // The checkpoint directory must contain: + // For running a custom model the checkpoint directory must contain: // - checkpoint files (.index, .data-*) - // - model.example_info.json (required by DV 1.10.0 inference) + // - model.example_info.json (required by DV 1.10.0 ) input: tuple val(library), path(bam), path(bai) path(fasta) path(fai) - path(checkpoint_dir) // staged dir with .index/.data-*/model.example_info.json; pass [] when unused - val(checkpoint_name) // checkpoint prefix e.g. "checkpoint-16896-0.97789-1"; pass '' when unused + tuple path(checkpoint_dir), val(checkpoint_name), val(model_type) output: tuple val(library), path("${library}.deepvariant.vcf.gz"), @@ -33,7 +25,7 @@ process deepvariantCustom { export TMPDIR=/tmp run_deepvariant \\ - --model_type WGS \\ + --model_type ${model_type} \\ ${custom_model_opt} \\ --ref ${fasta} \\ --reads ${bam} \\ diff --git a/nextflow.config b/nextflow.config index 32e3c8c..ad2ac0f 100644 --- a/nextflow.config +++ b/nextflow.config @@ -8,21 +8,23 @@ // Global default params, used in configs params { - tmpdir = '/tmp' - index_format = 'csi' - publish_dir_mode = 'copy' - outdir = 'results' - tracedir = "${params.outdir}/pipeline_info" - run_clip_bam = false - run_strelka = true - run_freebayes = true - run_deepvariant = true - deepvariant_checkpoint = null - freebayes_qual_filter = 15 // default quality filter for freebayes output. - run_happy = true - target_bed = null - happy_truth_vcf = null - happy_bed = null + tmpdir = '/tmp' + index_format = 'csi' + publish_dir_mode = 'copy' + outdir = 'results' + tracedir = "${params.outdir}/pipeline_info" + run_clip_bam = false + run_strelka = true + run_freebayes = true + run_deepvariant = true + deepvariant_model_type = null // WGS | WES | PACBIO | ONT_R104 | HYBRID_PACBIO_ILLUMINA + deepvariant_custom_model = true // Download an EM-seq fine-tuned model, only available for WGS and WES (target capture). + deepvariant_checkpoint = null // Optional local custom model prefix + freebayes_qual_filter = 15 + run_happy = true + target_bed = null + happy_truth_vcf = null + happy_bed = null } includeConfig 'conf/base.config' diff --git a/tests/main.nf.test.snap b/tests/main.nf.test.snap index 301f7d3..b2177d0 100644 --- a/tests/main.nf.test.snap +++ b/tests/main.nf.test.snap @@ -54,7 +54,7 @@ "snp_breakdown", [ "Count Ref Alt Type", - "1 G C Correct" + "1 G C NotPassed" ] ], [ @@ -63,14 +63,14 @@ ], [ "happy_vcf_deepvariant", - "VcfFile [chromosomes=[chr21], sampleCount=2, variantCount=13, phased=false, phasedAutodetect=false]" + "VcfFile [chromosomes=[chr21], sampleCount=2, variantCount=4, phased=false, phasedAutodetect=false]" ] ], "meta": { "nf-test": "0.9.0", "nextflow": "24.04.2" }, - "timestamp": "2026-04-14T15:43:10.033366736" + "timestamp": "2026-07-17T17:08:12.351084775" }, "Pipeline test without optional inputs": { "content": [ @@ -136,13 +136,13 @@ ], [ "happy_vcf_deepvariant", - "VcfFile [chromosomes=[chr21], sampleCount=2, variantCount=13, phased=false, phasedAutodetect=false]" + "VcfFile [chromosomes=[chr21], sampleCount=2, variantCount=4, phased=false, phasedAutodetect=false]" ] ], "meta": { "nf-test": "0.9.0", "nextflow": "24.04.2" }, - "timestamp": "2026-04-14T15:47:22.184920401" + "timestamp": "2026-07-17T17:11:27.137540031" } } \ No newline at end of file diff --git a/workflows/emseq_variant_calling.nf b/workflows/emseq_variant_calling.nf index 91490b6..d430aea 100644 --- a/workflows/emseq_variant_calling.nf +++ b/workflows/emseq_variant_calling.nf @@ -25,13 +25,7 @@ tmpdir = Channel.value(params.tmpdir) // Optional input for regions to call, e.g. for target enrichment target_bed = Channel.value(params.target_bed ?: '') -// DeepVariant checkpoint — split single param into staged dir + name; [] and '' when unused -checkpoint_dir = (params.run_deepvariant && params.deepvariant_checkpoint) - ? Channel.value(file(params.deepvariant_checkpoint).parent) - : Channel.value([]) -checkpoint_name = (params.run_deepvariant && params.deepvariant_checkpoint) - ? Channel.value(file(params.deepvariant_checkpoint).name) - : Channel.value('') +// DeepVariant checkpoint — resolved to (dir, name): local override, else Zenodo fetch by mode. // Hap.py concordance inputs, only if run_happy is true happy_truth_vcf = params.run_happy ? Channel.fromPath(params.happy_truth_vcf, checkIfExists: true) : Channel.empty() @@ -50,6 +44,7 @@ include { happyConcordance as happyConcordanceStrelka; happyConcordance as happyConcordanceDeepvariant; } from '../modules/local/happy_concordance.nf' include { parseHappyVcf } from '../modules/local/parse_happy_vcf.nf' include { deepvariantCustom } from '../modules/local/deepvariant_custom.nf' +include { resolveDeepvariantCheckpoint } from '../modules/local/deepvariant_checkpoint.nf' workflow emseq_variant_calling { @@ -150,12 +145,12 @@ workflow emseq_variant_calling { // Module: Run DeepVariant with custom model // if (params.run_deepvariant) { + ck = resolveDeepvariantCheckpoint() deepvariantCustom( revelio.out.masked, fasta, fai, - checkpoint_dir, - checkpoint_name + ck.checkpoint ) if (params.run_happy) { From a94690c7082ee700b012c7d6190c9f3a555284c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:41:45 +0000 Subject: [PATCH 6/7] Fix CI: use Docker in test profile, update NXF_VER, fix trailing backslash in deepvariant script --- .github/workflows/ci.yml | 7 +------ modules/local/deepvariant_custom.nf | 5 ++--- nextflow.config | 4 ++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f4698d..9c1f46a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: NXF_VER: - - "23.10.0" + - "25.04.2" steps: - name: Check out pipeline code uses: actions/checkout@v4 @@ -39,11 +39,6 @@ jobs: with: version: 0.9.0 - - name: Install Singularity - uses: eWaterCycle/setup-singularity@v7 - with: - singularity-version: 3.8.3 - - name: Run pipeline with test data run: | nf-test test diff --git a/modules/local/deepvariant_custom.nf b/modules/local/deepvariant_custom.nf index 7816c34..d994e5e 100644 --- a/modules/local/deepvariant_custom.nf +++ b/modules/local/deepvariant_custom.nf @@ -30,7 +30,6 @@ process deepvariantCustom { --ref ${fasta} \\ --reads ${bam} \\ --output_vcf ${library}.deepvariant.vcf.gz \\ - --num_shards ${task.cpus} \\ - - """ + --num_shards ${task.cpus} +""" } diff --git a/nextflow.config b/nextflow.config index ad2ac0f..d4c9a06 100644 --- a/nextflow.config +++ b/nextflow.config @@ -63,8 +63,8 @@ profiles { includeConfig 'conf/test.config' conda.enabled = true conda.cacheDir = "${projectDir}/.conda/envs" - singularity.enabled = true - singularity.autoMounts = true + docker.enabled = true + singularity.enabled = false } } From b6465e5865c5f6fd8a43ebae9a05e005582d3683 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 22:04:04 +0000 Subject: [PATCH 7/7] Fix CI: update snapshot tasksCount from 19 to 20 for small dataset test --- tests/main.nf.test.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/main.nf.test.snap b/tests/main.nf.test.snap index b2177d0..78f1080 100644 --- a/tests/main.nf.test.snap +++ b/tests/main.nf.test.snap @@ -3,8 +3,8 @@ "content": [ { "tasksFailed": 0, - "tasksCount": 19, - "tasksSucceeded": 19 + "tasksCount": 20, + "tasksSucceeded": 20 }, [ "calcmd_bam",