Metadata-Version: 2.4
Name: sniffcell
Version: 0.9.7
Summary: SniffCell annotates structural variants using long-read methylation evidence and ctDMR signals.
Home-page: https://github.com/Fu-Yilei/SniffCell
Author: Yilei Fu
Author-email: yilei.fu@bcm.edu
License: Apache-2.0
Project-URL: Bug Tracker, https://github.com/Fu-Yilei/SniffCell/issues
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pysam>=0.21.0
Requires-Dist: numpy>=2.2.0
Requires-Dist: pandas>=2.3.0
Requires-Dist: scipy
Requires-Dist: tqdm
Requires-Dist: scikit-learn
Requires-Dist: matplotlib
Provides-Extra: discover
Requires-Dist: tdb; extra == "discover"
Requires-Dist: seaborn>=0.13; extra == "discover"
Provides-Extra: discover-plots
Requires-Dist: seaborn>=0.13; extra == "discover-plots"
Provides-Extra: igvreport
Requires-Dist: igv-reports; extra == "igvreport"
Provides-Extra: full
Requires-Dist: tdb; extra == "full"
Requires-Dist: seaborn>=0.13; extra == "full"
Requires-Dist: igv-reports; extra == "full"
Dynamic: license-file

# SniffCell

[![PyPI version](https://img.shields.io/pypi/v/sniffcell.svg)](https://pypi.org/project/sniffcell/)
[![Python](https://img.shields.io/badge/python-%3E%3D3.10-3776AB?logo=python&logoColor=white)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Issues](https://img.shields.io/badge/Issues-GitHub-red?logo=github)](https://github.com/Fu-Yilei/SniffCell/issues)

SniffCell links somatic structural variants (SVs) and tandem-repeat (TR)
expansions/contractions to the cell type they occurred in, using long-read DNA
methylation. It calls cell-type-specific differentially methylated regions
(ctDMRs) from a reference methylation atlas, discovers SVs and TRs directly
from long-read BAMs, extracts read-level methylation, and assigns each
variant's supporting reads to the cell population whose methylation profile
they match.

## Which Package Should I Use?

Use **`sniffcell`** when you are discovering variants from a BAM (no existing
callset yet):

- ctDMR discovery from an atlas (`find`)
- cell-type deconvolution and BAM splitting (`deconv`)
- cell-type-aware SV, tandem-repeat, and SNV discovery (`discover`)
- methylation-based annotation of the discovered variants (`anno`, `svanno`)
- visualization, IGV screenshots, differential methylation tests, and HTML reports

Use **`sniffcell-lite`** when you already have a variant callset with each
variant's supporting reads identified (e.g. from Sniffles/kanpig or another
caller) and just want fast ctDMR-based cell-type annotation, without running
the full deconvolution/discovery pipeline:

- `sniffcell-lite find`
- `sniffcell-lite anno`

`sniffcell-lite` is published as a separate PyPI package and does not replace
the full `sniffcell` package:

```bash
pip install sniffcell-lite
sniffcell-lite --help
```

The lite package is maintained in the separate
[`Fu-Yilei/SniffCell-lite`](https://github.com/Fu-Yilei/SniffCell-lite)
repository.

## Install SniffCell

```bash
pip install sniffcell
```

For the full external-tool environment:

```bash
micromamba env create -f environment.yml
micromamba activate sniffcell
pip install sniffcell
```

## Methylation Atlases

Download the processed methylation atlas inputs and precomputed cell-type-specific
differentially methylated regions (ctDMRs) from
[Zenodo](https://zenodo.org/records/22003085). For the `find` example below, place
`all_celltypes_blocks.npy`, `all_celltypes_blocks.index.gz`,
`index_to_major_celltypes.json`, and `all_celltypes.txt` in the `atlas/` directory.
Precomputed ctDMR tables are available for brain/cerebellum, PBMC, lung, liver,
pancreas, kidney, breast, and colon, and can be supplied directly to `deconv`
or `anno` with `-b` instead of running `find`.

To discuss tissue-specific atlases, additional tissues or cell types, or custom
atlas support, please [open a GitHub issue](https://github.com/Fu-Yilei/SniffCell/issues)
or Email us.

## Example Workflows

### SniffCell: discover and annotate SVs/TRs from a BAM

1. Call ctDMRs from an atlas:

```bash
sniffcell find \
  -n atlas/all_celltypes_blocks.npy \
  -i atlas/all_celltypes_blocks.index.gz \
  -cf atlas/index_to_major_celltypes.json \
  -m atlas/all_celltypes.txt \
  -ck pbmc \
  -o pbmc_ctdmr.tsv
```

2. Split the BAM into cell-type groups using those ctDMRs:

```bash
sniffcell deconv \
  -i sample.bam \
  -r ref.fa \
  -b pbmc_ctdmr.tsv \
  -o deconv_out \
  --split_bam_groups "Lymphoid=T-cell,NK-cell,B-cell;Myeloid=Monocyte" \
  -t 8
```

3. Discover SVs and tandem repeats from the two cell-type-split BAMs:

```bash
sniffcell discover tools run \
  --deconv-dir deconv_out \
  --reference ref.fa \
  --tr-bed tr_catalog.bed \
  --sex female \
  --run-id run1 \
  --threads 8
```

This runs Sniffles/Kanpig/Truvari (SVs), TRGT or Medaka (tandem repeats), and
optionally Clair3 (SNVs) on the two split BAMs, then harmonizes everything
into `deconv_out/deconv_requested_group_splits/discover/run1/harmonized_variants.tsv`.
Tool binaries are resolved from `PATH` by default, or pass explicit
`--sniffles-bin`/`--trgt-bin`/etc. flags; run `sniffcell discover tools check`
to preflight-check dependencies first.

4. Annotate the discovered SVs/TRs with cell-type methylation evidence:

```bash
sniffcell anno \
  -i sample.bam \
  -v deconv_out/deconv_requested_group_splits/discover/run1/harmonized_variants.tsv \
  -r ref.fa \
  -b pbmc_ctdmr.tsv \
  -o anno_out \
  -t 8
```

5. Build an HTML review report:

```bash
sniffcell report --anno_output anno_out
```

Open `anno_out/report/index.html` to review high-confidence calls and figures.

### SniffCell Lite: annotate an existing callset

If you already have a variant and know its supporting read names, skip
deconvolution and discovery entirely:

```bash
sniffcell-lite find -ck "Colon, Ascending" -o colon_ascending.ctdmr.tsv

sniffcell-lite anno \
  -i sample.bam \
  -r ref.fa \
  --variant-name variant_001 \
  --variant-location chr1:100000-101000 \
  --supporting-reads readA,readB,readC \
  --catalog colon_ascending.ctdmr.tsv \
  -o anno_out
```

`sniffcell-lite anno` also supports a `--batch variants.tsv` mode for
annotating many variants at once. See the
[SniffCell Lite README](https://github.com/Fu-Yilei/SniffCell-lite)
for details.

## Main Commands

| Command | Purpose |
|---------|---------|
| `sniffcell find` | Call ctDMRs from a methylation atlas |
| `sniffcell anno` | Extract BAM methylation and assign SVs/TRs to cell types |
| `sniffcell svanno` | Re-score SVs from a saved read-classification table |
| `sniffcell deconv` | Classify all reads and optionally split BAMs by group |
| `sniffcell discover` | Run cell-type-aware SV / TR / SNV discovery |
| `sniffcell viz` | Render per-SV methylation figures |
| `sniffcell igvviz` | Generate IGV batch screenshots |
| `sniffcell report` | Filter calls and build an HTML review report |
| `sniffcell dmsv` | Test methylation differences around SVs |

## Inputs

| Input | Format | Used by |
|-------|--------|---------|
| Long-read alignment | BAM with `MM` / `ML` modification tags | `anno`, `deconv`, `discover`, `dmsv`, `viz` |
| Variants | VCF / VCF.GZ, harmonized TSV from `discover`, or a variant + supporting-read names (`sniffcell-lite`) | `anno`, `dmsv`, `viz`, `report` |
| Reference genome | FASTA plus index | `anno`, `deconv`, `discover`, `dmsv`, `viz` |
| ctDMR table | TSV from `sniffcell find` | `anno`, `deconv`, `viz` |
| Methylation atlas | NumPy matrix, CpG index, and metadata | `find` |

## Outputs

A `find -> deconv -> discover -> anno -> report` run produces:

```text
pbmc_ctdmr.tsv
deconv_out/
  deconv_summary.tsv
  deconv_reads_classification.tsv
  deconv_requested_group_splits/
    <group>.bam
    discover/run1/
      harmonized_variants.tsv
      run_summary.json
anno_out/
  reads_classification.tsv
  blocks_classification.tsv
  sv_assignment.tsv
  sv_assignment_readable.tsv
  sv_assignment_readable_long.tsv
  anno_run_manifest.json
  report/
    index.html
    high_confidence_sv.tsv
    figures/
```

A `sniffcell-lite find -> anno` run produces:

```text
colon_ascending.ctdmr.tsv
colon_ascending.ctdmr.tsv.igv.bed
anno_out/
  variant_assignment.tsv
  variant_assignment_readable.tsv
  reads_classification.tsv
  anno_compact_manifest.json
```

### Discovery VCF exports

`sniffcell discover tools run` automatically exports matching caller records
beside `harmonized_variants.tsv`, when the corresponding source VCFs exist:

```text
harmonized_variants.sv.vcf.gz
harmonized_variants.tr.<group>.vcf.gz
harmonized_variants.snv.<group>.vcf.gz
```

Each VCF is coordinate-sorted, BGZF-compressed, and tabix-indexed. SV records
come from the joint Sniffles/Truvari/Kanpig postprocessing VCF; TR records come
from each group's TRGT VCF; SNV records come from each group's Clair3 pileup
VCF used for comparison. Original caller alleles, genotypes, quality, and
filters are retained. These are subsets, not copies of the full callsets.

Additional INFO fields describe the SniffCell evidence:

| INFO field | Meaning |
|------------|---------|
| `SC_ID`, `SC_ROW` | Harmonized variant ID and one-based data-row number |
| `SC_CLASS`, `SC_SUBTYPE` | Variant class and change subtype |
| `SC_CATEGORY` | `group_a_only`, `group_b_only`, `shared`, or `unknown` |
| `SC_GROUP_A`, `SC_GROUP_B` | Actual comparison-group labels |
| `SC_TARGET_GROUP` | Group carrying the candidate change; absent for shared/unknown calls |
| `SC_SUPPORT_A`, `SC_SUPPORT_B` | Support counts from the harmonized TSV |
| `SC_CHANGE_BP` | SniffCell change size; for TRs, a between-group difference |

String values are percent-encoded so composite cell-type labels remain intact.
These annotations identify **split-BAM group-specific candidates**, not
methylation-confirmed assignments from `anno`. Zero support does not establish
reference genotype or coverage. For TRs, annotations describe a locus-level
change, not necessarily a particular TRGT ALT allele; `SC_CHANGE_BP` is not
reference-relative `SVLEN`. Multiple harmonized changes at one locus yield
separate annotated copies, distinguished by `SC_ROW`.

Missing source records are not reconstructed. Export paths, counts, and
unmatched data-row numbers are recorded in `harmonized_variants_manifest.json`,
with warnings for missing matches. Medaka-only TR runs currently have no TRGT
VCF export. An opposite-group SNV record may be absent even when the target
group has a matching call. These VCFs preserve caller genotypes rather than
inferring somatic genotypes; they are not somatic truth sets.

## Documentation

For command-line options, run `sniffcell --help` or
`sniffcell <command> --help` (for example, `sniffcell anno --help`).

## Citation

If you use SniffCell in your research, please cite:

> **[Cell-type-resolved somatic variant discovery from bulk long-read sequencing](https://www.medrxiv.org/content/10.64898/2026.09.01.26361966v1)**
> Yilei Fu et al. *medRxiv preprint (2026).* DOI: 10.64898/2026.09.01.26361966.

## License

MIT License. See [LICENSE](LICENSE) for details.
