Skip to content
Merged
1 change: 1 addition & 0 deletions .nf-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ org_path: UMCUGenetics
repository_type: modules
container-registry:
- ghcr.io
- 079623148045.dkr.ecr.eu-central-1.amazonaws.com
68 changes: 68 additions & 0 deletions modules/UMCUGenetics/dragen/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
process DRAGEN {
// Dragen requires specific hardware (FPGA) to run.
// This process is therefore only executed on Illumina® BioInsight Platform Core / Illumina Connected Analytics (ICA) compute nodes with FPGA support.
// Test only contain a stub of the process, which can be executed on any compute node, but does not perform any actual processing.
tag "${meta.id}"
label 'process_fpga'

// Dragen 4.2.4-2 (F2 compatible container)
container '079623148045.dkr.ecr.eu-central-1.amazonaws.com/cp-prod/f1b7ad6a-11ac-4bc1-b705-b275ff2887ad:latest'

input:
// TODO: Move ref_tar unpacking to a separate process to avoid unpacking the reference for every sample? -> added in prs branch
tuple val(meta), path(r1_fastq), path(r2_fastq)
path fastq_list
path ref_tar
path repeat_genotype_specs

output:
tuple val(meta), path("*"), emit: output
tuple val(meta), path('*.csv'), emit: csv
tuple val(meta), path("${prefix}.wgs_coverage_metrics.csv"), optional: true, emit: wgs_coverage_metrics
tuple val(meta), path("${prefix}.cnv_metrics.csv"), optional: true, emit: cnv_metrics
tuple val(meta), path("${prefix}.mapping_metrics.csv"), optional: true, emit: mapping_metrics
tuple val(meta), path("${prefix}.ploidy_estimation_metrics.csv"), optional: true, emit: ploidy_estimation_metrics
tuple val(meta), path("${prefix}.gvcf_metrics.csv"), optional: true, emit: gvcf_metrics
tuple val("${task.process}"), val('dragen'), eval("dragen --version 2>&1 | sed 's/^dragen Version //'"), topic: versions, emit: versions_dragen

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dragen --version in the eval block fails because the dragen binary seems to be at /opt/edico/bin/dragen and is not in $PATH. I would suggest to update it to use the full path: /opt/edico/bin/dragen --version 2>&1 | sed 's/^dragen Version //'.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem here is that we can't run this test using the dragen container -> this proprietary software and we can't access it, furthermore we don't have FPGA nodes available within GitHub Actions. In order to make the stub test work (just testing in/output logic) we overwrite the container in https://github.com/UMCUGenetics/NF-Modules/blob/feature/srwgs/tests/config/modules.config with a simple container containing only bash.

See also nf-core example: https://github.com/nf-core/modules/blob/master/modules/nf-core/dragen/germline/tests/main.nf.test.snap

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense now. Thanks for the clarification!


when:
task.ext.when == null || task.ext.when

script:
prefix = task.ext.prefix ?: "${meta.id}"
def args = task.ext.args ?: ''

if (repeat_genotype_specs) {
args = args + " --repeat-genotype-enable true --repeat-genotype-specs " + repeat_genotype_specs
}

"""
mkdir -p /scratch/reference

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can imagine that hardcoding /scratch/reference could become problematic when multiple DRAGEN tasks run concurrently on the same node. This may increase the risk of race conditions or file collisions. I would suggest using task-specific scratch directories (for example /scratch/${task.workDir.name}) to priovide better isolation between tasks.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Jorisvansteenbrugge is 'fixing' this by creating a separate process for untarring the reference genome, see: https://github.com/UMCUGenetics/NF-Modules/blob/feature/dragen_genome_untar/modules/UMCUGenetics/untar/genome/main.nf

This also makes sure that we only perform the untar once per pipeline excecution.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice solution @Jorisvansteenbrugge ! I think it's safe to keep this as-is too.

tar -C /scratch/reference -xf ${ref_tar}

/opt/edico/bin/dragen --partial-reconfig HMM --ignore-version-check true

/opt/edico/bin/dragen --lic-instance-id-location /opt/instance-identity \\
--ref-dir /scratch/reference/DRAGEN/9 \\
--fastq-list ${fastq_list} \\
--fastq-list-sample-id ${meta.id} \\
--output-file-prefix ${prefix} \\
--output-directory ./ \\
--intermediate-results-dir /scratch \\

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the comment above about hardcoded /scratch.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for this module it is fine to just use /scratch, because this process can only run on ICA, where this /scratch is a special folder (see https://help.ica.illumina.com/project/p-flow/f-pipelines#scratch-space-notes). On ICA this scratch folder is unique to the job.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. You may ignore my comment.

${args}
"""

stub:
prefix = task.ext.prefix ?: "${meta.id}"
def args = task.ext.args ?: ''
"""
echo ${args}

touch ${prefix}.bam
touch ${prefix}.wgs_coverage_metrics.csv
touch ${prefix}.cnv_metrics.csv
touch ${prefix}.mapping_metrics.csv
touch ${prefix}.ploidy_estimation_metrics.csv
touch ${prefix}.gvcf_metrics.csv
"""
}
37 changes: 37 additions & 0 deletions modules/UMCUGenetics/dragen/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
nextflow_process {

name "Test Process DRAGEN"
script "../main.nf"
process "DRAGEN"

tag "modules"
tag "modules_UMCUGenetics"
tag "dragen"

test("dragen - stub") {
options '-stub'
when {
process {
"""
input[0] = [
[id:'sample1'],
[],
[]
]
input[1] = []
input[2] = []
input[3] = []
"""
}
}
then {
assertAll(
{ assert process.success },
{ assert snapshot(
process.out,
process.out.findAll { key, val -> key.startsWith('versions') }
).match() }
)
}
}
}
174 changes: 174 additions & 0 deletions modules/UMCUGenetics/dragen/tests/main.nf.test.snap
Comment thread
rernst marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
{
"dragen - stub": {
"content": [
{
"0": [
[
{
"id": "sample1"
},
[
"sample1.bam:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.cnv_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.gvcf_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.mapping_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.ploidy_estimation_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.wgs_coverage_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
]
],
"1": [
[
{
"id": "sample1"
},
[
"sample1.cnv_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.gvcf_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.mapping_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.ploidy_estimation_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.wgs_coverage_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
]
],
"2": [
[
{
"id": "sample1"
},
"sample1.wgs_coverage_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"3": [
[
{
"id": "sample1"
},
"sample1.cnv_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"4": [
[
{
"id": "sample1"
},
"sample1.mapping_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"5": [
[
{
"id": "sample1"
},
"sample1.ploidy_estimation_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"6": [
[
{
"id": "sample1"
},
"sample1.gvcf_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"7": [
[
"DRAGEN",
"dragen",
"bash: line 1: dragen: command not found"
]
],
"cnv_metrics": [
[
{
"id": "sample1"
},
"sample1.cnv_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"csv": [
[
{
"id": "sample1"
},
[
"sample1.cnv_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.gvcf_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.mapping_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.ploidy_estimation_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.wgs_coverage_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
]
],
"gvcf_metrics": [
[
{
"id": "sample1"
},
"sample1.gvcf_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"mapping_metrics": [
[
{
"id": "sample1"
},
"sample1.mapping_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"output": [
[
{
"id": "sample1"
},
[
"sample1.bam:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.cnv_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.gvcf_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.mapping_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.ploidy_estimation_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e",
"sample1.wgs_coverage_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
]
],
"ploidy_estimation_metrics": [
[
{
"id": "sample1"
},
"sample1.ploidy_estimation_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
],
"versions_dragen": [
[
"DRAGEN",
"dragen",
"bash: line 1: dragen: command not found"
]
],
"wgs_coverage_metrics": [
[
{
"id": "sample1"
},
"sample1.wgs_coverage_metrics.csv:md5,d41d8cd98f00b204e9800998ecf8427e"
]
]
},
{
"versions_dragen": [
[
"DRAGEN",
"dragen",
"bash: line 1: dragen: command not found"
]
]
}
],
"timestamp": "2026-08-18T11:08:58.003281",
"meta": {
"nf-test": "0.9.5",
"nextflow": "25.10.2"
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline at the end of this file, see this StackOverflow thread on why it's important.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snap files are automatically generated by nf-test. I don't think we should update/change these because they are compared using diff when running (automated) tests.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, lets keep them as-is :)

37 changes: 37 additions & 0 deletions modules/UMCUGenetics/dragenutils/dxqc/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
process DRAGENUTILS_DXQC {
tag "${meta.id}"
label 'process_single'

container 'ghcr.io/umcugenetics/dragen-utils:1.0.0'

input:
tuple val(meta), path(wgs_coverage_metrics), path(cnv_metrics), path(mapping_metrics), path(ploidy_estimation_metrics), path(gvcf_metrics)

output:
tuple val(meta), path("${prefix}.dragen_dx_qc.csv"), emit: csv
tuple val("${task.process}"), val('dragenutils'), eval("dragen-utils --version"), topic: versions, emit: versions_dragenutils

when:
task.ext.when == null || task.ext.when

script:
prefix = task.ext.prefix ?: "${meta.id}"

"""
dragen-utils dx_qc \\
${prefix} \\
${wgs_coverage_metrics} \\
${cnv_metrics} \\
${mapping_metrics} \\
${ploidy_estimation_metrics} \\
${gvcf_metrics} \\
> ${prefix}.dragen_dx_qc.csv
"""

stub:
prefix = task.ext.prefix ?: "${meta.id}"

"""
touch ${prefix}.dragen_dx_qc.csv
"""
}
40 changes: 40 additions & 0 deletions modules/UMCUGenetics/dragenutils/dxqc/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
nextflow_process {

name "Test Process DRAGENUTILS_DXQC"
script "../main.nf"
process "DRAGENUTILS_DXQC"

tag "modules"
tag "modules_UMCUGenetics"
tag "dragenutils"
tag "dragenutils/dxqc"

test("HG002 - srWGS QC csv files") {
when {
process {
"""
input[0] = [
[ id:'HG002' ],
file(params.modules_umcu_testdata_base_path + "dragen_utils/qc/HG002.wgs_coverage_metrics.csv", checkIfExists: true),
file(params.modules_umcu_testdata_base_path + "dragen_utils/qc/HG002.cnv_metrics.csv", checkIfExists: true),
file(params.modules_umcu_testdata_base_path + "dragen_utils/qc/HG002.mapping_metrics.csv", checkIfExists: true),
file(params.modules_umcu_testdata_base_path + "dragen_utils/qc/HG002.ploidy_estimation_metrics.csv", checkIfExists: true),
file(params.modules_umcu_testdata_base_path + "dragen_utils/qc/HG002.gvcf_metrics.csv", checkIfExists: true),
]
"""
}
}

then {
assert process.success
assertAll(
{ assert snapshot(
process.out,
process.out.findAll { key, val -> key.startsWith('versions') }
).match() }
)
}

}

}
Loading
Loading