Repository navigation
NH over all loci, 0x200 from the Illumina filter flag, --outFilterMismatchNoverReadLmax - #375
Open
BenjaminDEMAILLE wants to merge 1 commit into
Open
BenjaminDEMAILLE wants to merge 1 commit into
BenjaminDEMAILLE wants to merge 1 commit into
Conversation
…matchNoverReadLmax - NH counts every locus (STAR nTr), not the --outSAMmultNmax records written, single-end and paired-end. - A FASTQ name with n:Y: sets 0x200 on every record of the read, mapped or not, both mates, in SAM/BAM and the transcriptome BAM. - --outFilterMismatchNoverReadLmax is accepted and caps the per-read mismatches as STAR's outFilterMismatchNmaxTotal does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 3, 2026
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.
Part of #354 (NH under
--outSAMmultNmax, SAM flag 0x200,--outFilterMismatchNoverReadLmax).Three record and parameter differences from STAR, each small. Lowercase bases in SEQ, the fourth item of the same audit, is #376, separate because it touches every place SEQ is written.
Problem
NHwas the number of records written, not the number of loci. A single-end read with 3 loci under--outSAMmultNmax 2hadNH:i:2; STAR writesNH:i:3. Paired-end had the same bug.@r1 1:Y:0:ACGT: 512 when mapped, 516 when unmapped.--outFilterMismatchNoverReadLmaxwas refused by the CLI (tests/parameter_surface.rslisted it as not yet accepted). STAR 2.7.11b accepts it.What STAR does (2.7.11b source)
NH:
ReadAlign_outputAlignments.cpp:168:nTrOutWrite=min(P.outSAMmultNmax,nTrOutSAM)is only the number of records written.ReadAlign_outputAlignments.cpp:177,184: each record getsnTrOutSAM, all loci, asnTrOut, which is written asNH(ReadAlign_outputTranscriptSAM.cpp:281,ReadAlign_alignBAM.cpp:293). Same code for single-end and paired-end.0x200:
ReadAlignChunk_processChunks.cpp:121-127:readFilterisYwhen the second word of the read-name line has:Y:at bytes 1 to 3. Only mate 1's name line is read; both mates get the same value (:133-140).readLoad.cpp:27reads it back.ReadAlign_outputTranscriptSAM.cpp:27-28(unmapped) and:91(mapped),ReadAlign_alignBAM.cpp:146,198:0x200whenreadFilter=='Y'. The transcriptome BAM goes throughalignBAMtoo.ReadAlign_outputTranscriptSAM.cpp:219,ReadAlign_alignBAM.cpp:543:--outSAMflagAND/ORare applied after 0x200, on mapped records only.--outFilterMismatchNoverReadLmax:parametersDefault:473: default1.0.Parameters.cpp:152: a double.ReadAlign_oneRead.cpp:78:outFilterMismatchNmaxTotal=min(outFilterMismatchNmax, (uint)(outFilterMismatchNoverReadLmax*(readLength[0]+readLength[1]))), per read, after clipping.outFilterMismatchNmaxeverywhere the aligner checks mismatches: stitching (stitchAlignToTranscript.cpp:310,314,374,391), extension (ReadAlign_stitchWindowSeeds.cpp:58,83,146,165,stitchWindowAligns.cpp:45,66), the mapped filter (ReadAlign_mappedFilter.cpp:12) and the transcriptome projection (ReadAlign_quantTranscriptome.cpp:57).ReadAlign_waspMap.cpp:120(copyRead): the merged read of--peOverlapNbasesMinkeeps the pair's total.Change
src/io/sam.rs:transcript_to_recordgetsn_alignments(all loci) instead ofmax_output(records written), inwrite_alignment,build_alignment_recordsandbuild_paired_records.src/io/fastq.rs:EncodedRead.filter_flag, parsed from the FASTQ description byfilter_flag().src/lib.rs:apply_qc_failsets 0x200 on the read's genomic and transcriptome records, from mate 1 for pairs, and applies--outSAMflagAND/ORagain on mapped records so the order is STAR's. It runs next to the per-file read-group pass, in the single-end, paired-end and both STARsolo paths.src/params/mod.rs: the new parameter andParameters::mismatch_nmax_total(read_len).src/align/read_align.rssets the per-read scorer'sn_mm_maxfrom it (stitching and extension already readn_mm_max) and uses it in both mapped filters; the merged-mates read gets the pair's total.src/quant/transcriptome.rsused the formula with the ratio fixed at 1; it now calls the same function.Overlap with #364. #364 (
fix/unmapped-mate-names) adds the samefilter_flag()function andEncodedRead.filter_flagfield, for theUnmapped.out.mate*names. This PR copies them as they are there, including the unit test, so whichever merges second has a conflict insrc/io/fastq.rsand thesrc/solo/mod.rstest fixtures that resolves by keeping one copy. This PR does not touch theUnmapped.out.mate*names.Measured against STAR 2.7.11b
Synthetic genome (3 x 20 kb, one 400 bp segment on each chromosome), macOS, each tool with its own index.
Records of the same reads,
--outSAMmultNmax 2 --outSAMunmapped Within:NH:i:3NH:i:2NH:i:3NH:i:3NH:i:2NH:i:31:Y:mapped1:Y:unmapped1:Y:mapped1:Y:unmappedThe same in BAM (
samtools view): identical to STAR after, apart from the IUPAC letter, which is #376.Aligned.toTranscriptome.out.bamof the mapped1:Y:read: FLAG 512 after (rustar only; STAR's quant mode does not run on this macOS build).--outFilterMismatchNoverReadLmax, 4 000 single-end reads and 4 000 pairs with 0 to 9 random mismatches per mate. Before, rustar refused the option (exit 2). After:EndToEndEndToEndThe
EndToEndgaps are not from this option. In both tools, ratio 0.04 gives exactly the same records as--outFilterMismatchNmax 4(SE) and--outFilterMismatchNmax 8(PE), and at ratio 1.0EndToEndPE already differs (6 810 vs 7 202 records). That is a separate, existing difference under--alignEndsType EndToEndfor pairs.Tests
tests/alignment_features.rs::test_nh_qc_fail_and_mismatch_over_read_length_match_star: maps single-end and paired-end reads on a genome with a repeat planted three times and checks FLAG and NH of every record against STAR 2.7.11b's output on the same input (I ran STAR on it). Without the fix it fails: first because the CLI refuses the option, and with only the NH and 0x200 parts reverted, onNH:i:2and the missing 0x200.src/io/sam.rs::test_out_sam_mult_nmaxasserted the old NH (3 of 5 loci); it now expects 5.src/io/fastq.rs::filter_flag_needs_y_as_the_second_colon_field_of_a_one_byte_first_field(the same as in Unmapped.out.mate* names and sequences as STAR writes them #364).tests/parameter_surface.rs: the option leavesNOT_YET_ACCEPTED.cargo test --release,cargo clippy --all-targets -- -D warningsandcargo fmt --checkare clean on macOS.Not checked
--outSAMflagAND/ORtogether with a1:Y:read: the code path is there, not compared with STAR.NH:i:1for its mapped mate whatever the number of loci. That is a separate path (build_half_mapped_records), not changed here.Unmapped.out.mate*names (0:Y:) are Unmapped.out.mate* names and sequences as STAR writes them #364's.parity_suite.sh); the cases above are synthetic.🤖 Generated with Claude Code