Skip to content

Repository files navigation

Nextflow Pipeline for 10x Genomics single cell runs

ci Nextflow Singularity cellranger 10.1.0 cellranger 9.0.1 cellranger-arc 2.2.0


mode tool status
gex cellranger count working
arc cellranger-arc count working
cellranger aggr working, for multi-library labels
atac cellranger-atac count planned

Requires Nextflow >= 24.04, tested against 25.04.1.

Usage

Grab params.yaml next to your sample sheet:

wget https://raw.githubusercontent.com/bixBeta/10x/main/params.yaml

Set ref and anything else you need, then:

nextflow run bixBeta/10x -r main -params-file params.yaml

It carries every default with comments, so a line only needs changing if you want something else. Note the single dash: -params-file is a Nextflow option, not a pipeline param.

The command line still wins over the file, which is handy for one-off tweaks:

nextflow run bixBeta/10x -r main -params-file params.yaml --crversion 10.1.0

Everything can be passed directly instead, if you prefer:

nextflow run bixBeta/10x -r main --id BRC_1234 --sheet sample-sheet.csv --ref Human
nextflow run bixBeta/10x -r main --help
nextflow run bixBeta/10x -r main --listRefs

The run directory gathers the summaries so a whole run can be skimmed at a glance:

CELLRANGER/JS4/outs   -> work/../outs        the full cellranger output
filtered_counts/JS4/  filtered_feature_bc_matrix.h5
web_summary_htmls/    JS4_web_summary.html   JS5_web_summary.html    ...
summary_metrics/      JS4_metrics_summary.csv  JS5_metrics_summary.csv ...
                      ALL_metrics_summary.csv
pipeline_info/        software_versions.yml

ALL_metrics_summary.csv is every per library table stacked, header kept once, with a leading label column so each row can be traced back — Cell Ranger's own metrics_summary.csv carries no sample identifier. Multiome joins the same directories, and since cellranger-arc reports different columns it gets its own ALL_summary.csv; CELLRANGER_ARC/<label>/ also keeps the generated libraries.csv, and CELLRANGER_AGGR/<label>/ the aggregated result.

CELLRANGER/<label>/outs is a single symlink to the task's real outs, so everything Cell Ranger wrote — matrices, molecule_info.h5, analysis/, the BAM if asked for — is reachable from the run directory without copying anything. Multiome and aggregation mirror theirs the same way under CELLRANGER_ARC/<label>/ and CELLRANGER_AGGR/<label>/.

Everything published is a link into work/, so keep work/ until you are done, or dereference when archiving (cp -rL, rsync -L, tar -h).

Sample sheet

Most runs need two columns:

label,fastqs
JS4,/local/Illumina/DRV/260810_RX_0556_253GGLLT4/Unaligned/Project_10488923/Sample_SC2620_JS4_G3_Reign_10488923_253GGLLT4_L2
JS5,/local/Illumina/DRV/260810_RX_0556_253GGLLT4/Unaligned/Project_10488923/Sample_SC2620_JS5_G3_Sofia_10488923_253GGLLT4_L2

label — your short name for the library. It becomes the output folder and Cell Ranger's --id.

fastqs — the 10x delivery directory, used exactly as given. Nothing is copied or renamed. Any fastq inside it works too, and resolves to its parent.

The remaining columns are optional, one per situation:

column add it when
library one sample was run in two GEM wells
sample the fastqs path holds more than one sample
library_type multiome, to mark the GEX and ATAC halves

The same library sequenced twice

Repeat the label. Both runs are pooled into one count.

label,fastqs
JS5,/local/Illumina/DRV/run1/.../Sample_SC2620_JS5_G3_Sofia_10488923_253GGLLT4_L2
JS5,/local/Illumina/DRV/run2/.../Sample_SC2620_JS5_G3_Sofia_10488923_999XYZAB2_L3

Two GEM wells from one sample

Give each its own library. They are counted separately, since barcodes are not comparable across GEM wells, then combined with cellranger aggr.

