Felix Krueger
01/27/2026, 3:29 PMraw reads to trimmed_reads to non_rRNA_reads to aligned_reads to deduplicated_reads , as a QC metric for the experiment/library/CRO.
Could we instruct UMI-tools to only consider primary alignments, or filter to primary alignments before (samtools view -F 0x900)? Alternatively, should we limit STAR to output only a single alignment, such as with: --outFilterMultimapNmax 1?
Any opinions? @Jonathan ManningJonathan Manning
01/27/2026, 4:43 PMsamtools view -F 0x900) - stats would then reflect actual read counts, though you'd lose multi-mapping info in the deduplicated BAM
2. ***Limit STAR output*** (--outFilterMultimapNmax 1) - cleaner, but discards multi-mappers entirely which might not be ideal if you want Salmon to use them for quantification
I'd probably lean toward option 1 for the QC metric flow you're describing, but I haven't tested either approach with this specific issue. Worth experimenting?Thomas Danhorn
01/27/2026, 5:51 PMsamtools view -F 4 "$bam" | cut -f 1 | sort -u | wc -l. With the sorting this is of course highly inefficient, so ideally there are filters that eliminate the sorting step, like using "primary alignments", but that depends on whether the aligner uses that flag correctly, and you still have to sort out how to get from aligned mates to a number of read pairs (some pairs may only have one mate aligned and the other unmapped). The dirty way is -f 64 to only count the "first in pair", but that is inaccurate of only the "second in pair" (its mate) is mapped.
I would not throw out multimappers at the BAM creation (--outFilterMultimapNmax 1), it makes wrong pretenses about the accuracy of the alignment. STAR does by default create a few more multimappers than required, by counting alignments that differ by only one mismatch as the same (e.g. a read with one perfect alignment and one or more other alignments with one mismatch would be counted as multimapped). This can be switched off with --outFilterMultimapScoreRange 0, but since it only affects a few reads, it does nothing to keep the statistics straight.Felix Krueger
01/27/2026, 11:12 PMFelix Krueger
01/27/2026, 11:16 PM--primary_only for the UMI-deduplication (possible set to ON by default) - that would output only a single, deduplicated primary hit by default, which would be what one would expect. And revert to the current handling by setting this flag to false?Thomas Danhorn
01/28/2026, 1:18 AMHow is this situation handled in say the RNA-seq workflow with paired-end reads?You'd have to look at the #CE8SSJV3N source code for details, but with RNAseq there are fewer steps to lose reads. I think for the most part it is sufficient to look at the trimming losses, the STAR log (STAR has to handle every single read pair anyway, so it can keep track of where it goes without extra cost) and then at (sum of the) the reads counted (by Salmon or RSEM).
Maybe we could add a flagIt would be nice to see in the BAM all the locations a certain read maps to. And if you make it optional, it won't help with the stats, because you can't rely it. Since STAR sets (AFAIK) the "primary alignment" flag correctly, using that for counting should be fine. (I don't have enough experience with UMIs to say if there is any other log output, etc., that might help.)for the UMI-deduplication (possible set to ON by default) ...--primary_only
Felix Krueger
02/03/2026, 10:14 AMIt would be nice to see in the BAM all the locations a certain read maps to.If we went with filtering for primary alignments only before the deduplication, one could still go back to the original BAM file to look at all different mapping locations if one wanted to - so that would be fine?
And if you make it optional, it won’t help with the stats, because you can’t rely it.I would suggest that the default is to set this to
true by default so the stats check out. Switching it off would then have to come with a warning that says that including secondary alignments in the UMI-deduplication step would lead to stats that are somewhat disconnected to the number of input reads (“proceed at your own risk”).Thomas Danhorn
02/04/2026, 3:52 AMFelix Krueger
02/04/2026, 2:17 PMThomas Danhorn
02/04/2026, 11:10 PMsamtools view -c (with reasonable filters) to do the counting, independently of UMI tools. This is not nearly as efficient as taking values from a log file, but if the log doesn't show what we want, it's at least correct. There are no other outputs from UMI tools from which the correct values can be derived, right?Felix Krueger
02/05/2026, 3:38 PM# UMI-tools version: 1.1.5
# output generated by dedup -I BulkD83_S8.genome.sorted.bam -S BulkD83_S8.umi_dedup.genome.sorted.bam -L BulkD83_S8.umi_dedup.genome.sorted.log --output-
stats BulkD83_S8.umi_dedup.genome.sorted --method=directional --random-seed=100
# job started at Thu Jan 22 11:20:19 2026 on <http://ip-10-3-1-113.lab.altoslabs.com|ip-10-3-1-113.lab.altoslabs.com> -- 8be2ef46-f571-4fec-a4d8-b5e157b0049c
# pid: 132, system: Linux 6.1.141-155.222.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Jun 17 10:29:47 UTC 2025 x86_64
# assigned_tag : None
# cell_tag : None
# cell_tag_delim : None
# cell_tag_split : -
# chimeric_pairs : use
# chrom : None
# compresslevel : 6
# detection_method : None
# filter_umi : None
# gene_tag : None
# gene_transcript_map : None
# get_umi_method : read_id
# ignore_tlen : False
# ignore_umi : False
# in_sam : False
# log2stderr : False
# loglevel : 1
# mapping_quality : 0
# method : directional
# no_sort_output : False
# out_sam : False
# output_unmapped : False
# paired : False
# per_cell : False
# per_contig : False
# per_gene : False
# random_seed : 100
# read_length : False
# short_help : None
# skip_regex : ^(__|Unassigned)
# soft_clip_threshold : 4
# spliced : False
# stats : BulkD83_S8.umi_dedup.genome.sorted
# stderr : <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>
# stdin : <_io.TextIOWrapper name='BulkD83_S8.genome.sorted.bam' mode='r' encoding='UTF-8'>
# stdlog : <_io.TextIOWrapper name='BulkD83_S8.umi_dedup.genome.sorted.log' mode='a' encoding='UTF-8'>
# stdout : <_io.TextIOWrapper name='BulkD83_S8.umi_dedup.genome.sorted.bam' mode='w' encoding='UTF-8'>
# subset : None
# threshold : 1
# timeit_file : None
# timeit_header : None
# timeit_name : all
# tmpdir : None
# umi_sep : _
# umi_tag : RX
# umi_tag_delim : None
# umi_tag_split : None
# umi_whitelist : None
# umi_whitelist_paired : None
# unmapped_reads : discard
# unpaired_reads : use
# whole_contig : False
2026-01-22 11:20:19,121 INFO command: dedup -I BulkD83_S8.genome.sorted.bam -S BulkD83_S8.umi_dedup.genome.sorted.bam -L BulkD83_S8.umi_dedup.genome.sort
ed.log --output-stats BulkD83_S8.umi_dedup.genome.sorted --method=directional --random-seed=100
2026-01-22 11:22:04,051 INFO total_umis 73103229
2026-01-22 11:22:04,051 INFO #umis 26262
2026-01-22 11:22:07,764 INFO Written out 100000 reads
2026-01-22 11:22:21,083 INFO Parsed 1000000 input reads
...
2026-01-22 12:03:29,107 INFO Reads: Input Reads: 73103229
2026-01-22 12:03:29,107 INFO Number of reads out: 12133067
2026-01-22 12:03:29,107 INFO Total number of positions deduplicated: 3940867
2026-01-22 12:03:29,107 INFO Mean number of unique UMIs per position: 4.49
2026-01-22 12:03:29,107 INFO Max. number of unique UMIs per position: 10063
# job finished in 2590 seconds at Thu Jan 22 12:03:29 2026 -- 2562.59 33.76 0.00 0.00 -- 8be2ef46-f571-4fec-a4d8-b5e157b0049c
It doesn’t look like there are other, better stats in there we could use…
Here are the numbers from samtools, filtering out secondary alignments:
samtools view -c MAT-LIBR-19610_BulkD83_S8.genome.sorted.bam
73,103,229
samtools view -F 256 -c MAT-LIBR-19610_BulkD83_S8.genome.sorted.bam
26,062,970
samtools view -c BulkD83_S8.umi_dedup.genome.sorted.bam
12,133,067
samtools view -F 256 -c BulkD83_S8.umi_dedup.genome.sorted.bam
7,884,975
To get a feel for the UMI-aware de-duplication in the sample, and put in relation to the other numbers in the run, i.e. raw reads > trimmed/length excluded reads > reads after rRNA removal > reads that can be aligned to genome > UMI-deduplicated alignments it seems we’ve got to calculate 7,884,975 / 26,062,970 *100 = 30.2 % are left over sequences. Or in other words, the duplication rate of the sample was ~70%.
If the values reported are 12,133,067 / 73,103,229 *100 = 16.6 % left over sequences, the implied duplication rate is not only ~84%, but also the numbers do not relate to any of the other steps mentioned above.
In my mind, the numbers need to be sensible throughout the report to allow for a quality assessment of the library, so we should either:
• 1) filter out secondary alignments before running UMI-tools (that would fix it outright), or
• 2) run UMI-tools as is, but choose not to feed its output log to the MultiQC process, and then run samtools view -F 256 -c on both the original sorted and UMI-deduplicated result files, and regenerate a UMI-tools like report section (as custom content?)
To me, option 1 makes a lot more sense. Since UMI-tools requires a physical BAM file (and no pipes), we would need to add a step like this:
# Filter to primary (and optionally non-supplementary) alignments
samtools view -F 256 -F 2048 -b input.bam -o primary_only.bam
and use this as the input for UMI-tools. We can then choose whether to publish or not the primary-only BAM file. If this would be the default option, but can be skipped e.g. via --skip_primary_alignment filtering we should have all scenarios covered, or am I mistaken?Sebastian Uhrig
02/09/2026, 8:40 PMsamtools collate, we can avoid the sorting for better performance.
The only question to me that remains is how to represent this in the MultiQC plot? Shall we omit the UMI counts and only report the manual counts? Or shall we keep the UMI counts and report both (potentially causing even more confusion)?Felix Krueger
02/21/2026, 9:34 AM--umi_dedup_primary_only), and then use that (temporary) file for the UMI-module of MultiQC. While somewhat wasteful, this would probably super easy to implement as we could still make use of the UMI module of MultiQC, avoid having to remove and replace it by a manual kludge, and make all parties happy?Jonathan Manning
02/21/2026, 7:52 PMJonathan Manning
02/23/2026, 12:53 PMJonathan Manning
02/24/2026, 2:37 PMFelix Krueger
02/25/2026, 9:50 AMJonathan Manning
02/25/2026, 9:52 AMJonathan Manning
02/25/2026, 10:01 AMFelix Krueger
02/25/2026, 10:17 AM--umitools_dedup_primary_only true so that the deduplication stats are connected in the QC flow from: raw reads > trimmed > non-rRNA > aligned > deduplicated ; the deduplicated BAM file contains de-duplicated primary alignments only?
If someone has an interested in the looking at all multimapping de-duplicated alignments and the associated disconnected (and confusing) filtering stats, they can set a switch to --umitools_dedup_primary_only false , which would be the current behaviour?
As far as I am concerned, I am completely happy with thisJonathan Manning
02/25/2026, 10:20 AMJack Tierney
02/25/2026, 10:51 AMIra Iosub
02/25/2026, 11:29 AMJonathan Manning
02/25/2026, 11:48 AMIra Iosub
02/26/2026, 11:30 AMJonathan Manning
02/26/2026, 11:32 AM