Workflow Overview¶
This page provides a high-level view of the aa-tRNA-seq pipeline architecture and data flow.
Pipeline Architecture¶
The pipeline is organized into modular Snakemake rule files:
flowchart TB
subgraph rules[Rule Files]
REF[aatrnaseq-reference.smk<br/>Reference validation/build]
A[aatrnaseq-process.smk<br/>Core processing]
B[aatrnaseq-charging.smk<br/>Charging analysis]
C[aatrnaseq-qc.smk<br/>Quality control]
D[aatrnaseq-modifications.smk<br/>Modification calling]
OR[aatrnaseq-odds-ratios.smk<br/>Odds ratio analysis]
R[aatrnaseq-report.smk<br/>QC report]
CL[clean.smk<br/>On-demand cleanup]
E[demux.smk<br/>WDX + LDX/FDX + EDX<br/><i>conditional</i>]
end
subgraph common[common.smk]
F[Sample parsing]
G[Helper functions]
H[Output definitions]
end
I[Snakefile<br/>Main entry] --> rules
I --> common
Complete Pipeline Flow¶
Standard Pipeline (No Demultiplexing)¶
flowchart TB
subgraph Input
A[POD5 files<br/>per sample]
end
subgraph Processing[aatrnaseq-process.smk]
B[stage_pod5<br/>Symlink raw POD5s]
C[rebasecall<br/>Dorado basecalling]
E[bwa_align<br/>Align, dorado tags carried through]
CM[calmd<br/>MD/NM tags for the ref]
F[classify_charging<br/>escpod classify]
G2[add_adapter_tags<br/>PT tags]
G3[finalize_bam<br/>Hardlink final BAM]
end
subgraph Charging[aatrnaseq-charging.smk]
H[get_cca_trna<br/>Extract probabilities]
I[get_cca_trna_cpm<br/>Calculate CPM]
end
subgraph QC[aatrnaseq-qc.smk]
J[base_calling_error<br/>Error metrics]
K[align_stats<br/>Read statistics]
L[read_attrition<br/>Where reads were lost]
end
subgraph Mods[aatrnaseq-modifications.smk]
M[bam_to_coverage<br/>Coverage tracks]
N[modkit_pileup<br/>Modification consensus]
O[modkit_extract_calls<br/>Per-read mods]
P[modkit_extract_full<br/>Full export]
end
subgraph OddsRatios[aatrnaseq-odds-ratios.smk]
Q[compute_odds_ratios<br/>Pairwise OR]
end
subgraph Report[aatrnaseq-report.smk]
R[render_combined_qc_report<br/>QC report]
end
A --> B --> C --> E --> CM --> F --> G2 --> G3
G3 --> H --> I
G3 --> J
G3 --> K
G3 --> L
G3 --> M
G3 --> N
G3 --> O
G3 --> P
O --> Q
H --> Q
J --> R
H --> R
I --> R
K --> R
With Demultiplexing (WarpDemuX + EDX)¶
flowchart TB
subgraph Input
A[Pooled POD5 files<br/>per run]
end
subgraph WDX[WDX Demultiplexing]
B[warpdemux<br/>Barcode prediction]
C[parse_warpdemux<br/>Create mapping]
D[extract_sample_reads<br/>Per-sample IDs]
E[split_pod5<br/>Split by WDX barcode]
end
subgraph Standard[Standard Pipeline]
F[rebasecall]
end
subgraph EDX[EDX Early Splitting]
G[detect_edx_adapters<br/>3' adapter ID per read]
H[extract_edx_read_ids<br/>the read list bwa_align aligns]
end
subgraph Downstream[Downstream Processing]
K[bwa_align → classify_charging → ...]
end
A --> B --> C --> D --> E --> F --> G --> H
H --> K
With Demultiplexing (escapepod LDX/FDX)¶
The escapepod backend writes no split POD5: a per-read .p5s sidecar records
each read's barcode call, the run is basecalled once as a whole, and the
per-sample uBAM is cut from that afterwards — rejoining the standard pipeline
at bwa_align.
flowchart TB
subgraph Input
A[Raw POD5 files<br/>per run]
end
subgraph LDX[LDX/FDX Demultiplexing]
B[escapepod_demux<br/>--annotate, LDX axis, writes .p5s]
C[escapepod_demux_fdx<br/>FDX axis, dual-index samples only]
D[ldx_run_read_ids<br/>select_demux_reads.py joins axes]
E[rebasecall_ldx_run<br/>whole run, one dorado pass]
F[ldx_split_parent_map<br/>dorado split-read parents]
G[extract_ldx_sample_reads<br/>per-sample read IDs]
H[split_ldx_ubam<br/>per-sample uBAM]
end
subgraph Downstream[Downstream Processing]
I[bwa_align → calmd → classify_charging → ...]
end
A --> B
A -.-> C
B --> D
C -.-> D
D --> E --> F --> G --> H --> I
B -.-> G
C -.-> G
Rule Categories¶
Processing Rules¶
Core data processing from raw signal to classified reads:
| Rule | Purpose | GPU |
|---|---|---|
stage_pod5 |
Symlink a sample's raw POD5 files into one directory | No |
download_mod_models |
Pre-download dorado modification models (local rule) | No |
rebasecall |
Basecall with Dorado | Yes |
bwa_idx |
Build BWA index | No |
bwa_align |
Align reads to reference, carrying dorado's tags through | No |
calmd |
Recompute MD/NM tags against the reference |
No |
classify_charging |
ML charging classification (escpod classify) |
Opt-in (charging.gpu) |
add_adapter_tags |
Add PT tags for adapter positions | No |
finalize_bam |
Hardlink adapter-tagged BAM as the final BAM | No |
Charging Analysis Rules¶
Extract and summarize charging classification:
| Rule | Purpose |
|---|---|
get_cca_trna |
Extract per-read charging scores |
get_cca_trna_cpm |
Calculate CPM-normalized counts |
Quality Control Rules¶
Generate QC metrics and statistics:
| Rule | Purpose |
|---|---|
compute_reference_similarity |
Pairwise reference sequence similarity matrix |
base_calling_error |
Per-position error frequencies |
bcerror_sites |
Select sites for base-calling error reporting |
mismatch_calls |
Per-read mismatch calls at selected sites |
align_stats |
Read counts through pipeline |
anchor_coverage |
How many aligned reads span the CCA anchor |
read_attrition |
Where the run's reads were lost, as one table |
Modification Rules¶
RNA modification calling with Modkit:
| Rule | Purpose |
|---|---|
bam_to_coverage |
Generate coverage tracks |
modkit_pileup |
Per-site modification consensus |
modkit_extract_calls |
Per-read modification calls |
modkit_extract_full |
Comprehensive modification export |
Odds Ratio Rules¶
Per-tRNA pairwise modification odds ratios:
| Rule | Purpose |
|---|---|
compute_odds_ratios |
Pairwise modification odds ratios per tRNA |
filter_odds_ratios |
Filter odds-ratio table to significant/well-supported pairs |
Report Rules¶
QC report generation:
| Rule | Purpose |
|---|---|
render_combined_qc_report |
Combined Quarto QC report with per-sample tabs |
Reference Rules¶
Validate, build, or trim the alignment reference (aatrnaseq-reference.smk):
| Rule | Purpose |
|---|---|
validate_reference |
Check an existing reference's adapter structure (reference.mode: validate) |
build_reference |
Build an adapted reference from raw tRNA sequences (reference.mode: build) |
skip_reference_validation |
Copy the reference through unchecked (reference.mode: skip) |
trim_reference |
Strip adapters back off, for tools that want tRNA-only coordinates |
Demultiplexing Rules¶
Loaded from demux.smk when either backend is enabled (warpdemux.enabled or
ldx.enabled); a ruleorder picks the active backend's version of the outputs
they share. See Demultiplexing for the full flow.
WarpDemuX (WDX), signal-based:
| Rule | Purpose |
|---|---|
warpdemux |
Run WDX barcode prediction |
parse_warpdemux |
Parse predictions to mapping |
extract_sample_reads |
Filter reads by WDX barcode |
split_pod5 |
Create per-sample WDX POD5s |
escapepod (LDX/FDX), basecall-based:
| Rule | Purpose |
|---|---|
escapepod_demux |
LDX axis: escpod demux --annotate, writes a .p5s sidecar per POD5 |
escapepod_demux_fdx |
FDX axis (5' index), dual-index samples only |
ldx_run_read_ids |
Reads any run sample was assigned, across axes (bounds dorado's -l) |
rebasecall_ldx_run |
Basecall the whole run in one dorado pass |
ldx_split_parent_map |
Map dorado split-read children back to their parent read |
extract_ldx_sample_reads |
Per-sample read IDs where every named axis agrees |
split_ldx_ubam |
Cut one sample's uBAM out of the run-level basecall |
EDX (3' adapter), either backend:
| Rule | Purpose |
|---|---|
detect_edx_adapters |
Detect 3' adapter identity per read |
extract_edx_read_ids |
Extract matching read IDs for EDX (bwa_align aligns only these) |
edx_concordance |
WDX/LDX vs EDX concordance table |
Maintenance Rules¶
| Rule | Purpose |
|---|---|
clean |
On-demand deletion of regenerable intermediates for a completed run, by tier (see config/README.md) |
Key Processing Steps¶
1. POD5 Staging¶
stage_pod5 lays a directory of symlinks over a sample's raw POD5 files —
nothing is copied. Both consumers of a sample's signal (dorado, escpod
classify) take a directory and read it recursively, so a sample pooled from
several runs, or a run that keeps pod5_pass/pod5_fail apart, is covered
without merging:
| Text Only | |
|---|---|
1 2 3 | |
On the LDX path there is no per-sample staging step at all: rebasecall_ldx_run
basecalls the raw run directly, and the per-sample uBAM is cut out afterwards.
2. Basecalling¶
Dorado re-basecalls with:
- Move tables (
--emit-moves) required by the charging model - Modification calling (
--modified-bases pseU m5C inosine_m6A) - High-accuracy model (rna004_sup@v6.0.0)
3. Alignment¶
samtools fastq -T '*' streams the uBAM into bwa mem -C so dorado's tags
(move table, MM/ML modbase calls, RG) ride the FASTQ comment onto the aligned
records — no FASTQ file is written. BWA MEM runs with RNA-optimized parameters:
-x ont2dpreset for ONT reads-K 100000000pins the input batch regardless of thread count, since the tag-bearing comment is ~13x the read-F 2324: unmapped, reverse-strand, secondary and supplementary records removed, leaving primary forward alignments only
4. MD/NM Recomputation¶
calmd recomputes MD/NM against the reference (bwa mem does not emit
MD on its own). This runs as its own pass right after alignment.
5. Charging Classification¶
escpod classify analyzes signal at the CCA 3' end:
- Input: POD5 (signal) + aligned BAM (with move tables) + reference + model bundle
- Output: the same BAM records with a
cltag (0-255 score) added - Threshold: ≥200 = charged (the bundle's own recommended operating point)
Reads with no cl tag are no-calls, not uncharged
The model abstains on reads whose common arm did not align, emitting no tag
rather than a default class. Abstention is charging-correlated, so a
charging fraction over called reads alone is an underestimate — the
per-read reasons are in {sample}.charging_calls.tsv.gz and the run-level
rate in read_attrition.tsv.gz.
Charging Classification Details
A few notes about charging classification for charged vs. uncharged tRNA reads:
-
This step retains only full-length tRNA reads (with an allowance for signal loss at the 5' end of nanopore direct RNA sequencing)
-
The current approach does not rely on differences in adapter sequences attached to charged vs. uncharged tRNA molecules (though these sequences are retained as separate entries in the alignment reference). The pipeline relies exclusively on signal data over a 6-nucleotide modification kmer spanning the universal CCA 3' end of tRNA and the first three nucleotides of the 3' adapter (CCAGGC) to distinguish charged and uncharged reads.
6. Adapter Position Tagging¶
The add_adapter_tags rule adds PT tags with adapter boundaries:
- Uses parasail Smith-Waterman alignment
- Detects 5' and 3' adapter positions
- Can infer 5' adapter from alignment position when truncated
Resource Requirements¶
GPU Rules¶
These rules always request a GPU; classify_charging optionally does (see
GPU Configuration):
| Rule | Typical Runtime | Memory |
|---|---|---|
rebasecall |
30-60 min/sample | 24 GB |
rebasecall_ldx_run |
Hours, whole flowcell | 24 GB |
escapepod_demux / escapepod_demux_fdx |
Run-dependent | See cluster/slurm/config.yaml |
classify_charging |
Opt-in via charging.gpu (windowed/TCN bundle only) |
24 GB |
CPU-Intensive Rules¶
| Rule | Threads | Memory |
|---|---|---|
bwa_align |
16 | 160 GB (see cluster/slurm/config.yaml) |
classify_charging |
4 | 24 GB |
modkit_extract_full |
12 | 48 GB |
Memory-Intensive Rules¶
| Rule | Memory |
|---|---|
modkit_extract_calls |
96 GB |
warpdemux |
32 GB |
Configuration Points¶
Key parameters that affect pipeline behavior:
| Parameter | Affects |
|---|---|
opts.dorado |
Basecalling modifications |
opts.bwa |
Alignment sensitivity |
opts.bam_filter |
Full-length read filtering |
modkit.mod_thresholds |
Modification calling stringency |
warpdemux.barcode_kit |
WDX demultiplexing model |
ldx.model / fdx.model |
escapepod LDX/FDX demultiplexing bundle |
charging.gpu |
Score the windowed (TCN) charging bundle on GPU instead of CPU |
Next Steps¶
- Rules Reference - Detailed rule documentation
- Scripts Reference - Python scripts documentation
- Demultiplexing - WarpDemuX setup guide