mopdepth

build Lines of Code Static Badge

A fast BAM/CRAM depth calculation tool written in Crystal, inspired by mosdepth.

This is an experiment to see if well-known tools can be ported to Crystal using “vibe coding”.

Features

Installation

Prerequisites

Build from source

git clone https://github.com/kojix2/mopdepth
cd mopdepth
make

The default target always builds an optimized release binary at bin/mopdepth. Use make debug only when a non-optimized development binary is needed.

D4 support is included in the regular build through bio-cr/d4.cr. It requires neither Rust nor the native d4binding library. Run make test-d4 to execute the D4 write/read-back integration tests.

Library API

Applications can use mopdepth without invoking its CLI:

require "mopdepth"

options = Mopdepth::Options.new(
  mode: Mopdepth::Mode::Standard,
  minimum_mapping_quality: 20
)

Mopdepth::Reader.open("sample.bam", threads: 4) do |reader|
  reader.each_coverage(options: options) do |coverage|
    puts "#{coverage.region.chromosome}: #{coverage.values.size} bases"
  end
end

Mopdepth::Reader owns the indexed BAM/CRAM handle and exposes coverage, coverage_into, and each_coverage. Coordinates are always zero-based and half-open. coverage_into reuses a caller-owned Array(Int32); returned arrays contain one value per base and never expose the internal difference-array sentinel. A reader is serial: use one reader per concurrent worker.

Long operations support cooperative cancellation and progress callbacks:

control = Mopdepth::Control.new { |progress| puts progress }
# Another fiber or thread may call control.cancel.
coverage = reader.coverage("chr1", options: options, control: control)

Usage

./mopdepth [options] <prefix> <BAM-or-CRAM>

Basic example

./mopdepth output sample.bam

Options

Processing modes

Note: Fast mode and fragment mode cannot be used together.

Window sizes must be positive. BED intervals are validated against the alignment header: coordinates must be 0-based half-open, non-empty, non-negative, and contained in the named reference. References absent from the alignment header are reported and ignored. Overlapping BED intervals remain independent observations.

With -n -b regions.bed, chromosomes absent from the BED are skipped entirely when neither -q nor -T is requested. They are therefore absent from the chromosome summaries and global distributions, and do not contribute to total. For chromosomes present in the BED, total and the global distribution still cover each entire processed chromosome (or the selected --chrom range), while total_region and the region distribution cover the BED intervals. This skip is a mosdepth-compatible performance optimization.

--chrom also accepts a 1-based inclusive range such as chr1:100-200. Unlike mosdepth 0.3.x, mopdepth applies that range to calculation and output. In fragment mode it still examines the complete chromosome so fragments spanning the selected range are not missed.

Output files

By default, files are named with the mopdepth.* label. Use -M/--mos to switch to mosdepth.*.

Histogram-based outputs have bounded depth bins, matching mosdepth's performance conventions: -m reports 65,535 for medians above 65,535; numeric-window region.dist places rounded means or medians above 511 in bin 511; and global or BED-region distributions place per-base depths above 400,000 in bin 399,990. These bounds do not clip per-base depth, threshold counts, quantized output, or mean and maximum depth in the summary.

mosdepth compatibility

mopdepth follows mosdepth's coverage, filtering, NoData, threshold, quantization, and window-distribution conventions where those conventions are well-defined. Compressed output and CSI files are compared by decoded content and query results, not by their binary bytes.

Two known mosdepth edge-case errors are intentionally not reproduced: contained read pairs use the true intersection when removing mate overlap, and quantization examines the final real base of every reference. Region means are accumulated as integer depth sums before formatting to avoid floating-point accumulation drift.

D4-enabled builds support mosdepth-style --d4 per-base output and create an embedded secondary frame index after successfully closing the file. -M/--mos changes compatible output names; it does not enable bug-for-bug compatibility.

Summary file format

The summary file contains the following columns:

Examples

Basic depth calculation

./mopdepth output sample.bam

With BED regions

./mopdepth -b regions.bed output sample.bam

Window-based analysis (1kb windows)

./mopdepth -b 1000 output sample.bam

Fast mode with MAPQ filtering

./mopdepth -x -Q 20 output sample.bam

Fragment mode for paired-end data

./mopdepth -a -l 100 -u 1000 output sample.bam

License

MIT License