chimeric: --chimMultimapNmax enumeration, and --chimFilter banGenomicN - #149
Open
BenjaminDEMAILLE wants to merge 2 commits into
Open
chimeric: --chimMultimapNmax enumeration, and --chimFilter banGenomicN#149BenjaminDEMAILLE wants to merge 2 commits into
BenjaminDEMAILLE wants to merge 2 commits into
Conversation
Adds `--chimFilter`, `--chimMultimapNmax`, `--chimMultimapScoreRange` and `--chimNonchimScoreDropMin`. `--chimFilter` is implemented. STAR treats it as a post-detection filter rather than a condition inside the search, and so does this: every detection route — the intra-mate path and the inter-mate path — funnels through `apply_chim_filter`, so a route cannot quietly skip it. `banGenomicN` (STAR's default) drops a junction whose flanking genomic bases are not real bases: sequence around an assembly gap yields junctions that look clean by score and mean nothing. `None` keeps everything. One detail worth stating because it changes what the code does: the ban tests the base *value*, as STAR does (`G[pos] == 4`), not whether the position is in bounds. Treating an unanswerable position as a ban would reject alignments STAR keeps, and it broke an existing test whose fixture deliberately uses out-of-range coordinates. The three multimap knobs are declared and validated but not yet acted on: `--chimMultimapNmax 0` is STAR's default and means the existing single-best detection, which is what this branch still does. Reporting several chimeras per read is `chimericDetectionMult`, a separate search with its own output columns, and it deserves its own change rather than being smuggled in behind a flag declaration. Declaring them now keeps the parameter surface honest and gives that work somewhere to land. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
STAR's `ChimericDetection::chimericDetectionMult`. The old path pins the best transcript as one segment and searches for a partner, so it can only ever emit one chimera; a read whose fusion partner maps equally well to two places has one of them silently discarded. This walks every transcript pair instead and reports all that survive. Two behaviours the loop depends on, both STAR's: The score floor ratchets. It starts at `--chimScoreMin`, rises above the best linear alignment score, again to `readLength - chimScoreDropMax`, and then every new best chimera raises it to `best - chimMultimapScoreRange`. Pairs admitted early can therefore be dropped later, which is why the final `retain` is not redundant with the in-loop test. More survivors than `--chimMultimapNmax` means no output at all, not a truncated list. The read is chimerically multimapping beyond the cap and STAR reports nothing rather than an arbitrary subset. `--chimNonchimScoreDropMin` gates the whole search: a read that already aligns linearly across its length is not a fusion candidate, however well its halves score apart. All three knobs were parsed but inert before this; they are real now, and the default `--chimMultimapNmax 0` keeps the old path unchanged. The tail shared by both paths — order the segments by read position, check the junction overhang and the geometry, classify the motif, apply the non-GTAG penalty, re-check the score — moves into `finalize_chimera`, so the two routes cannot drift apart. It returns the post-penalty score alongside the alignment because the ratchet needs that number, not `ChimericAlignment::total_score`, which is the plain sum of the segment scores and knows nothing about the read overlap or the penalty. Tests cover the discriminator (two equal loci: old path 1, multimap 2), the cap reporting nothing, the score range admitting and excluding the weaker locus, the nonchim gate, and the knob being inert at its default.
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.
Chimeric detection:
--chimMultimapNmaxenumeration, and--chimFilter banGenomicN.What changed
--chimMultimapNmaximplements STAR'sChimericDetection::chimericDetectionMult. The existing path pins the best transcript as one segment and searches for a partner, so it can emit at most one chimera per read. The new path walks every transcript pair, keeps those within--chimMultimapScoreRangeof the best chimeric score, and reports them all.Two STAR behaviours the loop depends on:
--chimScoreMin, rises above the best linear alignment score, again toreadLength - chimScoreDropMax, then tobest - chimMultimapScoreRangeeach time a better chimera appears. Pairs admitted early can be dropped later, so the finalretainis not redundant with the in-loop test.--chimMultimapNmaxand STAR emits no chimera rather than an arbitrary subset.--chimNonchimScoreDropMingates the search: a read that already aligns linearly across its length is not a fusion candidate however well its halves score apart.The tail shared by both paths — segment ordering, junction overhang, geometry, motif classification, non-GTAG penalty, score re-check — moved into
finalize_chimera, so the two routes cannot drift apart.--chimFilteris a post-detection filter, as in STAR: both detection routes funnel throughapply_chim_filter, so neither can skip it.banGenomicN(the default) drops a junction whose flanking genomic bases are not real bases, since sequence around an assembly gap produces junctions that look clean by score and mean nothing.Nonedisables filtering.The ban tests the base value, as STAR does (
G[pos] == 4), not whether the position is in bounds. Treating an unanswerable position as a ban would reject alignments STAR keeps.Why
Three of the four flags were parsed but inert.
--chimMultimapNmax 0remains the default and selects the single-best path, so behaviour without the flag is unchanged.Verification
New tests:
chim_multimap_reports_every_locus_within_the_score_range— a read whose 3' end maps equally well to two places: the old path reports 1 junction,--chimMultimapNmax 10reports 2chim_multimap_beyond_the_cap_reports_nothingchim_multimap_score_range_drops_the_weaker_locus— and admits it again when the range widenschim_multimap_requires_the_linear_alignment_to_leave_the_read_unexplainedchim_multimap_is_off_by_defaultchim_filter_bans_a_genomic_n_at_the_junction_and_none_keeps_itGate green: 566 lib + integration tests, clippy
-D warnings,cargo fmt --check, MSRV 1.89.Output-neutral on non-chimeric runs. On chimeric runs at default flags, output is unchanged:
banGenomicNcan only remove a junction, and multimap detection is off.🤖 Generated with Claude Code