Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/actions/find-tests/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Find tests
description: Finds all subdirectories and local modules containing a 'tests' folder, outputs the directory as tag. In addition, find all pipeline-level tests that are directly under the 'tests' directory. This assumes that the pipeline-level tests has a tag named the same as the file.

inputs: {}

outputs:
tags:
description: JSON array of matching subworkflow directory names
value: ${{ steps.search.outputs.tags }}

runs:
using: "composite"
steps:
- shell: bash
id: "search"
run: |
names_json="["

first=true
# Find all 'tests' dirs anywhere under modules/local and subworkflows
tests_dirs=$(find modules/local subworkflows -type d -name tests)

for td in $tests_dirs; do
module_dir=$(dirname "$td")
module_name=$(basename "$module_dir")

if [ "$first" = true ]; then
first=false
else
names_json+=","
fi

names_json+="\"$module_name\""
done

# Find all *.test files directly under ./tests (top-level)
test_files=$(find tests -maxdepth 1 -type f -name '*.test')
for tf in $test_files; do
tf_base=$(basename "$tf" .nf.test)
echo "Found top-level test file: $tf_base"

if [ "$first" = true ]; then
first=false
else
names_json+=","
fi

names_json+="\"$tf_base\""
done

names_json+="]"

# Write to GitHub Actions outputs
echo "tags=$names_json" >> $GITHUB_OUTPUT
12 changes: 7 additions & 5 deletions conf/modules.config
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ process {
}

