Skip to content
Open
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@ Sections commonly used: Features, Bug fixes, Other changes.

### Features

- `--soloOutLayout CellRanger` writes the solo matrices in the shape
`cellranger count` produces: `outs/raw_feature_bc_matrix/` and
`outs/filtered_feature_bc_matrix/`, gzipped, with a `-1` GEM-well suffix
on every barcode and one raw column per observed barcode. It implies
`--soloOutGzip yes`, `--soloOutRawBarcodes Observed` and an `outs/`
output directory, each still overridable on the command line. Counts are
unchanged. It is the default on 10x geometry, which **changes where
output files are written** on those runs; see `DIVERGENCE.md` §3.3.

- On 10x geometry (`CB_UMI_Simple`, a whitelist, 16 bp CB, 10 or 12 bp
UMI), the five CellRanger-matching flags now **default** to their
CellRanger values. Any flag named on the command line wins, and the
substitution is logged. **This changes default output on 10x runs** and
diverges from STARsolo; see `DIVERGENCE.md` §1.3.

- `--soloOutRawBarcodes Observed` writes the raw matrix with one column
per *observed* barcode instead of one per whitelist barcode, matching
what CellRanger's `raw_feature_bc_matrix` contains. Counts are
unchanged; on a 200-cell run `barcodes.tsv` goes from 62 MB to 3.4 kB.
**Not a STAR parameter**; default `Whitelist` keeps STARsolo behaviour.

- **STARsolo single-cell quantification (`--soloType`)** — the 10x
Chromium / plate-based count-matrix pipeline, ported from STAR and
verified against real STARsolo (#90).
Expand Down Expand Up @@ -99,6 +120,12 @@ Sections commonly used: Features, Bug fixes, Other changes.

### Bug fixes

- `--soloUMIfiltering MultiGeneUMI_CR` kept every gene tied at the
highest read count; CellRanger gives a tied UMI to no gene at all.
Since one read per gene is the ordinary shape of a multi-gene UMI, the
flag removed nothing in practice. On a 20k-read 10x fixture the count
matrix moves from 16 465 to 15 414 against STAR's 15 423.

- **STARsolo `Gene` assignment now requires exon concordance**, matching
STARsolo: a read counts toward a gene only when every aligned block
lies within the gene's exons, rather than merely overlapping one. This
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ noodles-bgzf = { version = "0.49", features = ["libdeflate"] }

[dev-dependencies]
assert_cmd = "2"
# Already a runtime dependency at the same version; listed here so the
# integration tests can read the gzipped CellRanger-layout matrix files.
flate2 = { version = "1", default-features = false, features = ["zlib-rs"] }
predicates = "3"

[build-dependencies]
Expand Down
93 changes: 93 additions & 0 deletions DIVERGENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,99 @@ On the 10k yeast PE benchmark, 4 reads differ in alignment score (AS) because ST

---

### 1.3 CellRanger behaviour is the default on 10x geometry

**What STAR does.** STARsolo's defaults are its own (`1MM_multi`,
`1MM_All`, no UMI filtering, `Hamming` clipping, `outFilterScoreMin 0`)
whatever the barcode geometry. Matching CellRanger requires passing five flags,
listed in STAR's `docs/STARsolo.md`.

**What rustar-aligner does.** When the run is unambiguously 10x —
`CB_UMI_Simple`, a whitelist, a 16-base CB and a 10- or 12-base UMI — those
five flags default to their CellRanger values. Any flag given on the command
line wins, and the substitution is logged in full.

**Why.** A user aligning 10x data and comparing against CellRanger otherwise
gets a successful run and different numbers, with nothing pointing at the five
flags that explain it. Measured against CellRanger 10.0.0 on a 20 000-read
fixture, those flags move the count matrix from 8.96% above CellRanger to
0.03% above it, once #165's `cbMinP` posterior threshold is also applied.
STAR 2.7.11b with the same flags is at +0.09%, so all three agree to within a
fraction of a percent.

