Skip to content
Open
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
78 changes: 51 additions & 27 deletions modules/nf-core/cellranger/multi/main.nf
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ process CELLRANGER_MULTI {

input:
val meta
tuple val(meta2) , path (gex_fastqs , stageAs: "fastqs/gex/fastq_???/*") , val(gex_options)
tuple val(meta3) , path (vdj_fastqs , stageAs: "fastqs/vdj/fastq_???/*")
tuple val(meta4) , path (ab_fastqs , stageAs: "fastqs/ab/fastq_???/*")
tuple val(meta5) , path (beam_fastqs , stageAs: "fastqs/beam/fastq_???/*")
tuple val(meta6) , path (cmo_fastqs , stageAs: "fastqs/cmo/fastq_???/*")
tuple val(meta7) , path (crispr_fastqs, stageAs: "fastqs/crispr/fastq_???/*")
tuple val(meta2) , path (gex_fastqs , stageAs: "fastqs/gex/fastq_???/*") , val(gex_options)
tuple val(meta3) , path (vdj_fastqs , stageAs: "fastqs/vdj/fastq_???/*") , val(vdj_options)
tuple val(meta4) , path (ab_fastqs , stageAs: "fastqs/ab/fastq_???/*") , val(ab_options)
tuple val(meta5) , path (beam_fastqs , stageAs: "fastqs/beam/fastq_???/*") , val(beam_options)
tuple val(meta6) , path (cmo_fastqs , stageAs: "fastqs/cmo/fastq_???/*") , val(cmo_options)
tuple val(meta7) , path (crispr_fastqs, stageAs: "fastqs/crispr/fastq_???/*"), val(crispr_options)
path gex_reference , stageAs: "references/gex/*"
path gex_frna_probeset , stageAs: "references/gex/probeset/*"
path gex_targetpanel , stageAs: "references/gex/targetpanel/*"
Expand Down Expand Up @@ -66,28 +66,40 @@ process CELLRANGER_MULTI {
gex_section << "reference,\$PWD/${gex_reference.name}"
if (gex_frna_probeset) gex_section << "probe-set,\$PWD/${gex_frna_probeset.name}"

// GEX options from a dedicated input channel
def gex_has_opts = gex_options ?: [:]
def chemistry = gex_has_opts.containsKey("chemistry") ? gex_has_opts["chemistry"] : "auto"
gex_section << "chemistry,${chemistry}"

if (gex_has_opts.containsKey("expect-cells")) {
gex_section << "expect-cells,${gex_has_opts["expect-cells"]}"
// GEX options forwarded from the gex_options input map
['filter-probes', 'r1-length', 'r2-length', 'chemistry', 'expect-cells', 'force-cells',
'no-secondary', 'check-library-compatibility', 'no-target-umi-filter', 'include-introns'].each { key ->
if (gex_options?.containsKey(key)) gex_section << "${key},${gex_options[key]}"
}

def create_bam = gex_has_opts.containsKey("create-bam") ? gex_has_opts["create-bam"] : "true"
gex_section << "create-bam,${create_bam}"
// create-bam defaults to true if not specified
gex_section << "create-bam,${gex_options?.get('create-bam') ?: 'true'}"

if (gex_targetpanel) {
gex_section << "target-panel,\$PWD/${gex_targetpanel.name}"
}

// CMO-related settings that live inside the [gene-expression] section
if (has_cmo) {
if (cmo_options?.containsKey('min-assignment-confidence')) {
gex_section << "min-assignment-confidence,${cmo_options['min-assignment-confidence']}"
}
if (cmo_reference) gex_section << "cmo-set,\$PWD/${cmo_reference.name}"
if (cmo_barcode_assignment) gex_section << "barcode-sample-assignment,\$PWD/${cmo_barcode_assignment.name}"
}
}

// Build [feature] section
def fb_section = []
if (has_ab || has_crispr) {
if (has_ab || has_crispr || has_beam) {
fb_section << '[feature]'
fb_section << 'reference,\$PWD/fb_reference_copy.csv'
if (has_ab || has_crispr) fb_section << 'reference,\$PWD/fb_reference_copy.csv'
if (has_beam && beam_antigen_panel) fb_section << "reference,\$PWD/${beam_antigen_panel.name}"

// r1/r2-length from ab_options takes priority over crispr_options
def fb_opts = has_ab ? ab_options : (has_crispr ? crispr_options : null)
if (fb_opts?.containsKey('r1-length')) fb_section << "r1-length,${fb_opts['r1-length']}"
if (fb_opts?.containsKey('r2-length')) fb_section << "r2-length,${fb_opts['r2-length']}"
}

// Build [vdj] section
Expand All @@ -98,6 +110,8 @@ process CELLRANGER_MULTI {
if (vdj_primer_index) {
vdj_section << "inner-enrichment-primers,\$PWD/${vdj_primer_index.name}"
}
if (vdj_options?.containsKey('r1-length')) vdj_section << "r1-length,${vdj_options['r1-length']}"
if (vdj_options?.containsKey('r2-length')) vdj_section << "r2-length,${vdj_options['r2-length']}"
}

// Build [libraries] section
Expand Down Expand Up @@ -129,25 +143,35 @@ process CELLRANGER_MULTI {
config_lines << '[samples]'
config_lines << ocm_barcodes.text.trim()
}
if (has_beam) {
config_lines << '[antigen-specificity]'
config_lines << beam_control_panel.text.trim()
}
def config_content = config_lines.findAll { line -> line }.join('\n ')
"""
#
# Rename FASTQs to Cell Ranger naming convention
# Maintains R1/R2 pairing order by processing directories sequentially
# Output pattern: \${prefix}_S1_L001_R1_001.fastq.gz, L002_R1_001.fastq.gz, etc.
# Symlink FASTQs into fastq_all/, maintaining R1/R2 lane-pairing order.
# skip_renaming=false (default): rename to Cell Ranger convention \${prefix}_S1_L00N_R[12]_001.fastq.gz
# skip_renaming=true: keep original filenames as-is
#
mkdir -p fastq_all/{gex,vdj,ab,beam,cmo,crispr}

for modality in gex vdj ab beam cmo crispr; do
lane=1
while IFS= read -r -d '' r1_dir && IFS= read -r -d '' r2_dir; do
r1=\$(find "\${r1_dir}" -maxdepth 1 -name "*_R1_*.fastq.gz" | head -1)
r2=\$(find "\${r2_dir}" -maxdepth 1 -name "*_R2_*.fastq.gz" | head -1)

[ -z "\${r1}" ] || [ -z "\${r2}" ] && continue

ln -sf "\$(readlink -f "\${r1}")" "fastq_all/\${modality}/${prefix}_S1_L\$(printf %03d \${lane})_R1_001.fastq.gz"
ln -sf "\$(readlink -f "\${r2}")" "fastq_all/\${modality}/${prefix}_S1_L\$(printf %03d \${lane})_R2_001.fastq.gz"
if [ "${skip_renaming}" = "true" ]; then
r1=\$(find "\${r1_dir}" -maxdepth 1 -name "*.fastq.gz" | head -1)
r2=\$(find "\${r2_dir}" -maxdepth 1 -name "*.fastq.gz" | head -1)
[ -z "\${r1}" ] || [ -z "\${r2}" ] && continue
ln -sf "\$(readlink -f "\${r1}")" "fastq_all/\${modality}/\$(basename "\${r1}")"
ln -sf "\$(readlink -f "\${r2}")" "fastq_all/\${modality}/\$(basename "\${r2}")"
else
r1=\$(find "\${r1_dir}" -maxdepth 1 -name "*_R1_*.fastq.gz" | head -1)
r2=\$(find "\${r2_dir}" -maxdepth 1 -name "*_R2_*.fastq.gz" | head -1)
[ -z "\${r1}" ] || [ -z "\${r2}" ] && continue
ln -sf "\$(readlink -f "\${r1}")" "fastq_all/\${modality}/${prefix}_S1_L\$(printf %03d \${lane})_R1_001.fastq.gz"
ln -sf "\$(readlink -f "\${r2}")" "fastq_all/\${modality}/${prefix}_S1_L\$(printf %03d \${lane})_R2_001.fastq.gz"
fi
lane=\$((lane + 1))
done < <(find fastqs/\${modality} -maxdepth 1 -type d -name "fastq_*" | sort | xargs -n1 printf '%s\\0')
done
Expand Down
30 changes: 28 additions & 2 deletions modules/nf-core/cellranger/multi/meta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ input:
- gex_options:
type: map
description: |
Optional Cell Ranger gene expression section options.
Supported keys include chemistry, expect-cells, and create-bam.
Optional Cell Ranger gene expression section options as a Map.
Supported keys: filter-probes, r1-length, r2-length, chemistry, expect-cells,
force-cells, no-secondary, check-library-compatibility, no-target-umi-filter,
include-introns, create-bam (defaults to true if not set).
- - meta3:
type: map
description: |
Expand All @@ -54,6 +56,11 @@ input:
pattern: "*.fastq.gz"
ontologies:
- edam: http://edamontology.org/format_3989
- vdj_options:
type: map
description: |
Optional Cell Ranger VDJ section options as a Map.
Supported keys: r1-length, r2-length.
- - meta4:
type: map
description: |
Expand All @@ -64,6 +71,11 @@ input:
pattern: "*.fastq.gz"
ontologies:
- edam: http://edamontology.org/format_3989
- ab_options:
type: map
description: |
Optional Cell Ranger feature section options for Antibody Capture as a Map.
Pass null to disable this modality. Supported keys: r1-length, r2-length.
- - meta5:
type: map
description: |
Expand All @@ -74,6 +86,10 @@ input:
pattern: "*.fastq.gz"
ontologies:
- edam: http://edamontology.org/format_3989
- beam_options:
type: map
description: |
Optional Cell Ranger BEAM section options as a Map. Reserved for future use.
- - meta6:
type: map
description: |
Expand All @@ -84,6 +100,11 @@ input:
pattern: "*.fastq.gz"
ontologies:
- edam: http://edamontology.org/format_3989
- cmo_options:
type: map
description: |
Optional Cell Ranger CMO section options as a Map.
Supported keys: min-assignment-confidence.
- - meta7:
type: map
description: |
Expand All @@ -94,6 +115,11 @@ input:
pattern: "*.fastq.gz"
ontologies:
- edam: http://edamontology.org/format_3989
- crispr_options:
type: map
description: |
Optional Cell Ranger feature section options for CRISPR Guide Capture as a Map.
Pass null to disable this modality. Supported keys: r1-length, r2-length.
- gex_reference:
type: directory
description: Folder containing Cellranger gene expression reference. Can also
Expand Down
82 changes: 41 additions & 41 deletions modules/nf-core/cellranger/multi/tests/main.nf.test
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,12 @@ nextflow_process {
// create empty channels to fill unused cellranger multi arguments
// fastqs need a [ meta, ref ] structure
// references just need a path
ch_gex_fastqs = [ [:], [], [] ]
ch_vdj_fastqs = [ [:], [] ]
ch_ab_fastqs = [ [:], [] ]
ch_beam_fastqs = [ [:], [] ]
ch_cmo_fastqs = [ [:], [] ]
ch_crispr_fastqs = [ [:], [] ]
ch_gex_fastqs = [ [:], [], [:] ]
ch_vdj_fastqs = [ [:], [], [:] ]
ch_ab_fastqs = [ [:], [], [:] ]
ch_beam_fastqs = [ [:], [], [:] ]
ch_cmo_fastqs = [ [:], [], [:] ]
ch_crispr_fastqs = [ [:], [], [:] ]
ch_gex_frna_probeset = []
ch_gex_targetpanel = []
ch_vdj_primer_index = []
Expand All @@ -192,10 +192,10 @@ nextflow_process {
ch_vdj_reference = Channel.of( vdj_reference )
ch_bcell_fastqs_10k_pbmc = Channel.of( bcell_fastqs_10k_pbmc )
.collect()
.map { reads -> [ [ id:bcell_fastq_samplename_10k_pbmc ], reads ] }
.map { reads -> [ [ id:bcell_fastq_samplename_10k_pbmc ], reads, [:] ] }
ch_ab_fastqs_10k_pbmc = Channel.of( ab_fastqs_10k_pbmc )
.collect()
.map { reads -> [ [ id:fivepab_fastq_samplename_10k_pbmc ], reads ] }
.map { reads -> [ [ id:fivepab_fastq_samplename_10k_pbmc ], reads, [:] ] }
ch_ab_reference_10k_pbmc = Channel.of( fb_reference_10k_pbmc )

//
Expand Down Expand Up @@ -388,12 +388,12 @@ nextflow_process {
// create empty channels to fill unused cellranger multi arguments
// fastqs need a [ meta, ref ] structure
// references just need a path
ch_gex_fastqs = [ [:], [], [] ]
ch_vdj_fastqs = [ [:], [] ]
ch_ab_fastqs = [ [:], [] ]
ch_beam_fastqs = [ [:], [] ]
ch_cmo_fastqs = [ [:], [] ]
ch_crispr_fastqs = [ [:], [] ]
ch_gex_fastqs = [ [:], [], [:] ]
ch_vdj_fastqs = [ [:], [], [:] ]
ch_ab_fastqs = [ [:], [], [:] ]
ch_beam_fastqs = [ [:], [], [:] ]
ch_cmo_fastqs = [ [:], [], [:] ]
ch_crispr_fastqs = [ [:], [], [:] ]
ch_gex_frna_probeset = []
ch_gex_targetpanel = []
ch_vdj_primer_index = []
Expand All @@ -413,10 +413,10 @@ nextflow_process {
ch_vdj_reference = Channel.of( vdj_reference )
ch_bcell_fastqs_10k_pbmc = Channel.of( bcell_fastqs_10k_pbmc )
.collect()
.map { reads -> [ [ id:bcell_fastq_samplename_10k_pbmc ], reads ] }
.map { reads -> [ [ id:bcell_fastq_samplename_10k_pbmc ], reads, [:] ] }
ch_ab_fastqs_10k_pbmc = Channel.of( ab_fastqs_10k_pbmc )
.collect()
.map { reads -> [ [ id:fivepab_fastq_samplename_10k_pbmc ], reads ] }
.map { reads -> [ [ id:fivepab_fastq_samplename_10k_pbmc ], reads, [:] ] }
ch_ab_reference_10k_pbmc = Channel.of( fb_reference_10k_pbmc )

ch_gex_fastqs_10k_pbmc_cmo = Channel.of( threepgex_fastqs_10k_pbmc_cmo )
Expand All @@ -425,7 +425,7 @@ nextflow_process {
ch_vdj_reference = Channel.of( vdj_reference )
ch_cmo_fastqs_10k_pbmc_cmo = Channel.of( cmo_fastqs_10k_pbmc_cmo )
.collect()
.map { reads -> [ [ id:cmo_fastq_samplename_10k_pbmc_cmo ], reads ] }
.map { reads -> [ [ id:cmo_fastq_samplename_10k_pbmc_cmo ], reads, [:] ] }
ch_cmo_reference_10k_pbmc_cmo = Channel.of( cmo_reference_10k_pbmc_cmo )

// empty vdj reference
Expand Down Expand Up @@ -624,12 +624,12 @@ nextflow_process {
// create empty channels to fill unused cellranger multi arguments
// fastqs need a [ meta, ref ] structure
// references just need a path
ch_gex_fastqs = [ [:], [], [] ]
ch_vdj_fastqs = [ [:], [] ]
ch_ab_fastqs = [ [:], [] ]
ch_beam_fastqs = [ [:], [] ]
ch_cmo_fastqs = [ [:], [] ]
ch_crispr_fastqs = [ [:], [] ]
ch_gex_fastqs = [ [:], [], [:] ]
ch_vdj_fastqs = [ [:], [], [:] ]
ch_ab_fastqs = [ [:], [], [:] ]
ch_beam_fastqs = [ [:], [], [:] ]
ch_cmo_fastqs = [ [:], [], [:] ]
ch_crispr_fastqs = [ [:], [], [:] ]
ch_gex_frna_probeset = []
ch_gex_targetpanel = []
ch_vdj_primer_index = []
Expand All @@ -649,10 +649,10 @@ nextflow_process {
ch_vdj_reference = Channel.of( vdj_reference )
ch_bcell_fastqs_10k_pbmc = Channel.of( bcell_fastqs_10k_pbmc )
.collect()
.map { reads -> [ [ id:bcell_fastq_samplename_10k_pbmc ], reads ] }
.map { reads -> [ [ id:bcell_fastq_samplename_10k_pbmc ], reads, [:] ] }
ch_ab_fastqs_10k_pbmc = Channel.of( ab_fastqs_10k_pbmc )
.collect()
.map { reads -> [ [ id:fivepab_fastq_samplename_10k_pbmc ], reads ] }
.map { reads -> [ [ id:fivepab_fastq_samplename_10k_pbmc ], reads, [:] ] }
ch_ab_reference_10k_pbmc = Channel.of( fb_reference_10k_pbmc )

ch_gex_fastqs_5k_cmvpos_tcells = Channel.of( gex_fastqs_5k_cmvpos_tcells )
Expand All @@ -661,11 +661,11 @@ nextflow_process {
ch_vdj_reference = Channel.of( vdj_reference )
ch_vdj_fastqs_5k_cmvpos_tcells = Channel.of( vdj_fastqs_5k_cmvpos_tcells )
.collect()
.map { reads -> [ [ id:vdj_fastq_samplename_5k_cmvpos_tcells ], reads ] }
.map { reads -> [ [ id:vdj_fastq_samplename_5k_cmvpos_tcells ], reads, [:] ] }
ch_fb_reference_5k_cmvpos_tcells = Channel.of( fb_reference_5k_cmvpos_tcells )
ch_ab_fastqs_5k_cmvpos_tcells = Channel.of( ab_fastqs_5k_cmvpos_tcells )
.collect()
.map { reads -> [ [ id:ab_fastq_samplename_5k_cmvpos_tcells ], reads ] }
.map { reads -> [ [ id:ab_fastq_samplename_5k_cmvpos_tcells ], reads, [:] ] }

//
// execution
Expand Down Expand Up @@ -779,12 +779,12 @@ nextflow_process {
// create empty channels to fill unused cellranger multi arguments
// fastqs need a [ meta, ref ] structure
// references just need a path
ch_gex_fastqs = [ [:], [], [] ]
ch_vdj_fastqs = [ [:], [] ]
ch_ab_fastqs = [ [:], [] ]
ch_beam_fastqs = [ [:], [] ]
ch_cmo_fastqs = [ [:], [] ]
ch_crispr_fastqs = [ [:], [] ]
ch_gex_fastqs = [ [:], [], [:] ]
ch_vdj_fastqs = [ [:], [], [:] ]
ch_ab_fastqs = [ [:], [], [:] ]
ch_beam_fastqs = [ [:], [], [:] ]
ch_cmo_fastqs = [ [:], [], [:] ]
ch_crispr_fastqs = [ [:], [], [:] ]
ch_gex_frna_probeset = []
ch_gex_targetpanel = []
ch_vdj_reference = []
Expand All @@ -802,11 +802,11 @@ nextflow_process {

ch_gex_fastqs_sc3_v3_5k_a549 = Channel.of( test_10x_sc3_v3_5k_a549_gex )
.collect()
.map { reads -> [ [ id:test_10x_sc3_v3_5k_a549_gex_samplename ], reads, [ "expect-cells":"5000", chemistry:"SC3Pv3", "create-bam":false, "no-secondary":true ] ] }
.map { reads -> [ [ id:test_10x_sc3_v3_5k_a549_gex_samplename ], reads, [ "expect-cells":"5000", chemistry:"SC3Pv3", "create-bam":false ] ] }

ch_crispr_fastqs_sc3_v3_5k_a549 = Channel.of( test_10x_sc3_v3_5k_a549_crispr )
.collect()
.map { reads -> [ [ id:test_10x_sc3_v3_5k_a549_crispr_samplename ], reads ] }
.map { reads -> [ [ id:test_10x_sc3_v3_5k_a549_crispr_samplename ], reads, [:] ] }

ch_crispr_reference_sc3_v3_5k_a549 = Channel.of( test_10x_sc3_v3_5k_a549_crispr_fb_reference )

Expand Down Expand Up @@ -896,12 +896,12 @@ nextflow_process {
// create empty channels to fill unused cellranger multi arguments
// fastqs need a [ meta, ref ] structure
// references just need a path
ch_gex_fastqs = [ [:], [], [] ]
ch_vdj_fastqs = [ [:], [] ]
ch_ab_fastqs = [ [:], [] ]
ch_beam_fastqs = [ [:], [] ]
ch_cmo_fastqs = [ [:], [] ]
ch_crispr_fastqs = [ [:], [] ]
ch_gex_fastqs = [ [:], [], [:] ]
ch_vdj_fastqs = [ [:], [], [:] ]
ch_ab_fastqs = [ [:], [], [:] ]
ch_beam_fastqs = [ [:], [], [:] ]
ch_cmo_fastqs = [ [:], [], [:] ]
ch_crispr_fastqs = [ [:], [], [:] ]
ch_gex_frna_probeset = []
ch_gex_targetpanel = []
ch_vdj_reference = []
Expand Down