withName: 'MINIMAP2_ALIGN' {
ext.args = { "-Y -y -ax map-ont " }
// ext.args = { "-Y -y -ax map-ont -R '@RG\\tID:${meta.id}\\tSM:${meta.id}\\tPL:ONT'" }
// ext.args = { "-Y -y -ax map-ont " }
ext.args = { "-Y -y -ax map-ont -R '@RG\\tID:${meta.id}\\tSM:${meta.id}\\tPL:ONT'" }
cpus = 8
publishDir = [
path: { "${params.outdir}/${meta.id}/mapped_bam" },
Expand All @@ -75,8 +75,8 @@ process {
}

withName: 'WINNOWMAP_ALIGN' {
ext.args = { "-ax map-ont " }
// ext.args = { "-ax map-ont -R '@RG\\tID:${meta.id}\\tSM:${meta.id}\\tPL:ONT'" }
// ext.args = { "-ax map-ont " }
ext.args = { "-ax map-ont -R '@RG\\tID:${meta.id}\\tSM:${meta.id}\\tPL:ONT'" }
cpus = 8
publishDir = [
path: { "${params.outdir}/${meta.id}/mapped_bam" },
Expand Down Expand Up @@ -359,6 +359,7 @@ process {
}

withName: 'CLAIR3' {
ext.args = {"--sample_name=${meta.id}"}
ext.prefix = { "${meta.id}_clair3" }
publishDir = [
path: { "${params.outdir}/${meta.id}/clair3" },
Expand Down Expand Up @@ -414,7 +415,8 @@ process {
// Use bcftools isec instead works much better
withName: 'BCFTOOLS_CONCAT_SNV' {
ext.prefix = { "${meta.id}_merged_SNV" }
ext.args = '--rm-dups all --write-index=tbi --allow-overlaps --threads 10'
ext.args = '--rm-dups all --write-index=tbi --allow-overlaps --threads 10 --output-type z'
// ext.extension = 'vcf.gz'
publishDir = [
path: { "${params.outdir}/${meta.id}/merged_SNV" },
mode: 'copy'
Expand Down
55 changes: 40 additions & 15 deletions subworkflows/local/bam2fastq.nf
Original file line number Diff line number Diff line change
@@ -1,38 +1,63 @@
// Convert merged BAM files to extract unmapped/other reads using samtools fastq with methylation tags

include {
SAMTOOLS_MERGE
} from '../../modules/nf-core/samtools/merge/main'
include {
SAMTOOLS_FASTQ
} from '../../modules/nf-core/samtools/fastq/main'
include {
SAMTOOLS_VIEW
} from '../../modules/nf-core/samtools/view/main'

workflow bam2fastq_subworkflow {
take:
ch_bam_files // channel: [ meta, [ bam1, bam2, ... ] ]
ch_fasta // channel: [ meta, fasta ] (optional)
ch_fai // channel: [ meta, fai ] (optional)
ch_gzi // channel: [ meta, gzi ] (optional)

main:
ch_versions = Channel.empty()
// Handle optional inputs - use empty if not provided
// ch_fasta_final = ch_fasta ?: ([[:], []])
// ch_fai_final = ch_fai ?: ([[:], []])

// Run samtools merge
SAMTOOLS_MERGE (
ch_bam_files,
ch_fasta,
ch_fai
ch_bam_files,
ch_fasta,
ch_fai,
ch_gzi
)
ch_versions = ch_versions.mix(SAMTOOLS_MERGE.out.versions)

// Prepare input for SAMTOOLS_VIEW - add empty index to merged BAM
ch_merged_bam_with_index = SAMTOOLS_MERGE.out.bam.map { meta, bam ->
[meta, bam, []] // Add empty index as third element
}

// Extract unmapped reads and convert to CRAM
SAMTOOLS_VIEW (
ch_merged_bam_with_index, // tuple val(meta), path(input), path(index)
ch_fasta, // tuple val(meta2), path(fasta)
[[:], []], // path qname (no qname file)
"crai" // val index_format
)
ch_versions = ch_versions.mix(SAMTOOLS_VIEW.out.versions)

// Convert merged BAM to FASTQ
SAMTOOLS_FASTQ(
SAMTOOLS_MERGE.out.bam,
false // interleave parameter set to false
SAMTOOLS_MERGE.out.bam,
false // interleave parameter set to false
)
ch_versions = ch_versions.mix(SAMTOOLS_FASTQ.out.versions)

emit:
fastq = SAMTOOLS_FASTQ.out.fastq // channel: [meta, [fastq_1, fastq_2]] - paired-end files
interleaved = SAMTOOLS_FASTQ.out.interleaved // channel: [meta, interleaved.fastq] - interleaved file
singleton = SAMTOOLS_FASTQ.out.singleton // channel: [meta, singleton.fastq.gz] - singleton reads
other = SAMTOOLS_FASTQ.out.other // channel: [meta, other.fastq.gz] - unmapped/other reads // channel: [versions.yml]
versions = ch_versions // channel: [versions.yml] - versions of tools used in the workflow
// Original outputs
fastq = SAMTOOLS_FASTQ.out.fastq // channel: [meta, [fastq_1, fastq_2]]
interleaved = SAMTOOLS_FASTQ.out.interleaved // channel: [meta, interleaved.fastq]
singleton = SAMTOOLS_FASTQ.out.singleton // channel: [meta, singleton.fastq.gz]
other = SAMTOOLS_FASTQ.out.other // channel: [meta, other.fastq.gz]

// New unmapped CRAM output
unmapped_cram = SAMTOOLS_VIEW.out.cram // channel: [meta, unmapped.cram]
unmapped_crai = SAMTOOLS_VIEW.out.crai // channel: [meta, unmapped.cram.crai]

versions = ch_versions // channel: [versions.yml]
}
1 change: 0 additions & 1 deletion subworkflows/local/longphase.nf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ workflow longphase_subworkflow {
ch_versions = ch_versions.mix(LONGPHASE_PHASE.out.versions)
emit:
vcf = LONGPHASE_PHASE.out.vcf
sv_vcf = LONGPHASE_PHASE.out.sv_vcf
versions = ch_versions
}
114 changes: 61 additions & 53 deletions subworkflows/local/snv.nf
Original file line number Diff line number Diff line change
Expand Up @@ -18,83 +18,91 @@ include {

workflow snv_subworkflow {
take:
ch_input_clair3 // channel: tuple(val(meta), path(bam), path(bai))
ch_input_clair3 // channel: tuple(val(meta), path(bam), path(bai), val(packaged_model), path(user_model), val(platform))
fasta // channel: tuple(val(meta2), path(fasta))
fai // channel: tuple(val(meta3), path(fai)) - optional
deepvariant // boolean, if true, will run deepvariant on the input bam file
ch_input_deepvariant // channel: [meta, bam, bai]
filter_pass_snv // boolean, if true, will filter for PASS variants
filter_pass_snv // boolean, if true, will filter for PASS variants

main:
ch_versions = Channel.empty()
// Run CLAIR3

// Run CLAIR3 - now with correct input structure
CLAIR3(
ch_input_clair3, // tuple(meta, bam, bai)
fasta, // tuple(meta2, fasta)
fai // tuple(meta3, fai)
ch_input_clair3, // tuple(meta, bam, bai, packaged_model, user_model, platform)
fasta, // tuple(meta2, fasta)
fai // tuple(meta3, fai)
)

// Fix CLAIR3 VCF
CLAIR3_FIX(
CLAIR3.out.vcf, // path to VCF file
CLAIR3.out.tbi // path to TBI file
CLAIR3.out.vcf, // path to VCF file
CLAIR3.out.tbi // path to TBI file
)

// Collect versions
ch_versions = ch_versions.mix(CLAIR3.out.versions)
ch_versions = ch_versions.mix(CLAIR3_FIX.out.versions)

// Handle CLAIR3 filtering
if (filter_pass_snv) {
ch_clair3_vcf = CLAIR3_FIX.out.vcf
.join(CLAIR3_FIX.out.tbi, by: 0)
BCFTOOLS_FILTER_CLAIR3(
ch_clair3_vcf, // tuple(meta, vcf, tbi)
Channel.value([]), // empty channel for samples
Channel.value([]), // empty channel for regions
Channel.value([]) // empty channel for filters
)
ch_final_clair3_vcf = BCFTOOLS_FILTER_CLAIR3.out.vcf
ch_final_clair3_tbi = BCFTOOLS_FILTER_CLAIR3.out.tbi
ch_versions = ch_versions.mix(BCFTOOLS_FILTER_CLAIR3.out.versions)
ch_clair3_vcf = CLAIR3_FIX.out.vcf
.join(CLAIR3_FIX.out.tbi, by: 0)
BCFTOOLS_FILTER_CLAIR3(
ch_clair3_vcf, // tuple(meta, vcf, tbi)
Channel.value([]), // empty channel for samples
Channel.value([]), // empty channel for regions
Channel.value([]) // empty channel for filters
)
ch_final_clair3_vcf = BCFTOOLS_FILTER_CLAIR3.out.vcf
ch_final_clair3_tbi = BCFTOOLS_FILTER_CLAIR3.out.tbi
ch_versions = ch_versions.mix(BCFTOOLS_FILTER_CLAIR3.out.versions)
} else {
ch_final_clair3_vcf = CLAIR3_FIX.out.vcf
ch_final_clair3_tbi = CLAIR3_FIX.out.tbi
ch_final_clair3_vcf = CLAIR3_FIX.out.vcf
ch_final_clair3_tbi = CLAIR3_FIX.out.tbi
}

// Handle DeepVariant
if (deepvariant) {
DEEPVARIANT_RUNDEEPVARIANT(
ch_input_deepvariant,
fasta,
fai,
[[:], []],
[[:], []]
)
ch_versions = ch_versions.mix(DEEPVARIANT_RUNDEEPVARIANT.out.versions)
if (filter_pass_snv) {
ch_deepvariant_vcf = DEEPVARIANT_RUNDEEPVARIANT.out.vcf
.join(DEEPVARIANT_RUNDEEPVARIANT.out.vcf_tbi, by: 0)
BCFTOOLS_FILTER_DEEPVARIANT(
ch_deepvariant_vcf,
Channel.value([]),
Channel.value([]),
Channel.value([])
DEEPVARIANT_RUNDEEPVARIANT(
ch_input_deepvariant,
fasta,
fai,
[[:], []],
[[:], []]
)
ch_final_deepvariant_vcf = BCFTOOLS_FILTER_DEEPVARIANT.out.vcf
ch_final_deepvariant_tbi = BCFTOOLS_FILTER_DEEPVARIANT.out.tbi
ch_versions = ch_versions.mix(BCFTOOLS_FILTER_DEEPVARIANT.out.versions)
} else {
ch_final_deepvariant_vcf = DEEPVARIANT_RUNDEEPVARIANT.out.vcf
ch_final_deepvariant_tbi = DEEPVARIANT_RUNDEEPVARIANT.out.vcf_tbi
}
ch_versions = ch_versions.mix(DEEPVARIANT_RUNDEEPVARIANT.out.versions)

if (filter_pass_snv) {
ch_deepvariant_vcf = DEEPVARIANT_RUNDEEPVARIANT.out.vcf
.join(DEEPVARIANT_RUNDEEPVARIANT.out.vcf_index, by: 0) // Fixed from vcf_tbi
BCFTOOLS_FILTER_DEEPVARIANT(
ch_deepvariant_vcf,
Channel.value([]),
Channel.value([]),
Channel.value([])
)
ch_final_deepvariant_vcf = BCFTOOLS_FILTER_DEEPVARIANT.out.vcf
ch_final_deepvariant_tbi = BCFTOOLS_FILTER_DEEPVARIANT.out.tbi
ch_versions = ch_versions.mix(BCFTOOLS_FILTER_DEEPVARIANT.out.versions)
} else {
ch_final_deepvariant_vcf = DEEPVARIANT_RUNDEEPVARIANT.out.vcf
ch_final_deepvariant_tbi = DEEPVARIANT_RUNDEEPVARIANT.out.vcf_index // Fixed from vcf_tbi
}
} else {
// Create empty channels when DeepVariant is not run
ch_final_deepvariant_vcf = Channel.empty()
ch_final_deepvariant_tbi = Channel.empty()
// Create empty channels when DeepVariant is not run
ch_final_deepvariant_vcf = Channel.empty()
ch_final_deepvariant_tbi = Channel.empty()
}

emit:
clair3_vcf = ch_final_clair3_vcf // Filtered or unfiltered based on filter_pass
clair3_tbi = ch_final_clair3_tbi // Corresponding index
clair3_full_vcf = CLAIR3.out.full_vcf // Original full VCF
clair3_full_tbi = CLAIR3.out.full_tbi // Original full TBI
clair3_pileup_vcf = CLAIR3.out.pileup_vcf // Pileup VCF
clair3_pileup_tbi = CLAIR3.out.pileup_tbi // Pileup TBI
clair3_vcf = ch_final_clair3_vcf // Filtered or unfiltered based on filter_pass
clair3_tbi = ch_final_clair3_tbi // Corresponding index
clair3_phased_vcf = CLAIR3.out.phased_vcf // Optional phased VCF
clair3_phased_tbi = CLAIR3.out.phased_tbi // Optional phased TBI
clair3_gvcf = CLAIR3.out.gvcf // Optional GVCF
clair3_gtbi = CLAIR3.out.gtbi // Optional GVCF index
deepvariant_vcf = ch_final_deepvariant_vcf // Filtered or unfiltered DeepVariant VCF
deepvariant_tbi = ch_final_deepvariant_tbi // Corresponding index
versions = ch_versions
Expand Down
3 changes: 2 additions & 1 deletion workflows/longraredisease.nf
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ workflow longraredisease {
.first()

// Generate FAI index
SAMTOOLS_FAIDX(ch_fasta, true)
SAMTOOLS_FAIDX(ch_fasta, [[:], []], true)
ch_fai = SAMTOOLS_FAIDX.out.fai
ch_versions = ch_versions.mix(SAMTOOLS_FAIDX.out.versions)

Expand Down Expand Up @@ -187,6 +187,7 @@ workflow longraredisease {
bam2fastq_subworkflow(
ch_bam_files,
[[:], []],
[[:], []],
[[:], []]
)

Expand Down
Loading