pipeline-rnaseq · diff
git:20260321.d9029d0 to git:20260920.ce7840d
247 added, 144 removed. Audit A to A.
---
name: pipeline-rnaseq
description: "Execute ENCODE RNA-seq pipeline from FASTQ to gene quantification and signal tracks. Child of pipeline-guide. Provides Nextflow execution with Docker and cloud deployment. Use when processing RNA-seq data with STAR alignment, RSEM/Kallisto quantification, or generating expression matrices. Trigger on: RNA-seq pipeline, gene expression, STAR alignment, RSEM quantification, transcript quantification, TPM, FPKM, RNA processing, run RNA-seq."
---
# ENCODE RNA-seq Pipeline
## When to Use
- User wants to run an RNA-seq processing pipeline from FASTQ to gene quantification
- User asks about "RNA-seq pipeline", "STAR alignment", "RSEM", "gene expression quantification", or "Kallisto"
- User needs to process bulk RNA-seq data with ENCODE-standard 2-pass STAR alignment
- Example queries: "process my RNA-seq FASTQs", "quantify gene expression from RNA-seq", "run STAR and RSEM on my data"
Execute the ENCODE RNA-seq processing pipeline from raw FASTQ files through splice-aware
alignment, gene/transcript quantification, and strand-specific signal track generation.
This skill provides a complete Nextflow DSL2 implementation following ENCODE uniform
analysis standards.
## Overview
RNA-seq measures transcriptome-wide gene expression by sequencing cDNA derived from
cellular RNA. The ENCODE pipeline processes RNA-seq data through quality control,
splice-aware alignment with STAR (2-pass mode), gene and transcript quantification
with RSEM, optional fast pseudoalignment with Kallisto, and generation of strand-specific
signal tracks as bigWig files.
Key design decisions: STAR 2-pass mode for maximum splice junction sensitivity, RSEM
for accurate gene/transcript/isoform quantification including multi-mapped reads,
stranded library protocol (dUTP/rf-stranded) as the ENCODE standard, and paired-end
sequencing with a minimum of 30 million uniquely mapped reads per replicate.
## Key Literature
| Reference | Journal | Year | DOI | Relevance |
|-----------|---------|------|-----|-----------|
| Dobin et al. "STAR: ultrafast universal RNA-seq aligner" | Bioinformatics | 2013 | 10.1093/bioinformatics/bts635 | Splice-aware aligner (~12,000 citations) |
| Li & Dewey "RSEM: accurate transcript quantification from RNA-Seq data" | BMC Bioinformatics | 2011 | 10.1186/1471-2105-12-323 | Gene/transcript quantification (~6,000 citations) |
| Bray et al. "Near-optimal probabilistic RNA-seq quantification" | Nature Biotechnology | 2016 | 10.1038/nbt.3519 | Fast pseudoalignment (~4,000 citations) |
| Wang et al. "RSeQC: quality control of RNA-seq experiments" | Bioinformatics | 2012 | 10.1093/bioinformatics/bts356 | RNA-seq QC suite (~3,500 citations) |
| ENCODE Project Consortium "Expanded encyclopaedias" | Nature | 2020 | 10.1038/s41586-020-2493-4 | ENCODE Phase 3 standards |
| Frankish et al. "GENCODE 2021" | Nucleic Acids Research | 2021 | 10.1093/nar/gkaa1087 | Gene annotation reference |
## Pipeline Stages
```
- FASTQ ──> FastQC / Trim Galore ──> STAR (2-pass) ──> Genome BAM + Transcriptome BAM
- | | |
- | ┌─────────────────────────────────────┘ |
- | v v
- | Signal Track Generation RSEM Quantification
- | (strand-specific bigWig) (gene + transcript + isoform)
- | | |
- | v v
- | Plus strand bigWig genes.results (TPM/FPKM)
- | Minus strand bigWig isoforms.results
- | |
- | ┌───────────────────────────────────────────────────────────┘
- | v
- | Kallisto (optional fast pseudoalignment)
- | |
- | v
- └──> RSeQC + MultiQC ──> Aggregated QC Report
+ FASTQ
+ ├─> FastQC (raw reads)
+ └─> Trim Galore (+ FastQC on the trimmed reads)
+ ├─> Kallisto (optional) ──────────> kallisto/<sample>/abundance.tsv
+ └─> STAR (2-pass)
+ ├─> transcriptome BAM ─> RSEM ─> <sample>.genes.results / .isoforms.results
+ ├─> bedGraph str1/str2 ─> bedGraphToBigWig ─> signal/<sample>_{plus,minus}.bw
+ └─> genome BAM ─> RSeQC (infer_experiment, read_distribution,
+ geneBody_coverage, inner_distance)
+
+ MultiQC <── FastQC (raw + trimmed), trimming reports, STAR Log.final.out,
+ RSEM .stat/, RSeQC infer_experiment + read_distribution
+ └─> qc/multiqc/multiqc_report.html
```
+ The Kallisto abundances, the gene body coverage and the inner distance files are
+ published but are not part of the MultiQC report; read those files directly.
+
### Stage Summary
| Stage | Tool | Input | Output | Reference |
|-------|------|-------|--------|-----------|
- | 1. QC & Trimming | FastQC, Trim Galore | Raw FASTQ | Trimmed FASTQ | references/01-qc-trimming.md |
- | 2. Alignment | STAR (2-pass) | Trimmed FASTQ | Genome BAM + Transcriptome BAM | references/02-star-alignment.md |
- | 3. Quantification | RSEM, Kallisto | Transcriptome BAM / FASTQ | Gene/transcript counts, TPM, FPKM | references/03-quantification.md |
+ | 1. QC & Trimming | FastQC, Trim Galore | Raw FASTQ | Trimmed FASTQ + FastQC reports | references/01-qc-trimming.md |
+ | 2. Alignment | STAR (2-pass) | Trimmed FASTQ | Genome BAM + Transcriptome BAM + bedGraph | references/02-star-alignment.md |
+ | 3. Quantification | RSEM, Kallisto | Transcriptome BAM / trimmed FASTQ | Gene/transcript counts, TPM, FPKM | references/03-quantification.md |
| 4. Signal Tracks | bedGraphToBigWig | STAR bedGraph | Strand-specific bigWig | references/04-signal-tracks.md |
- | 5. QC Metrics | RSeQC, MultiQC | BAM, counts | Strandedness, coverage, saturation | references/05-qc-metrics.md |
+ | 5. QC Metrics | RSeQC, MultiQC | Genome BAM, logs | Strandedness, read distribution, gene body coverage | references/05-qc-metrics.md |
## Input Requirements
### Required Files
- - **RNA-seq FASTQ**: Paired-end reads (ENCODE standard; single-end supported)
- - **Reference genome**: STAR-indexed genome with gene annotation (GRCh38 + GENCODE for human)
- - **Gene annotation**: GENCODE GTF (v38+ for human, vM27+ for mouse)
- ### Sample Sheet Format
- ```csv
- sample_id,read1,read2,replicate,strandedness
- SAMPLE1_rep1,rna_R1.fq.gz,rna_R2.fq.gz,1,reverse
- SAMPLE1_rep2,rna_R1.fq.gz,rna_R2.fq.gz,2,reverse
- ```
+ - **RNA-seq FASTQ**: paired-end reads matched by the `--reads` glob (ENCODE standard;
+ single-end with `--single_end`)
+ - **STAR genome index directory** (`--star_index`)
+ - **RSEM reference prefix** (`--rsem_index`) produced by `rsem-prepare-reference`
+ - **BED12 gene model for RSeQC** (`--rseqc_bed`)
+ - **Kallisto index file** (`--kallisto_index`), unless `--skip_kallisto` is set
- **Strandedness**: ENCODE uses dUTP-based stranded libraries. The resulting reads are
- `reverse` stranded (read 2 matches the sense strand). If unknown, the pipeline will
- auto-detect strandedness using RSeQC `infer_experiment.py`.
+ There is no sample sheet: samples are the pairs that `--reads` matches, and the sample
+ ID is the shared prefix of each pair. The gene annotation is not a workflow parameter
+ and there is no `--gtf`; the GTF is consumed when the STAR and RSEM references are built
+ (references/02 and references/03), so the annotation is fixed by the index you pass in.
## Library Strandedness
- | Protocol | Strandedness | RSEM flag | Kallisto flag | Common Usage |
- |----------|-------------|-----------|---------------|-------------|
- | dUTP (ENCODE standard) | Reverse | `--strandedness reverse` | `--rf-stranded` | Most ENCODE RNA-seq |
- | SMARTer / SMART-Seq2 | Unstranded | `--strandedness none` | (default) | Single-cell, low-input |
- | Illumina TruSeq Stranded | Reverse | `--strandedness reverse` | `--rf-stranded` | Standard bulk RNA-seq |
- | Directional ligation | Forward | `--strandedness forward` | `--fr-stranded` | Some legacy protocols |
+ `--strandedness` takes one value for the whole run — `reverse` (default), `forward` or
+ `none` — and is validated before the first task. It drives three things at once: the RSEM
+ `--strandedness` flag, the kallisto strand flag, and which STAR bedGraph becomes which
+ signal track.
+ | Protocol | `--strandedness` | RSEM receives | Kallisto receives | Signal tracks |
+ |----------|------------------|---------------|-------------------|---------------|
+ | dUTP (ENCODE standard), Illumina TruSeq Stranded | `reverse` | `--strandedness reverse` | `--rf-stranded` | `<sample>_plus.bw`, `<sample>_minus.bw` |
+ | Directional ligation (some legacy protocols) | `forward` | `--strandedness forward` | `--fr-stranded` | `<sample>_plus.bw`, `<sample>_minus.bw` |
+ | SMARTer / SMART-Seq2 and other unstranded kits | `none` | `--strandedness none` | no strand flag | `<sample>_unstranded.bw` |
+
+ There is no per-sample strandedness and the workflow does not detect it. RSeQC
+ `infer_experiment.py` runs as a post-hoc check and writes
+ `qc/rseqc/<sample>.infer_experiment.txt`. If the library type is unknown, run a first
+ pass, read that file (references/05 explains the output), and rerun with the correct
+ `--strandedness` — the RSEM counts, the kallisto abundances and the signal tracks all
+ depend on it, so a wrong value has to be corrected by rerunning, not by post-processing.
+
## QC Thresholds
- | Metric | Threshold | Category | Source |
- |--------|-----------|----------|--------|
- | Total sequenced reads | >=30M PE reads | Read depth | ENCODE |
- | Uniquely mapped reads | >=70% of total | Alignment | ENCODE |
- | Multi-mapped reads | <10% | Alignment | ENCODE |
- | rRNA rate | <10% | Sample quality | ENCODE |
- | Strandedness agreement | >90% | Library prep | RSeQC |
- | Exonic rate | >60% | Mapping quality | RSeQC |
- | Gene body coverage | Relatively uniform (5'/3' bias <1.5) | RNA integrity | RSeQC |
- | Duplication rate | <60% | Library complexity | Picard |
- | Detected genes (TPM>1) | >12,000 (human) | Sensitivity | ENCODE |
- | Saturation | Approaching plateau at sequencing depth | Depth sufficiency | RSeQC |
+ | Metric | Threshold | Produced by |
+ |--------|-----------|-------------|
+ | Total sequenced reads | >=30M PE reads | `fastqc/`, `star/<sample>.Log.final.out` |
+ | Uniquely mapped reads | >=70% of input reads | `star/<sample>.Log.final.out` |
+ | Multi-mapped reads | <10% | `star/<sample>.Log.final.out` |
+ | Strandedness agreement | >90% for a stranded library | `qc/rseqc/<sample>.infer_experiment.txt` |
+ | Exonic rate | >60% | `qc/rseqc/<sample>.read_distribution.txt` |
+ | Gene body coverage | Relatively uniform (5'/3' bias <1.5) | `qc/rseqc/<sample>.geneBody_coverage.geneBodyCoverage.txt` |
+ Not computed by this workflow: rRNA rate, library duplication rate (beyond the
+ sequence-level estimate inside the FastQC report), detected-gene counts, and saturation
+ curves. references/05-qc-metrics.md gives the commands to run those by hand on the
+ published BAM and RSEM output.
+
### Read Depth Guidelines
| Application | Minimum Reads (PE) | Recommended | Notes |
|-------------|-------------------|-------------|-------|
| Gene-level expression | 20M | 30M | ENCODE minimum |
| Transcript-level expression | 40M | 60M | Isoform resolution requires more depth |
| Differential expression | 20M per sample | 30M per sample | 3+ biological replicates per condition |
| Novel junction discovery | 60M | 100M+ | STAR 2-pass mode benefits from depth |
- | Fusion detection | 50M | 80M+ | Chimeric reads are rare |
+ | Fusion detection | 50M | 80M+ | Chimeric reads are rare; needs a separate STAR run (references/02) |
## Execution
- ### Quick Start (Local Docker)
+ The versions the workflow runs are the ones in `scripts/Dockerfile`: STAR 2.7.10b,
+ RSEM 1.3.3, kallisto 0.48.0, samtools 1.17, RSeQC 4.0.0, Trim Galore 0.6.7, cutadapt 4.4,
+ MultiQC 1.14, and FastQC unpinned from the Ubuntu 22.04 package. The conda environment in
+ `bioinformatics-installer` (`environments/rnaseq-env.yml`) is a separate manual route and
+ may ship different point releases of the same tools.
+
+ Every index flag is shown in the examples below because the defaults are bare names
+ resolved in the launch directory (`GRCh38_star_index`, `GRCh38_rsem_index/GRCh38`,
+ `gencode.v38.kallisto.idx`, `hg38_RefSeq.bed` for `--genome GRCh38`). The run stops
+ before the first task if any of them is missing. `--kallisto_index` is the one exception:
+ it is not read when `--skip_kallisto` is set.
+
+ ### Quick Start (local, Docker)
+
```bash
- nextflow run scripts/main.nf \
- -profile local \
- --reads 'fastq/*_R{1,2}.fq.gz' \
- --genome GRCh38 \
- --outdir results/
+ nextflow run scripts/main.nf -profile local \
+ --reads 'fastq/*_R{1,2}.fq.gz' \
+ --genome GRCh38 \
+ --star_index /ref/GRCh38_star_index \
+ --rsem_index /ref/GRCh38_rsem_index/GRCh38 \
+ --kallisto_index /ref/gencode.v38.kallisto.idx \
+ --rseqc_bed /ref/hg38_RefSeq.bed \
+ --strandedness reverse \
+ --outdir results/
```
### SLURM HPC
- ```bash
- nextflow run scripts/main.nf \
- -profile slurm \
- --reads 'fastq/*_R{1,2}.fq.gz' \
- --genome GRCh38 \
- --outdir results/
- ```
- ### Google Cloud
```bash
- nextflow run scripts/main.nf \
- -profile gcp \
- --reads 'gs://bucket/fastq/*_R{1,2}.fq.gz' \
- --genome GRCh38 \
- --outdir 'gs://bucket/results/'
+ nextflow run scripts/main.nf -profile slurm \
+ --container /path/to/pipeline-rnaseq.sif \
+ --slurm_queue normal \
+ --reads 'fastq/*_R{1,2}.fq.gz' \
+ --genome GRCh38 \
+ --star_index /ref/GRCh38_star_index \
+ --rsem_index /ref/GRCh38_rsem_index/GRCh38 \
+ --kallisto_index /ref/gencode.v38.kallisto.idx \
+ --rseqc_bed /ref/hg38_RefSeq.bed \
+ --outdir results/
```
- ### AWS Batch
+ ### Cloud
+
```bash
- nextflow run scripts/main.nf \
- -profile aws \
- --reads 's3://bucket/fastq/*_R{1,2}.fq.gz' \
- --genome GRCh38 \
- --outdir 's3://bucket/results/'
+ # Google Cloud Batch
+ nextflow run scripts/main.nf -profile gcp \
+ --container us-docker.pkg.dev/<project>/<repo>/pipeline-rnaseq:1.0.0 \
+ --gcp_project <project> \
+ --gcp_workdir gs://<bucket>/work \
+ --reads 'gs://<bucket>/fastq/*_R{1,2}.fq.gz' \
+ --genome GRCh38 \
+ --star_index gs://<bucket>/ref/GRCh38_star_index \
+ --rsem_index gs://<bucket>/ref/GRCh38_rsem_index/GRCh38 \
+ --kallisto_index gs://<bucket>/ref/gencode.v38.kallisto.idx \
+ --rseqc_bed gs://<bucket>/ref/hg38_RefSeq.bed \
+ --outdir gs://<bucket>/results
+
+ # AWS Batch
+ nextflow run scripts/main.nf -profile aws \
+ --container <account>.dkr.ecr.<region>.amazonaws.com/pipeline-rnaseq:1.0.0 \
+ --aws_queue <job-queue> \
+ --aws_workdir s3://<bucket>/work \
+ --reads 's3://<bucket>/fastq/*_R{1,2}.fq.gz' \
+ --genome GRCh38 \
+ --star_index s3://<bucket>/ref/GRCh38_star_index \
+ --rsem_index s3://<bucket>/ref/GRCh38_rsem_index/GRCh38 \
+ --kallisto_index s3://<bucket>/ref/gencode.v38.kallisto.idx \
+ --rseqc_bed s3://<bucket>/ref/hg38_RefSeq.bed \
+ --outdir s3://<bucket>/results
```
+ `--outdir` only sets where results are published; Google Batch and AWS Batch stage every
+ task through the work directory, and the workflow stops with an error if it or the
+ project/queue is missing.
+
+ ## Pipeline Parameters
+
+ | Parameter | Default | Description |
+ |-----------|---------|-------------|
+ | `--reads` | required | Glob matching the FASTQ pairs, e.g. `'fastq/*_R{1,2}.fq.gz'`. Quote it |
+ | `--genome` | `GRCh38` | `GRCh38` or `mm10`; selects the default index names and nothing else |
+ | `--outdir` | `./results` | Directory results are published to |
+ | `--single_end` | `false` | Treat `--reads` as single files; kallisto then runs with the fixed `--single -l 200 -s 20`, and RSeQC `inner_distance.py` is skipped |
+ | `--strandedness` | `reverse` | `reverse`, `forward` or `none`; one value for the whole run |
+ | `--skip_kallisto` | `false` | Skip `KALLISTO_QUANT`; `--kallisto_index` is then not read |
+ | `--star_index` | `GRCh38_star_index` (`mm10_star_index`) | STAR genome directory |
+ | `--rsem_index` | `GRCh38_rsem_index/GRCh38` (`mm10_rsem_index/mm10`) | RSEM reference **prefix**, not a directory; every file starting with it is staged |
+ | `--kallisto_index` | `gencode.v38.kallisto.idx` (`gencode.vM27.kallisto.idx`) | kallisto index file |
+ | `--rseqc_bed` | `hg38_RefSeq.bed` (`mm10_RefSeq.bed`) | BED12 gene model used by all four RSeQC modules |
+ | `--chrom_sizes` | `<star_index>/chrNameLength.txt` | Chromosome sizes for `bedGraphToBigWig` |
+
+ ### Infrastructure parameters (`nextflow.config`)
+
+ | Parameter | Default | Description |
+ |-----------|---------|-------------|
+ | `--container` | `encode-toolkit/pipeline-rnaseq:1.0.0` | Image built from `scripts/Dockerfile`. Pass a registry image for `gcp`/`aws`, or a `.sif` file for `slurm` |
+ | `--max_cpus`, `--max_memory`, `--max_time` | `16`, `64.GB`, `24.h` | Upper bounds applied to every process |
+ | `--slurm_queue`, `--slurm_account` | `normal`, none | SLURM partition and account |
+ | `--gcp_project`, `--gcp_workdir` | none (both required for `-profile gcp`) | Google Cloud project and `gs://` work directory |
+ | `--gcp_location`, `--gcp_disk` | `us-central1`, `200.GB` | Google Batch region and per-task disk |
+ | `--aws_queue`, `--aws_workdir` | none (both required for `-profile aws`) | AWS Batch job queue and `s3://` work directory |
+ | `--aws_region`, `--aws_cli_path` | `us-east-1`, `/home/ec2-user/miniconda/bin/aws` | AWS region, and the AWS CLI path inside the Batch AMI |
+
## Cloud Cost Estimates
| Platform | Instance | Cost/Sample | Time/Sample | Notes |
|----------|----------|-------------|-------------|-------|
- | GCP | n1-highmem-8 | ~$3-6 | 2-4 hours | STAR index loading dominates; preemptible recommended |
+ | GCP | n1-highmem-8 | ~$3-6 | 2-4 hours | STAR index loading dominates; the `gcp` profile uses spot VMs |
| AWS | r5.2xlarge | ~$3-6 | 2-4 hours | r-series for STAR memory; spot recommended |
- | Local | 8 cores, 32GB | $0 | 3-6 hours | Docker required; STAR needs 32GB+ RAM |
- | SLURM | 8 cores, 32GB | Varies | 2-4 hours | Singularity recommended |
+ | Local | 8 cores, 36 GB | $0 | 3-6 hours | Docker required |
+ | SLURM | 8 cores, 36 GB | Varies | 2-4 hours | Singularity; pass the `.sif` with `--container` |
- **Memory note**: STAR genome index loading requires ~32GB RAM for human. Machines with
- <32GB will fail at the alignment step. Use `--limitGenomeGenerateRAM` to cap index size
- for constrained environments, but this reduces mapping sensitivity.
+ **Memory note**: `nextflow.config` asks for 36 GB for `STAR_ALIGN` (doubling on each
+ retry, capped by `--max_memory`). The local executor refuses the task on a machine with
+ less, so a 32 GB host is not enough. If 36 GB is out of reach, rebuild the STAR index
+ with a larger `--genomeSAsparseD` (which shrinks the loaded index at some cost in
+ mapping speed) or move to a bigger machine. `--limitGenomeGenerateRAM` is a
+ `genomeGenerate` option and is not a parameter of this workflow.
## Output Directory Structure
```
results/
- fastqc/ # Raw and trimmed QC reports
- trimmed/ # Trimmed FASTQ files
+ fastqc/ # FastQC HTML/zip for raw and trimmed reads
+ trimmed/
+ <sample>_R1_val_1.fq.gz, <sample>_R2_val_2.fq.gz
+ <sample>_R1.fq.gz_trimming_report.txt # one per input file
star/
- genome_bam/ # Genome-aligned BAM files
- transcriptome_bam/ # Transcriptome BAM files (for RSEM)
- logs/ # STAR alignment logs (Log.final.out)
- junctions/ # Splice junction files (SJ.out.tab)
+ <sample>.Aligned.sortedByCoord.out.bam
+ <sample>.Aligned.sortedByCoord.out.bam.bai
+ <sample>.Aligned.toTranscriptome.out.bam # RSEM input
+ <sample>.Log.final.out
+ <sample>.SJ.out.tab
+ <sample>.ReadsPerGene.out.tab # STAR gene counts
+ <sample>.Signal.UniqueMultiple.str1.out.bg # plus str2 unless --strandedness none
rsem/
- genes/ # genes.results (gene_id, TPM, FPKM, expected_count)
- isoforms/ # isoforms.results (transcript_id, TPM, FPKM)
- kallisto/ # Kallisto quantification output (optional)
- abundance.tsv # transcript TPMs and counts
+ <sample>.genes.results # gene_id, TPM, FPKM, expected_count
+ <sample>.isoforms.results # transcript_id, TPM, FPKM, IsoPct
+ <sample>.stat/ # RSEM model statistics, read by MultiQC
+ kallisto/ # absent with --skip_kallisto
+ <sample>/abundance.tsv
+ <sample>/run_info.json
signal/
- plus/ # Plus-strand bigWig signal tracks
- minus/ # Minus-strand bigWig signal tracks
+ <sample>_plus.bw, <sample>_minus.bw # or <sample>_unstranded.bw
qc/
- rseqc/ # RSeQC output (strandedness, coverage, distribution)
- multiqc/ # Aggregated QC report
- logs/ # Nextflow execution logs
+ rseqc/
+ <sample>.infer_experiment.txt
+ <sample>.read_distribution.txt
+ <sample>.geneBody_coverage.*
+ <sample>.inner_distance.* # paired-end only
+ multiqc/
+ multiqc_report.html
+ multiqc_data/
+ pipeline_info/
+ timeline.html, report.html, trace.txt # Nextflow execution reports
```
## Common Pitfalls
### 1. Insufficient Memory for STAR
- STAR loads the entire genome index into shared memory (~32GB for human). This is the most
- common failure mode. Always allocate at least 32GB RAM for the alignment step. On shared
- HPC systems, check memory limits per job.
+ STAR loads the whole genome index into memory. `STAR_ALIGN` is configured for 36 GB, so a
+ 32 GB machine will not schedule the task at all under `-profile local`. On shared HPC
+ systems, check the per-job memory limit before submitting.
### 2. Wrong Strandedness Setting
Using incorrect strandedness results in near-zero gene counts. If you see uniformly low
- counts, check strandedness with `infer_experiment.py` from RSeQC. ENCODE dUTP libraries
- are `reverse` stranded.
+ counts, read `qc/rseqc/<sample>.infer_experiment.txt` and rerun with the matching
+ `--strandedness`. ENCODE dUTP libraries are `reverse` stranded.
### 3. Using FPKM for Cross-Sample Comparison
FPKM values are not comparable across samples because they depend on total library
composition. Use TPM (comparable across samples) or raw counts with DESeq2/edgeR
normalization for differential expression.
### 4. Ignoring Multi-Mapped Reads
RSEM uses an expectation-maximization algorithm to probabilistically assign multi-mapped
reads. This is critical for gene families and repetitive elements. Do not pre-filter
multi-mappers before RSEM quantification.
- ### 5. Skipping rRNA Assessment
- High rRNA contamination (>10%) indicates failed rRNA depletion. This reduces effective
- sequencing depth and inflates apparent duplication rates. Always check rRNA rate before
- downstream analysis.
+ ### 5. Assuming rRNA Contamination Was Checked
+ High rRNA contamination (>10%) indicates failed rRNA depletion and reduces effective
+ sequencing depth. This workflow does not measure it — run the manual check in
+ references/05-qc-metrics.md against `star/<sample>.Aligned.sortedByCoord.out.bam` before
+ trusting the quantifications.
### 6. Not Using 2-Pass Mode for Novel Junctions
STAR 1-pass mode only uses annotated splice junctions. 2-pass mode first discovers novel
junctions then re-maps, critical for non-model organisms or samples with extensive
- alternative splicing.
+ alternative splicing. This workflow always runs `--twopassMode Basic`.
## Pipeline Scripts
- | File | Description | Lines |
- |------|-------------|-------|
- | `scripts/main.nf` | Nextflow DSL2 pipeline | ~130 |
- | `scripts/nextflow.config` | Execution profiles (local/slurm/gcp/aws) | ~60 |
- | `scripts/Dockerfile` | Docker build with STAR, RSEM, Kallisto, RSeQC | ~35 |
+ | File | Description |
+ |------|-------------|
+ | `scripts/main.nf` | Nextflow DSL2 pipeline |
+ | `scripts/nextflow.config` | Execution profiles (local/slurm/gcp/aws) |
+ | `scripts/Dockerfile` | Docker build with STAR, RSEM, Kallisto, RSeQC |
## ENCODE Data Integration
After running on your own data, compare with ENCODE reference:
```python
# Find matching ENCODE RNA-seq experiments
encode_search_experiments(
assay_title="total RNA-seq",
organ="pancreas",
biosample_type="tissue"
)
# Download ENCODE gene quantifications for comparison
encode_batch_download(
download_dir="/data/encode_reference/",
output_type="gene quantifications",
assay_title="total RNA-seq",
organ="pancreas",
assembly="GRCh38"
)
# Download ENCODE signal tracks for browser visualization
encode_search_files(
file_format="bigWig",
assay_title="total RNA-seq",
organ="pancreas",
output_type="signal of unique reads"
)
```
## Pitfalls & Edge Cases
- - **Strandedness must match library prep**: STAR requires correct `--outSAMstrandField` and RSEM needs `--strandedness`. Using wrong strand settings can halve gene counts or assign reads to antisense genes.
- - **rRNA contamination**: rRNA >10% wastes sequencing depth. Ribosomal depletion libraries should have <5%. Poly-A selection libraries should have <1%. Check with FastQC or Picard CollectRnaSeqMetrics.
- - **STAR 2-pass mode is required**: The first pass discovers novel splice junctions; the second pass uses them. Single-pass STAR misses tissue-specific or rare splicing events, reducing sensitivity for differential exon usage.
+ - **Strandedness must match library prep**: `--strandedness` feeds RSEM, kallisto and the
+ signal tracks. Using the wrong value can halve gene counts, assign reads to antisense
+ genes, and swap the plus/minus bigWigs.
+ - **rRNA contamination**: rRNA >10% wastes sequencing depth. Ribosomal depletion libraries
+ should have <5%, poly-A selection libraries <1%. The workflow does not measure it; see
+ references/05-qc-metrics.md for a manual count, or run Picard CollectRnaSeqMetrics
+ outside the container.
+ - **STAR 2-pass mode is required**: The first pass discovers novel splice junctions; the
+ second pass uses them. Single-pass STAR misses tissue-specific or rare splicing events,
+ reducing sensitivity for differential exon usage.
- **Gene-level vs transcript-level quantification**: RSEM provides transcript-level estimates but gene-level aggregation is more robust for differential expression. Transcript-level analysis requires many more replicates (≥6).
- **TPM normalization is not for cross-sample comparison**: TPM normalizes within a sample but is NOT appropriate for comparing expression across conditions. Use DESeq2 size factors or TMM normalization for differential expression.
- **Batch effects in multi-lab data**: RNA-seq is highly sensitive to library prep method, sequencer, and lab. Always check for batch effects with PCA before combining datasets from different sources.
## Walkthrough: Processing ENCODE RNA-seq from FASTQ to Gene Quantification
**Goal**: Process raw RNA-seq FASTQ files through the ENCODE pipeline to generate gene expression quantifications (TPM/FPKM) and signal tracks.
**Context**: The ENCODE RNA-seq pipeline uses STAR 2-pass alignment and RSEM quantification, producing both gene-level and transcript-level expression estimates.
### Step 1: Find RNA-seq experiment
```
encode_get_experiment(accession="ENCSR000CPR")
```
Expected output:
```json
{
"accession": "ENCSR000CPR",
"assay_title": "RNA-seq",
"biosample_summary": "K562",
"replicates": 2,
"status": "released"
}
```
### Step 2: List FASTQ files
```
- encode_list_files(accession="ENCSR000CPR", file_format="fastq")
+ encode_list_files(experiment_accession="ENCSR000CPR", file_format="fastq")
```
Expected output:
```json
{
"files": [
{"accession": "ENCFF200RN1", "output_type": "reads", "paired_end": "1", "biological_replicates": [1], "file_size_mb": 3200},
{"accession": "ENCFF201RN2", "output_type": "reads", "paired_end": "2", "biological_replicates": [1], "file_size_mb": 3300}
]
}
```
### Step 3: Run the RNA-seq pipeline
+ Name the downloaded FASTQs so that one glob matches both mates of each sample, then:
+
```bash
- nextflow run pipeline-rnaseq/main.nf \
- --fastq_r1 ENCFF200RN1.fastq.gz \
- --fastq_r2 ENCFF201RN2.fastq.gz \
- --genome GRCh38 \
- --annotation gencode.v36.annotation.gtf \
- --strandedness reverse \
- -profile docker
+ nextflow run scripts/main.nf -profile local \
+ --reads 'fastq/*_R{1,2}.fq.gz' \
+ --genome GRCh38 \
+ --star_index /ref/GRCh38_star_index \
+ --rsem_index /ref/GRCh38_rsem_index/GRCh38 \
+ --kallisto_index /ref/gencode.v38.kallisto.idx \
+ --rseqc_bed /ref/hg38_RefSeq.bed \
+ --strandedness reverse \
+ --outdir results/
```
Key pipeline steps:
- 1. Quality trimming (fastp)
- 2. STAR 2-pass alignment (splice-aware)
- 3. RSEM gene quantification (TPM, FPKM, expected counts)
- 4. Kallisto transcript quantification (optional)
- 5. Signal track generation (bigWig)
- 6. QC metrics (RSeQC, MultiQC)
+ 1. FastQC on the raw reads, quality and adapter trimming with Trim Galore (which also runs FastQC on the trimmed reads)
+ 2. STAR 2-pass alignment (splice-aware), writing the genome BAM, the transcriptome BAM, gene counts and bedGraphs
+ 3. RSEM gene and isoform quantification (TPM, FPKM, expected counts)
+ 4. Kallisto transcript quantification (optional, skipped with `--skip_kallisto`)
+ 5. Signal track generation (bedGraph to bigWig)
+ 6. RSeQC (infer_experiment, read_distribution, geneBody_coverage, inner_distance) and MultiQC
### Step 4: Validate output quality
- | Metric | Threshold | Purpose |
+ | Metric | Threshold | Where to read it |
|---|---|---|
- | Mapping rate | >= 70% | Alignment success |
- | rRNA rate | < 10% | rRNA depletion efficiency |
- | Replicate correlation | >= 0.9 | Biological consistency |
- | Exonic rate | > 60% | Expected for mRNA-seq |
+ | Uniquely mapped rate | >= 70% | `star/<sample>.Log.final.out` |
+ | Strandedness agreement | > 90% and matching `--strandedness` | `qc/rseqc/<sample>.infer_experiment.txt` |
+ | Exonic rate | > 60% | `qc/rseqc/<sample>.read_distribution.txt` |
+ | Replicate correlation | >= 0.9 | compute yourself from the TPM column of `rsem/<sample>.genes.results` |
### Step 5: Use expression data with ENCODE epigenomic data
Compare gene expression with enhancer marks:
```
encode_search_experiments(assay_title="Histone ChIP-seq", biosample_term_name="K562", target="H3K27ac", organism="Homo sapiens")
```
**Interpretation**: Genes with high TPM AND nearby H3K27ac peaks have validated enhancer-gene connections. Low expression despite nearby enhancer marks suggests poised or tissue-specific regulation.
### Integration with downstream skills
- Gene quantifications feed into -> **peak-annotation** for expression-validated peak targets
- Expression data connects to -> **gtex-expression** for tissue comparison
- Processed data feeds into -> **compare-biosamples** for differential expression analysis
- Pipeline provenance logged by -> **data-provenance**
## Code Examples
### 1. Find RNA-seq experiments for a tissue
```
encode_search_experiments(assay_title="total RNA-seq", organ="liver", organism="Homo sapiens")
```
Expected output:
```json
{
"total": 35,
"results": [
{"accession": "ENCSR300RNA", "assay_title": "RNA-seq", "biosample_summary": "liver", "status": "released"}
]
}
```
### 2. Check for existing gene quantifications
```
- encode_list_files(accession="ENCSR300RNA", file_format="tsv", output_type="gene quantifications", assembly="GRCh38")
+ encode_list_files(experiment_accession="ENCSR300RNA", file_format="tsv", output_type="gene quantifications", assembly="GRCh38")
```
Expected output:
```json
{
"files": [
{"accession": "ENCFF400GEQ", "output_type": "gene quantifications", "file_format": "tsv", "file_size_mb": 5.2}
]
}
```
### 3. Download expression data
```
- encode_download_files(accessions=["ENCFF400GEQ"], download_dir="/data/rnaseq/quantification")
+ encode_download_files(file_accessions=["ENCFF400GEQ"], download_dir="/data/rnaseq/quantification")
```
Expected output:
```json
{
"downloaded": 1,
"md5_verified": true,
"files": ["/data/rnaseq/quantification/ENCFF400GEQ.tsv"]
}
```
## Integration
| This skill produces... | Feed into... | Purpose |
|---|---|---|
| Gene expression (TPM/FPKM) | **peak-annotation** | Validate enhancer targets with expression data |
| Expression matrix | **gtex-expression** | Compare cell-line vs. tissue expression |
| Differential expression results | **compare-biosamples** | Identify tissue-specific gene regulation |
| Signal tracks (bigWig) | **visualization-workflow** | Display expression signal in genome browser |
| Expression quantifications | **disease-research** | Connect gene expression to disease phenotypes |
| Pipeline run parameters | **data-provenance** | Record STAR/RSEM versions and settings |
| QC metrics | **quality-assessment** | Validate against ENCODE RNA-seq standards |
## Related Skills
- **pipeline-guide** (parent): General pipeline selection and resource assessment
- **quality-assessment**: Deep-dive QC analysis beyond basic metrics
- **integrative-analysis**: Combine RNA-seq with ChIP-seq/ATAC-seq for regulatory inference
- **compare-biosamples**: Compare expression profiles across cell types
- **single-cell-encode**: For scRNA-seq data processing (different pipeline)
- **pipeline-chipseq**: Sibling pipeline for ChIP-seq data
- **pipeline-atacseq**: Sibling pipeline for ATAC-seq data
- **publication-trust**: Verify literature claims backing analytical decisions
## Presenting Results
When reporting RNA-seq pipeline results:
- - **Mapping rate**: Report STAR uniquely mapped rate (>70% expected), multi-mapped rate (<10%), and unmapped rate from Log.final.out
- - **Gene/transcript counts**: Report number of detected genes (TPM>1, expect >12,000 for human) and detected transcripts
- - **rRNA rate**: Report rRNA contamination percentage (<10% expected). Flag if rRNA depletion appears to have failed
- - **Quantification paths**: Provide paths to RSEM genes.results (TPM, FPKM, expected_count) and Kallisto abundance.tsv files
- - **Strandedness**: Confirm the detected strandedness from RSeQC infer_experiment.py matches the expected library protocol
- - **Key QC metrics**: Present exonic rate (>60%), gene body coverage uniformity, duplication rate, and saturation curve status in a summary table
- - **Signal tracks**: Provide paths to strand-specific bigWig files (plus/ and minus/)
+ - **Mapping rate**: Report the STAR uniquely mapped rate (>70% expected), multi-mapped rate (<10%), and unmapped rate from `star/<sample>.Log.final.out`
+ - **Quantification paths**: Provide paths to `rsem/<sample>.genes.results` (TPM, FPKM, expected_count), `rsem/<sample>.isoforms.results`, and `kallisto/<sample>/abundance.tsv` when Kallisto ran
+ - **Strandedness**: Confirm the orientation reported in `qc/rseqc/<sample>.infer_experiment.txt` matches the `--strandedness` value the run used; if it does not, the run has to be repeated
+ - **Key QC metrics**: Present the exonic rate from `read_distribution.txt` and the gene body coverage uniformity from `geneBody_coverage.geneBodyCoverage.txt` in a summary table, alongside the MultiQC report at `qc/multiqc/multiqc_report.html`
+ - **Derived metrics**: Detected-gene counts (TPM>1), rRNA rate, library duplication rate and saturation are not produced by this workflow. Compute them separately if they are needed, and say so when reporting
+ - **Signal tracks**: Provide paths to `signal/<sample>_plus.bw` and `signal/<sample>_minus.bw` (or `signal/<sample>_unstranded.bw` for an unstranded run)
- **Next steps**: Suggest `integrative-analysis` to combine RNA-seq with ChIP-seq/ATAC-seq for regulatory inference, or `compare-biosamples` for cross-tissue expression comparison
## For the request: "$ARGUMENTS"