Skip to content

fix: count secondary+supplementary records as secondary only (flagstat) - #131

Open
BenjaminDEMAILLE wants to merge 1 commit into
seqeralabs:mainfrom
BenjaminDEMAILLE:fix/flagstat-supplementary-double-count
Open

fix: count secondary+supplementary records as secondary only (flagstat)#131
BenjaminDEMAILLE wants to merge 1 commit into
seqeralabs:mainfrom
BenjaminDEMAILLE:fix/flagstat-supplementary-double-count

Conversation

@BenjaminDEMAILLE

Copy link
Copy Markdown

Fixes #125.

Problem

Records carrying both SECONDARY (0x100) and SUPPLEMENTARY (0x800) were counted in both the secondary and supplementary flagstat totals, because the two bits were tested independently:

if is_secondary { self.secondary += 1; }
if is_supplementary { self.supplementary += 1; }

samtools gives SECONDARY priority — bam_stat.c (flagstat) and stats.c both classify such a record as secondary and never reach the supplementary counter. The double count inflates the supplementary total and breaks the invariant primary + secondary + supplementary == total.

While checking this against samtools, a second instance of the same conflation showed up in the samtools stats SN section: non-primary alignments is nreads_secondary in samtools, but RustQC emitted secondary + supplementary. That line was wrong for any BAM containing supplementary alignments, dual-flagged or not.

Fix

  • accumulators.rs: if is_secondary { .. } else if is_supplementary { .. }
  • stats.rs: non-primary alignments: now reports secondary alone

Verification

Ground truth from samtools 1.24 on a synthetic BAM (4 primary pairs, 2 secondary-only, 3 supplementary-only, 5 dual-flagged):

samtools flagstat            samtools stats
18 + 0 in total              SN  non-primary alignments:  7
 8 + 0 primary               SN  supplementary alignments: 3
 7 + 0 secondary
 3 + 0 supplementary

A second control file with the dual-flagged records removed (2 secondary-only, 3 supplementary-only) gives non-primary alignments: 2, confirming that line tracks secondary alone rather than the sum.

New unit test test_dual_flagged_reads_count_as_secondary_only builds the same record mix and asserts those numbers plus the sum invariant.

  • cargo test — 201 lib + 18 integration tests pass
  • cargo fmt --check, cargo clippy -- -D warnings clean

🤖 Generated with Claude Code

samtools gives the SECONDARY (0x100) flag priority over SUPPLEMENTARY
(0x800): a record carrying both is counted as secondary and never reaches
the supplementary counter (bam_stat.c flagstat_loop and stats.c both return
early on secondary). RustQC tested the two bits independently, so
dual-flagged records were counted twice — inflating the flagstat
supplementary total and breaking the invariant
`primary + secondary + supplementary == total`.

The same conflation affected the samtools stats SN section: "non-primary
alignments" is `nreads_secondary` in samtools, not secondary+supplementary.

Verified against samtools 1.24 on a synthetic BAM with 4 primary pairs,
2 secondary-only, 3 supplementary-only and 5 dual-flagged records:
18 total / 8 primary / 7 secondary / 3 supplementary, non-primary 7.

Closes seqeralabs#125

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

samtools flagstat: reads with both SECONDARY and SUPPLEMENTARY flags are double-counted in the supplementary total

1 participant