**Impact.** This is a **change of default output behaviour** and therefore the
largest divergence in this file. It is confined to a geometry nothing else in
common use shares, it is escapable by naming any flag explicitly, and it is
announced at `INFO` on every run it touches. It needs maintainer sign-off.

**Source.** `src/params/mod.rs` (`looks_like_10x`,
`apply_cellranger_defaults_on_10x`). STAR: `docs/STARsolo.md`, "Matching
CellRanger 4.x and 5.x results".

---

### 3.2 `--soloOutRawBarcodes Observed` (opt-in, non-STAR)

**What STAR does.** STARsolo's raw matrix has one column per whitelist
barcode, whether or not any read carried it. For 10x v3 that is 3 686 400
columns and a 62 MB `barcodes.tsv`, nearly all of it zeros.

**What rustar-aligner does.** The same, by default. `--soloOutRawBarcodes
Observed` narrows the raw matrix to the barcodes that actually hold a count,
which is what CellRanger's `raw_feature_bc_matrix` contains. On a 200-cell
fixture that is 200 columns and a 3.4 kB `barcodes.tsv`.

**Why.** Someone comparing our raw matrix against CellRanger's finds no
overlapping keys at all, because the two files mean different things by "raw".
The flag makes the comparison possible without changing what STARsolo users
get.

**Impact.** The counts are identical either way — same entries, same values,
verified on the fixture — only the columns present differ. This is a non-STAR
flag and needs maintainer sign-off; it is off by default so STARsolo parity is
untouched.

**Source.** `src/solo/count.rs` (`observed_barcodes`), `src/params/mod.rs`
(`solo_out_raw_barcodes`). CellRanger: `outs/raw_feature_bc_matrix/` from a
`cellranger count` run, observed directly rather than taken from its source.

---

### 3.3 `--soloOutLayout CellRanger` (non-STAR; on by default on 10x geometry)

**What STAR does.** STARsolo writes
`Solo.out/<feature>/{raw,filtered}/{matrix.mtx, barcodes.tsv, features.tsv}`,
uncompressed, with bare barcodes.

**What rustar-aligner does.** The same, by default, on non-10x geometry.
`--soloOutLayout CellRanger` writes the same numbers in the shape
`cellranger count` produces: `outs/{raw,filtered}_feature_bc_matrix/`, all three
files gzipped, a `-1` GEM-well suffix on every barcode, one raw column per
observed barcode, and no per-feature subdirectory when a single feature is
requested. It implies `--soloOutGzip yes`, `--soloOutRawBarcodes Observed` and
`--soloOutFileNames outs/ ...`, each still overridable on the command line.

Under §1.3 the same 10x detection turns this on by default, so a bare 10x run
lands in CellRanger's layout.

**Why.** Tools written against CellRanger's `outs/` (scanpy's `read_10x_mtx`,
Seurat's `Read10X`, any in-house loader) key on those directory names and on
the `-1` suffix. Without them the numbers are right and nothing downstream can
read them without a rename step.

**Impact.** No count changes: verified entry by entry on the 20 000-read
fixture, 13 959 entries and 15 439 counts in both layouts. On 10x geometry it
**changes where output files are written**, which needs maintainer sign-off
alongside §1.3. Against a real `cellranger count` run on the same fixture the
raw barcode sets match exactly, 200 of 200.

**Source.** `src/solo/count.rs` (`write_gene_matrix`, `write_one_barcode`),
`src/params/mod.rs` (`solo_out_layout`, `apply_cellranger_layout`). CellRanger:
`outs/` from a `cellranger count` 10.0.0 run, observed directly rather than
taken from its source.

---

## 4. Implementation divergences (no intended output difference)

These differ in *how* a result is produced, not *what* is produced. They are documented so a reviewer chasing a discrepancy knows the mechanism differs by design.
Expand Down
Loading
Loading