Repository navigation
fix(bam): unmapped sweep returns only records with no reference - #177
Draft
NicoDeVeaux wants to merge 2 commits into
Draft
NicoDeVeaux wants to merge 2 commits into
NicoDeVeaux wants to merge 2 commits into
Conversation
On the indexed (parallel) path, `fetch(Unmapped)` seeks to the last linear-index window of the last reference and keeps every UNMAP-flagged record, so a placed unmapped mate in that window is read by both its reference's fetch and the sweep. htslib's HTS_IDX_NOCOOR returns only records with no reference (tid -1). - Unit tests: BAM (noodles-written) and two samtools-written CRAMs (per-reference slices, one multi-reference slice); the sweep must return exactly the two unplaced records. - Integration test: flagstat at --threads 1 and 4 must match samtools 1.22.1; at 4 the placed mate is counted twice (in total 7, not 6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`fetch(Unmapped)` now keeps a record only when it has no reference sequence ID, matching htslib's HTS_IDX_NOCOOR: - BAM: the sweep seeks to the last reference's final linear-index window, so it re-read placed records there and kept every UNMAP-flagged one; a placed unmapped mate in that window was counted twice on the parallel path (flagstat "in total" 7 instead of 6 in the new test). - CRAM: `query_unmapped` starts at the slice holding the unplaced reads, which in a multi-reference slice also holds placed records; the same filter applies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(bam): unmapped sweep returns only records with no reference
For PR #118 (noodles migration). Base: #118's head
0b00cf1(perf(bam): stream CRAM reads via self-referential cursors), the newest at the time of writing.What
IndexedReader::fetch(Unmapped)now returns exactly the records with no reference sequence ID, which is whatmain's htslib path returns (FetchDefinition::Unmapped=HTS_IDX_NOCOOR):BamCursor::Unmapped): keep a record only whenreference_sequence_id()isNone, instead of whenever it carries the UNMAP flag.query_unmapped): the same filter on the returned records.Why
On the parallel path (
--threads> 1 with an index, the path nf-core/rnaseq uses), each reference is read withfetch(tid)and then the unplaced reads withfetch(Unmapped).index.last_first_record_start_position(), the first record overlapping the last reference's final 16 kb linear-index window, and keeps every UNMAP-flagged record from there on. A placed unmapped mate (flag 0x4 with a tid, as aligners write for pairs with one mate unmapped) in that window was already returned by its reference's fetch, so it is counted twice. Aligners that place unmapped mates (STAR/RSEM and HISAT2 BAMs in rnaseq) hit this whenever such a mate falls in the last contig's final window.query_unmappedstarts at the slice the index lists for unplaced reads. In a multi-reference slice that slice also holds placed records, and the placed unmapped mate is again returned twice.Minimal input (BAM, chr1/chr2 of 100 kb; 6 records):
in totalprimaryread2mappedmainbea5571,-t 4-t 1-t 4-t 4How tested
src/rna/bam/io.rs:bam_unmapped_sweep_returns_only_unplaced_records(noodles-written BAM of the records above; the sweep must return exactlyu1, u2, and the per-reference fetches plus the sweep must return each record once) andcram_unmapped_sweep_returns_only_unplaced_records(two samtools 1.22.1 CRAMs of the same records undertests/data/unmapped_sweep/, one with per-reference slices and one with a single multi-reference slice; see the README there). On 0b00cf1 both fail with the sweep returningr3, u1, u2.test_parallel_unmapped_sweep_counts_placed_unmapped_mate_once: flagstat ofrustqc rnaat--threads 1and4must equal samtools 1.22.1's; on 0b00cf1-t 4gives7 + 0 in total.cargo test --release --locked --offline: 206 unit, 12 bin, 19 integration, 2 doc tests pass.cargo fmt --checkpasses.cargo clippy -- -D warningsreports the same threeneedless_range_looperrors as unmodifiedmainwith the local toolchain (clippy 0.1.90); with that lint allowed,--all-targetsis clean.pr118_confirm.py, baselinemainbea5571): on 0b00cf1, W1 and W4 count one record twice at-t 4(4 -> 5, 6 -> 7) as predicted; on this branch every world matchesmainand-t 1(W1 4, W2 5, W3 5, W4 6). The script itself now exits 1, because it asserts the PR's predicted double count.Not changed, and why
fetch(tid)also returns the other references' records, because noodles'queryfilters on the interval only. With the multi-reference fixture at-t 4this branch prints10 + 0 in total(0b00cf1: 11,main: 6). Thecram_unmapped_sweeptest checks only the sweep for that file for this reason. A follow-up branch,fix/pr118-cram-multiref-fetch, stacked on this one, fixes it.🤖 Generated with Claude Code