Skip to content

MultiSeqDemux #8638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 16, 2025
Merged
Show file tree
Hide file tree
Changes from 6 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
8 changes: 8 additions & 0 deletions modules/nf-core/multiseqdemux/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json
channels:
- conda-forge
- bioconda
dependencies:
- "conda-forge::r-seurat=5.3.0"
- "conda-forge::r-seuratobject=5.1.0"
47 changes: 47 additions & 0 deletions modules/nf-core/multiseqdemux/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
process MULTISEQDEMUX {
tag "$meta.id"
label 'process_low'

conda "${moduleDir}/environment.yml"
container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ?
'oras://community.wave.seqera.io/library/r-seurat_r-seuratobject:4c5a804804327d29':
'community.wave.seqera.io/library/r-seurat_r-seuratobject:b11306d1bdc82827' }"

input:
tuple val(meta), path(seurat_object), val(assay)

output:
tuple val(meta), path("*_params_multiseqdemux.csv") , emit: params
tuple val(meta), path("*_res_multiseqdemux.csv") , emit: results
tuple val(meta), path("*_multiseqdemux.rds") , emit: rds
path "versions.yml" , emit: versions

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

script:
quantile = task.ext.assay ?: "0.7"
autoThresh = task.ext.autoThresh ?: "TRUE"
maxiter = task.ext.maxiter ?: "5"
qrangeFrom = task.ext.qrangeFrom ?: "0.1"
qrangeTo = task.ext.qrangeTo ?: "0.9"
qrangeBy = task.ext.qrangeBy ?: "0.05"
verbose = task.ext.verbose ?: 'TRUE'
prefix = task.ext.prefix ?: "${meta.id}"

template 'MultiSeqDemux.R'

stub:
def prefix = task.ext.prefix ?: "${meta.id}"
"""
touch ${prefix}_params_multiseqdemux.csv
touch ${prefix}_res_multiseqdemux.csv
touch ${prefix}_multiseqdemux.rds

cat <<-END_VERSIONS > versions.yml
"${task.process}":
multiseqdemux: \$(Rscript -e "library(Seurat); cat(as.character(packageVersion('Seurat')))")
r-base: \$(Rscript -e "cat(strsplit(R.version[['version.string']], ' ')[[1]][3])")
END_VERSIONS
"""
}
78 changes: 78 additions & 0 deletions modules/nf-core/multiseqdemux/meta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json
name: "multiseqdemux"
description: Identify singlets, doublets and negative cells from multiplexing experiments. Annotate singlets by tags.
keywords:
- demultiplexing
- hashing-based deconvolution
- single-cell
tools:
- "multiseqdemux":
description: "MULTIseqDemux is the demultiplexing module of Seurat, which demultiplex samples based on data from cell hashing."
homepage: "https://satijalab.org/seurat/reference/multiseqdemux"
documentation: "https://satijalab.org/seurat/reference/multiseqdemux"
tool_dev_url: "https://github.com/satijalab/seurat"
doi: "10.1038/s41592-019-0433-8"
licence: ["MIT"]
identifier: ""

input:
- - meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1' ]`
- seurat_object:
type: file
description: |
A `.rds` file containing the seurat object. Assumes that the hash tag oligo (HTO) data has been added and normalized.
- assay:
type: string
description: |
Name of the Hashtag assay, usually called "HTO" by default. Use the custom name if the assay has been named differently.

output:

- params:
- meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1' ]`
- "*_params_multiseqdemux.csv":
type: file
description: The used parameters to call MULTIseqDemux in the R-Script.
pattern: "_params_multiseqdemux.csv"

- results:
- meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1' ]`
- "*_res_multiseqdemux.csv":
type: file
description: Resuls of MULTIseqDemux.
pattern: "_res_multiseqdemux.csv"

- rds:
- meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1' ]`
- "*_multiseqdemux.rds":
type: file
description: SeuratObject saved as RDS.
pattern: "_multiseqdemux.rds"

