ReadAlign_mappedFilter.cpp applies outFilterMismatchNoverLmax to the mapped length of the alignment, while outFilterMismatchNoverReadLmax is the one applied to the read length.
In src/align/read_align.rs both are currently divided by read_seq.len():
let read_length = read_seq.len() as f64;
...
let mismatch_rate = t.n_mismatch as f64 / read_length;
if mismatch_rate > params.out_filter_mismatch_nover_lmax { ... }
Consequence: for a soft-clipped alignment the denominator is too large, so the filter is more permissive than STAR's. A 100 bp read mapping 50 bp with 12 mismatches gives 0.12 here (kept at the 0.3 default) against STAR's 0.24 (also kept), but at 20 mismatches over 50 mapped bases it is 0.20 here and 0.40 in STAR, which STAR rejects and this does not.
The fix is to divide by the summed mapped length of the alignment's exons. It is deliberately not bundled into the PR that adds outFilterMismatchNoverReadLmax, because changing the denominator moves the yeast benchmark and wants its own before/after measurement.
Until then the two parameters share a denominator, so outFilterMismatchNoverLmax and outFilterMismatchNoverReadLmax behave identically.
ReadAlign_mappedFilter.cppappliesoutFilterMismatchNoverLmaxto the mapped length of the alignment, whileoutFilterMismatchNoverReadLmaxis the one applied to the read length.In src/align/read_align.rs both are currently divided by
read_seq.len():Consequence: for a soft-clipped alignment the denominator is too large, so the filter is more permissive than STAR's. A 100 bp read mapping 50 bp with 12 mismatches gives 0.12 here (kept at the 0.3 default) against STAR's 0.24 (also kept), but at 20 mismatches over 50 mapped bases it is 0.20 here and 0.40 in STAR, which STAR rejects and this does not.
The fix is to divide by the summed mapped length of the alignment's exons. It is deliberately not bundled into the PR that adds
outFilterMismatchNoverReadLmax, because changing the denominator moves the yeast benchmark and wants its own before/after measurement.Until then the two parameters share a denominator, so
outFilterMismatchNoverLmaxandoutFilterMismatchNoverReadLmaxbehave identically.