Skip to content

v0.14.0

Choose a tag to compare

@github-actions github-actions released this 11 Sep 08:56
· 10 commits to master since this release

Added

--update-mods — keep base modification tags in step with trimming (#69).

MM encodes each modified base as a count of canonical bases to skip, so trimming or splitting a read silently invalidates it. The tags then describe a sequence the read no longer has, and htslib rejects them outright (MM tag refers to bases beyond sequence length), losing the modification data.

samtools fastq -T MM,ML,MN reads.bam \
  | chopper --trim-approach split-by-low-quality --cutoff 15 -l 500 --update-mods \
  > trimmed.fastq

The flag recomputes MM, ML and MN for whatever part of the read is kept, and works with all four trimming approaches. Calls outside the kept range are dropped along with their ML probabilities; a segment that keeps no calls gets an empty list, so "called here, nothing found" is preserved rather than becoming "never called". It is off by default and costs nothing when unset.

Two things to know:

  • Only MM, ML and MN are corrected. Other tags invalidated by trimming (qs, ns, ts, du) are passed through unchanged and will be stale.
  • Tags that cannot be rewritten are a fatal error, not a warning — malformed tags, MM and ML disagreeing on the number of calls, or tags describing a different sequence than the read carries. Writing them through would attach modification coordinates to a sequence they no longer describe. Rerun without --update-mods for the old pass-through behaviour.

Fixed

Split reads could share a read name. The _segment_N suffix was appended to the end of the header line rather than to the read name, so with tab-separated SAM tags it landed on the last tag. samtools import then parsed qs:f:20.1_segment_1 back as qs:f:20.1 and dropped the suffix, giving every segment of a read the same name. Affected --trim-approach split-by-low-quality on tag-carrying FASTQ.

Long reads could be silently truncated. Output used Write::write and discarded its result. Records at or above the 8 KiB buffer capacity — any read over roughly 4 kb — bypass the buffer and reach stdout in a single write call that is free to consume only part of it. The same discarded result also swallowed write errors on that path, so a read that failed to write produced no message and no non-zero exit. Now uses write_all and handles the error.

chopper ... | head no longer panics. A closed downstream pipe previously ended the run with a Rust panic and exit 101; it now stops quietly.

Validation

Modification tag rewriting was checked against htslib rather than against chopper itself: for 1000 reads, all 35,050 surviving calls under fixed-crop and 37,404 calls across 8,144 segments under split-by-low-quality decode at exactly the expected position with the expected probability. Untrimmed input is a byte-exact identity transform, and untagged input is byte-identical to v0.13.

Full changelog: v0.13.0...v0.14.0