- versions:
- "versions.yml":
type: file
description: File containing software versions
pattern: "versions.yml"

authors:
- "@LuisHeinzlmeier"
maintainers:
- "@LuisHeinzlmeier"
86 changes: 86 additions & 0 deletions modules/nf-core/multiseqdemux/templates/MultiSeqDemux.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env Rscript

################################################
################################################
## USE PARAMETERS FROM NEXTFLOW ##
################################################
################################################

# cast parameters from nextflow
seuratObj = '$seurat_object'
options(digits=5)
quantile = as.double('$quantile')
autoThresh = as.logical('$autoThresh')
maxiter = as.integer('$maxiter')
qrangeFrom = as.double('$qrangeFrom')
qrangeTo = as.double('$qrangeTo')
qrangeBy = as.double('$qrangeBy')
verbose = as.logical('$verbose')
assay ='$assay'
prefix = '$prefix'

if (! file.exists(seuratObj)){
stop(paste0(seuratObj, ' is not a valid file'))
}

################################################
################################################
## Finish loading libraries ##
################################################
################################################

library(Seurat)

################################################
################################################
## Main Process ##
################################################
################################################

# Loading Seurat object
hashtag <- readRDS(seuratObj)

# Demultiplex cells
if (autoThresh == TRUE) {
hashtag <- MULTIseqDemux(hashtag, assay = assay, quantile = quantile, autoThresh = TRUE, maxiter = maxiter, qrange = seq(from = qrangeFrom, to = qrangeTo, by = qrangeBy), verbose = verbose)
} else {
hashtag <- MULTIseqDemux(hashtag, assay = assay, quantile = quantile, verbose = verbose)
}

################################################
################################################
## SAVING RESULTS ##
################################################
################################################

# create a data frame to save the used parameters in a csv file
Argument <- c("seuratObjectPath", "quantile", "autoThresh", "maxiter", "qrangeFrom", "qrangeTo", "qrangeBy", "verbose", "assay")
Value <- c(seuratObj, quantile, autoThresh, maxiter, qrangeFrom, qrangeTo, qrangeBy, verbose, assay)
params <- data.frame(Argument, Value)
write.csv(params, paste0(prefix ,"_params_multiseqdemux.csv"))

# save the results from MULTIseqDemux()
write.csv(hashtag\$MULTI_ID, paste0(prefix , "_res_multiseqdemux.csv"))
saveRDS(hashtag, file = paste0(prefix ,"_multiseqdemux.rds"))

################################################
################################################
## VERSIONS FILE ##
################################################
################################################

r.version <- paste(R.version[['major']],R.version[['minor']], sep = ".")
seurat.version <- as.character(packageVersion('Seurat'))

writeLines(
c(
'"${task.process}":',
paste(' r-base:', r.version),
paste(' r-seurat:', seurat.version)
),
'versions.yml')

################################################
################################################
################################################
################################################
59 changes: 59 additions & 0 deletions modules/nf-core/multiseqdemux/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
nextflow_process {

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

tag "modules"
tag "modules_nfcore"
tag "multiseqdemux"

test("seuratObject - rds") {

when {
process {
"""
input[0] = [
[ id:'test'], // meta map
file(params.modules_testdata_base_path + '/genomics/homo_sapiens/10xgenomics/cellranger/hashing_demultiplexing/htodemux.rds', checkIfExists: true),
"HTO"
]
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert snapshot(process.out).match() }
)
}

}

test("seuratObject - rds - stub") {

options "-stub"

when {
process {
"""
input[0] = [
[ id:'test'], // meta map
file(params.modules_testdata_base_path + '/genomics/homo_sapiens/10xgenomics/cellranger/hashing_demultiplexing/htodemux.rds', checkIfExists: true),
"HTO"
]
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert snapshot(process.out).match() }
)
}

}

}
Loading
Loading