Skip to content

Commit 5e1bb13

Browse files
ktruvextrukaSchmytzi
authored
canvas_germline (nf-core#11164)
* Add canvas_germline module * Refactor canvas_germline module: rename to canvas/germline, bgzip output, combine ploidy inputs * Update canvas/germline tests to use tuple input for sex and ploidy VCFs * Update container to quay.io and add canvas/germline to conda skip list * Fix lint issues: container registry, meta.yml outputs, test tags, stub gzip syntax * Apply nextflow lint formatting to main.nf * Simplify Canvas version string to 1.40.0 * Update snapshot: canvas version string simplified to 1.40.0 * fix: add EDAM ontology terms to meta.yml * Changed container definition to appease linter --------- Co-authored-by: xtruka <xtruka@mandalore.medair.lcl> Co-authored-by: Daniel Schmitz <daniel.schmitz@gu.se>
1 parent 90e0aed commit 5e1bb13

5 files changed

Lines changed: 434 additions & 0 deletions

File tree

.github/skip_nf_test.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"modules/nf-core/bcl2fastq",
1313
"modules/nf-core/bclconvert",
1414
"modules/nf-core/braker3",
15+
"modules/nf-core/canvas/germline",
1516
"modules/nf-core/celesta",
1617
"modules/nf-core/cellpose",
1718
"modules/nf-core/cellranger/count",
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
process CANVAS_GERMLINE {
2+
tag "$meta.id"
3+
label 'process_high'
4+
5+
container "quay.io/nf-core/canvas:1.40.0"
6+
7+
input:
8+
tuple val(meta), path(bam), path(bai)
9+
path germline_snv_vcf
10+
tuple val(sex), path(male_ploidy_vcf), path(female_ploidy_vcf)
11+
path genomedir
12+
path reference
13+
path filter13
14+
15+
output:
16+
tuple val(meta), path("${prefix}.vcf.gz"), emit: vcf
17+
tuple val(meta), path("${prefix}.CoverageAndVariantFrequency.txt"), emit: covandvarfreq
18+
tuple val("${task.process}"), val('canvas'), val('1.40.0'), topic: versions, emit: versions_canvas
19+
// --version not supported by CLI, please update this manually when updating the tool
20+
21+
when:
22+
task.ext.when == null || task.ext.when
23+
24+
script:
25+
prefix = task.ext.prefix ?: "${meta.id}"
26+
def ploidy_vcf = (sex == 'male') ? male_ploidy_vcf : female_ploidy_vcf
27+
"""
28+
PLOIDYVCF=\$(mod_sex_vcf.py ${ploidy_vcf} ${prefix} ./)
29+
30+
SNV_VCF=${germline_snv_vcf}
31+
if [[ ${germline_snv_vcf} == *.gz ]]; then
32+
gunzip -c ${germline_snv_vcf} > snv_input.vcf
33+
SNV_VCF=snv_input.vcf
34+
fi
35+
36+
mkdir -p Sequence
37+
ln -s \$(realpath ${genomedir}) Sequence/WholeGenomeFasta
38+
39+
Canvas SmallPedigree-WGS \\
40+
--bam=${bam} \\
41+
--sample-b-allele-vcf=\$SNV_VCF \\
42+
--ploidy-vcf=\$PLOIDYVCF \\
43+
-g ./Sequence/WholeGenomeFasta \\
44+
-r ${reference} \\
45+
-f ${filter13} \\
46+
-o ./
47+
48+
if [ -f CNV.vcf.gz ]; then
49+
gunzip CNV.vcf.gz
50+
fi
51+
52+
grep -v 'Canvas:REF' CNV.vcf | bgzip > ${prefix}.vcf.gz
53+
54+
find . -name "CNV.CoverageAndVariantFrequency.txt" | head -1 | xargs -I{} cp {} ./${prefix}.CoverageAndVariantFrequency.txt
55+
"""
56+
57+
stub:
58+
prefix = task.ext.prefix ?: "${meta.id}"
59+
"""
60+
echo "" | gzip > ${prefix}.vcf.gz
61+
touch ${prefix}.CoverageAndVariantFrequency.txt
62+
"""
63+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
name: "canvas_germline"
2+
description: |
3+
Calls germline copy number variants (CNVs) from whole genome sequencing data using
4+
Illumina Canvas in SmallPedigree-WGS mode. Although named SmallPedigree-WGS, this
5+
mode is recommended for single germline samples as well as small pedigrees.
6+
The module accepts aligned reads (BAM), germline SNV calls, and sex-specific ploidy
7+
information, and outputs a VCF with copy number status across the genome together
8+
with a coverage-and-variant-frequency file used for downstream segmentation.
9+
keywords:
10+
- CNV
11+
- copy number variants
12+
- germline
13+
- WGS
14+
- canvas
15+
tools:
16+
- canvas:
17+
description: |
18+
Canvas is a tool for calling copy number variants (CNVs) from human DNA sequencing data.
19+
homepage: "https://github.com/Illumina/canvas"
20+
documentation: "https://github.com/Illumina/canvas/wiki"
21+
tool_dev_url: "https://github.com/Illumina/canvas"
22+
licence:
23+
- "GPL-3.0"
24+
identifier: ""
25+
input:
26+
- - meta:
27+
type: map
28+
description: |
29+
Groovy Map containing sample information.
30+
e.g. `[ id:'sample1' ]`
31+
ontologies: []
32+
- bam:
33+
type: file
34+
description: Sorted BAM file of aligned WGS reads.
35+
pattern: "*.bam"
36+
ontologies:
37+
- edam: "http://edamontology.org/format_2572"
38+
- bai:
39+
type: file
40+
description: BAM index file.
41+
pattern: "*.bai"
42+
ontologies:
43+
- edam: "http://edamontology.org/format_3327"
44+
- germline_snv_vcf:
45+
type: file
46+
description: |
47+
VCF (or gzipped VCF) containing germline SNV b-allele sites (PASS filter only).
48+
Typically produced by a variant caller such as DNAscope or DeepVariant.
49+
pattern: "*.{vcf,vcf.gz}"
50+
ontologies:
51+
- edam: "http://edamontology.org/format_3016"
52+
- - sex:
53+
type: string
54+
description: |
55+
Sample sex. Must be either "male" or "female". Used to select the
56+
appropriate ploidy VCF for sex chromosomes.
57+
- male_ploidy_vcf:
58+
type: file
59+
description: |
60+
VCF defining expected ploidy for sex chromosomes in male samples.
61+
Both ploidy VCFs (male and female) must always be provided. The module
62+
automatically selects the correct one based on the sex value.
63+
The sample name in the VCF header is automatically updated to match the SM tag in the BAM header.
64+
Expected format (hg38 example):
65+
66+
```
67+
##fileformat=VCFv4.1
68+
##INFO=<ID=END,Number=1,Type=Integer,Description="End position of the variant described in this record">
69+
##FORMAT=<ID=CN,Number=1,Type=Integer,Description="Copy number genotype for imprecise events">
70+
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT SAMPLE
71+
chrX 0 . N <CNV> . PASS END=10001 CN 1
72+
chrX 2781479 . N <CNV> . PASS END=155701383 CN 1
73+
chrX 156030895 . N <CNV> . PASS END=156040895 CN 1
74+
chrY 0 . N <CNV> . PASS END=57227415 CN 1
75+
```
76+
pattern: "*.vcf"
77+
ontologies:
78+
- edam: "http://edamontology.org/format_3016"
79+
- female_ploidy_vcf:
80+
type: file
81+
description: |
82+
VCF defining expected ploidy for sex chromosomes in female samples.
83+
Both ploidy VCFs (male and female) must always be provided. The module
84+
automatically selects the correct one based on the sex value.
85+
The sample name in the VCF header is automatically updated to match the SM tag in the BAM header.
86+
Expected format (hg38 example):
87+
88+
```
89+
##fileformat=VCFv4.1
90+
##INFO=<ID=END,Number=1,Type=Integer,Description="End position of the variant described in this record">
91+
##FORMAT=<ID=CN,Number=1,Type=Integer,Description="Copy number genotype for imprecise events">
92+
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT SAMPLE
93+
chrY 0 . N <CNV> . PASS END=57227415 CN 0
94+
```
95+
pattern: "*.vcf"
96+
ontologies:
97+
- edam: "http://edamontology.org/format_3016"
98+
- genomedir:
99+
type: directory
100+
description: |
101+
Canvas genome reference directory containing genome.fa and GenomeSize.xml.
102+
Available for GRCh37/hg19/GRCh38 from s3://canvas-cnv-public.
103+
- reference:
104+
type: file
105+
description: |
106+
Canvas-ready kmer reference file (kmer.fa).
107+
Available from s3://canvas-cnv-public.
108+
pattern: "*.fa"
109+
ontologies:
110+
- edam: "http://edamontology.org/format_1929"
111+
- filter13:
112+
type: file
113+
description: |
114+
BED file of regions to exclude from CNV calling (e.g. centromeres, telomeres).
115+
Named filter13 following Canvas reference package conventions.
116+
Available from s3://canvas-cnv-public.
117+
pattern: "*.bed"
118+
ontologies:
119+
- edam: "http://edamontology.org/format_3003"
120+
output:
121+
vcf:
122+
- - meta:
123+
type: map
124+
description: |
125+
Groovy Map containing sample information.
126+
e.g. `[ id:'sample1' ]`
127+
ontologies: []
128+
- ${prefix}.vcf.gz:
129+
type: file
130+
description: |
131+
Bgzipped VCF file containing germline CNV calls. Canvas:REF entries (non-variant regions)
132+
are excluded. Requires further annotation for standard SV ALT notation.
133+
pattern: "*.vcf.gz"
134+
ontologies:
135+
- edam: "http://edamontology.org/format_3016"
136+
covandvarfreq:
137+
- - meta:
138+
type: map
139+
description: |
140+
Groovy Map containing sample information.
141+
e.g. `[ id:'sample1' ]`
142+
ontologies: []
143+
- ${prefix}.CoverageAndVariantFrequency.txt:
144+
type: file
145+
description: |
146+
Tab-separated file with per-bin coverage and variant allele frequency values.
147+
Used as input for CANVAS_CREATESEG to generate segmentation files.
148+
pattern: "*.CoverageAndVariantFrequency.txt"
149+
ontologies:
150+
- edam: "http://edamontology.org/format_3475"
151+
versions_canvas:
152+
- - ${task.process}:
153+
type: string
154+
description: The name of the process
155+
- canvas:
156+
type: string
157+
description: The name of the tool
158+
- 1.40.0:
159+
type: string
160+
description: The version of the tool (hardcoded as Canvas is an archived
161+
tool)
162+
topics:
163+
versions:
164+
- - ${task.process}:
165+
type: string
166+
description: The name of the process
167+
- canvas:
168+
type: string
169+
description: The name of the tool
170+
- 1.40.0:
171+
type: string
172+
description: The version of the tool (hardcoded as Canvas is an archived
173+
tool)
174+
authors:
175+
- "@ktruve"
176+
maintainers:
177+
- "@ktruve"
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
nextflow_process {
2+
3+
name "Test Process CANVAS_GERMLINE"
4+
script "../main.nf"
5+
process "CANVAS_GERMLINE"
6+
7+
tag "modules"
8+
tag "modules_nfcore"
9+
tag "canvas/germline"
10+
tag "canvas"
11+
12+
test("homo_sapiens - bam - female - stub") {
13+
14+
options "-stub"
15+
16+
when {
17+
process {
18+
"""
19+
input[0] = [
20+
[ id:'test' ],
21+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true),
22+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true)
23+
]
24+
input[1] = file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/NA12878_GIAB.chr21_22.vcf.gz', checkIfExists: true)
25+
input[2] = [
26+
"female",
27+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/NA12878_GIAB.chr22.vcf.gz', checkIfExists: true),
28+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/NA12878.chr22.1X.vcf.gz', checkIfExists: true)
29+
]
30+
input[3] = file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/chr21/sequence/genome.dict', checkIfExists: true)
31+
input[4] = file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta', checkIfExists: true)
32+
input[5] = file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/chr21/sequence/multi_intervals.bed', checkIfExists: true)
33+
"""
34+
}
35+
}
36+
37+
then {
38+
assertAll(
39+
{ assert process.success },
40+
{ assert snapshot(process.out).match() }
41+
)
42+
}
43+
}
44+
45+
test("homo_sapiens - bam - male - stub") {
46+
47+
options "-stub"
48+
49+
when {
50+
process {
51+
"""
52+
input[0] = [
53+
[ id:'test' ],
54+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true),
55+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true)
56+
]
57+
input[1] = file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/NA12878_GIAB.chr21_22.vcf.gz', checkIfExists: true)
58+
input[2] = [
59+
"male",
60+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/NA12878_GIAB.chr22.vcf.gz', checkIfExists: true),
61+
file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/vcf/NA12878.chr22.1X.vcf.gz', checkIfExists: true)
62+
]
63+
input[3] = file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/chr21/sequence/genome.dict', checkIfExists: true)
64+
input[4] = file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta', checkIfExists: true)
65+
input[5] = file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/chr21/sequence/multi_intervals.bed', checkIfExists: true)
66+
"""
67+
}
68+
}
69+
70+
then {
71+
assertAll(
72+
{ assert process.success },
73+
{ assert snapshot(process.out).match() }
74+
)
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)