label,library,fastqs
JS6,JS6_wellA,/local/.../Sample_SC2620_JS6A_G3_Scooter_10488923_253GGLLT4_L2
JS6,JS6_wellB,/local/.../Sample_SC2620_JS6B_G3_Scooter_10488923_253GGLLT4_L2

You get a count per library plus CELLRANGER_AGGR/JS6/. Labels with a single library are left alone — a re-sequenced library was already pooled into one count, and aggregating it would only depth-normalise two halves of the same library against each other. --aggr false skips the step; --normalize controls Cell Ranger's own depth normalisation (mapped, the default, or none).

A path with several samples in it

Pointing at a Project_* directory is fine, but name which sample the row means. Without it the run stops rather than guessing.

label,fastqs,sample
JS4,/local/.../Project_10488923,SC2620_JS4_G3_Reign_10488923_253GGLLT4

Multiome (--mode arc)

One label, two rows: the GEX library and the ATAC library, marked with library_type.

label,fastqs,sample,library_type
JS4,/local/.../Project_10488522,SC2619_JS4_BC_MG3_8Healthy_10488522_25FWVCLT4,Gene Expression
JS4,/local/.../Project_10488522,SC2619_JS4_MA_8Healthy_10488522_23C52HLT4,Chromatin Accessibility

The pipeline writes the libraries.csv that cellranger-arc needs and keeps a copy with the results. library_type also accepts shorthands (gex, atac, adt, citeseq, crispr, cmo), and the full 10x vocabulary for the modes still to come.

How --sample is worked out

Cell Ranger needs the fastq prefix, which is not the directory name:

Sample_SC2620_JS4_G3_Reign_10488923_253GGLLT4_L2/
  SC2620_JS4_G3_Reign_10488923_253GGLLT4_S3_L002_R1_001.fastq.gz
  -> --sample=SC2620_JS4_G3_Reign_10488923_253GGLLT4

It is read from the actual filenames, so an unexpected naming variant is caught while reading the sheet rather than inside Cell Ranger. The sample column overrides it.

The flow cell is part of that prefix, so the same library re-sequenced on a different flow cell has a different prefix. Both are collected and passed as --sample=prefix1,prefix2 — passing only one would silently drop a run.

References

--listRefs prints the full map. Keys point at the build rather than the species directory, and the same key picks the right build for the mode.

key gex arc
Apoculata_stonyCoral 240514_fromSarahArnold/apoculata
Canine CanFam3_1 arc/CanFam3_Ensembl101annot
Combo_Human_Mouse refdata-gex-GRCh38_and_GRCm39-2024-A
Combo_Human_Mouse_2020 refdata-gex-GRCh38-and-mm10-2020-A
Feline Fca126
Horse EquCab3/ENSEMBL_annot116/spaceranger/EquCab3_ENS116
Horse_ENS112 EquCab3/ENSEMBL_annot112/spaceranger/EquCab3_ENS112
Human refdata-gex-GRCh38-2024-A refdata-cellranger-arc-GRCh38-2024-A
MAIZE MAIZE_CellRanger
Mouse refdata-gex-GRCm39-2024-A refdata-cellranger-arc-GRCm39-2024-A
Nematostella Nematostella2_2/Nematostella2_2_standard Nematostella2_2/Nematostella2_2
Nematostella_jaNemVect1 jaNemVect1.1/NCBI/jaNemVect1_1

Rooted at /local/workdir/10x_analysis/REFS. --ref also takes a full path.

A reference is a directory holding reference.json. If --ref points at a parent, the run stops and names the builds it found so you can pick one.

10x software

Two engines, singularity by default:

engine source resolves to
singularity a locally built image <sifdir>/cellranger-<crversion>.sif
local a native install /programs/cellranger-<crversion>/cellranger

Images currently built, in --sifdir (/local/workdir/10x_analysis/singularity-sifs):

image select with
cellranger-9.0.1.sif --crversion 9.0.1 (default)
cellranger-10.1.0.sif --crversion 10.1.0
cellranger-arc-2.2.0.sif --arcversion 2.2.0 (default)

--crsif / --arcsif name an image directly. --crpath / --arcpath name a binary and force local for that tool. --listPrograms shows what is installed under --programs.

The image or binary is checked before any work starts, and the error lists what is actually present.

Which version am I really running?

A filename is only a claim. Before counting anything the tool is asked its own version inside the image, and the run stops if it disagrees:

ERROR: asked for cellranger 9.0.1 but cellranger reports 7.0.0
       Set --crversion to match, point at another image with --crsif, or pass --checkversion false.

This applies to --engine local too. The version actually reported is recorded in pipeline_info/software_versions.yml.

Building an image

containers/build-sif.sh builds on the machine that will run it. Nothing is pushed anywhere.

From a 10x tarball:

bash containers/build-sif.sh cellranger 10.1.0 cellranger-10.1.0.tar.gz /local/workdir/10x_analysis/singularity-sifs

An existing image can also be converted rather than rebuilt, if you have one:

bash containers/build-sif.sh cellranger 9.0.1 docker://<registry>/cellranger:9.0.1 /local/workdir/10x_analysis/singularity-sifs

It writes <tool>-<version>.sif, exactly the name the pipeline looks for, then runs --version inside the image so you see what you got. Each version is its own image: the install is baked in and cannot be upgraded in place.

Notes for the build host:

  • Needs root or --fakeroot; set SIF_FAKEROOT=1.
  • Needs outbound network to bootstrap ubuntu:22.04. The finished image does not.
  • Needs several GB of scratch. Set APPTAINER_TMPDIR if /tmp is small.
  • Sites that auto-bind paths need those mount points inside the image. The default /workdir /local /programs is created during the build; adjust with SIF_BINDS="/workdir /local /fs".
  • cellranger keeps its executable at the top of the install dir, cellranger-arc in bin/. Both are put on PATH.

Params

param default notes
--id BRC_ID BRC Project ID
--sheet sample-sheet.csv
--mode gex gex, arc
--ref key from --listRefs, or a full path
--chemistry auto
--expectCells / --forceCells
--createBam false
--introns tool default --include-introns
--r1length 28 --r1-length; pass 0 to omit
--r2length --r2-length
--localcores 32 --localcores and the CPUs reserved
--localmem 128 --localmem in GB and the memory reserved
--maxforks 1 concurrent tasks per process
--aggr true run cellranger aggr for multi-library labels
--normalize mapped aggr depth normalisation; or none
--engine singularity or local
--crversion / --arcversion 9.0.1 / 2.2.0 selects the image or install
--sifdir see above where the .sif files live
--crsif / --arcsif a specific image
--programs /programs where native installs live
--crpath / --arcpath a specific binary; forces --engine local
--checkversion true verify the tool's own version

The command built per library:

cellranger count --id=<label> \
  --localcores=<localcores> --localmem=<localmem> --create-bam=<createBam> --r1-length=<r1length> \
  --transcriptome=<ref> \
  --fastqs=<dir[,dir2]> --sample=<prefix[,prefix2]>

--localcores and --localmem set the Nextflow reservation and the Cell Ranger flags from one value, so the allocation and what Cell Ranger believes it has cannot drift apart. Every process retries a failed task twice before the run stops, and no wall time is imposed.

Development

No Cell Ranger, no containers and no real data needed:

bash test/make_test_data.sh
nextflow run . -stub-run -c test/ci.config --sheet test/sample-sheet.csv --ref test/ref

CI runs the same on every push, and additionally drives the real command against a stand-in binary to check the flags, the pooling of re-sequenced libraries, the generated libraries.csv, engine selection, and every rejection case.

Layout

main.nf                            params, help, reference maps, workflows
modules/cellranger/main.nf         cellranger count
modules/cellrangeraggr/main.nf     cellranger aggr
modules/cellrangerarc/main.nf      cellranger-arc count
modules/versions/main.nf           software_versions.yml
nextflow.config                    engines, resources, retries
containers/build-sif.sh            build a .sif locally
test/                              stub inputs and a stand-in binary

Licence

Pipeline code: MIT. Cell Ranger and the 10x reference packages are covered by the 10x Genomics End User Licence Agreement and are not distributed here.

About

Modular Nextflow pipeline for 10x Genomics single-cell data (GEX, with ATAC/ARC to follow)

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages