Cross-sample contamination and identity detector for targeted-capture NGS runs
Project description
Sentinel finds cross-sample contamination, sample swaps, and identity drift on a run of targeted NGS samples. It works by comparing each sample's genotype calls to every other sample on the same run, looking for the asymmetric read-mixing signature that contamination leaves behind.
Around that core directional score, Sentinel layers a per-sample drop in
alt allele frequency at hom-alt sites, a VAF anomaly check, a read-level
haplotype consistency test, and an identity anchor that catches outright
swaps. Each sample comes back with a PASS / WARN / FAIL verdict and
a best-guess donor when it failed. The output is a self-contained HTML
report with an interactive 96-well plate view, a flagged-samples table, and
a per-sample contamination bar plot.
How it works
flowchart LR
subgraph Inputs
SS[sample sheet]
BAM[BAMs]
CAT[SNP catalog]
end
subgraph sentinel_run [sentinel run]
EX[extract_ad<br/>walk each BAM<br/>at catalog sites]
SC[pipeline<br/>identity, directional,<br/>deflation, tails,<br/>haplotypes]
PP[post_process<br/>anchor check<br/>+ verdict]
end
subgraph Outputs
H[report.html]
X[report.xlsx]
TSV[per_sample_report.tsv]
CSV[per_sample_report.csv]
end
SS --> EX
BAM --> EX
CAT --> EX
EX --> SC
SC --> PP
PP --> H
PP --> X
PP --> TSV
PP --> CSV
classDef input fill:#dde6f2,stroke:#1f4e79,stroke-width:1.5px,color:#0a1426
classDef stage fill:#1f4e79,stroke:#0a1426,stroke-width:1.5px,color:#ffffff
classDef output fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#0a1426
class SS,BAM,CAT input
class EX,SC,PP stage
class H,X,TSV,CSV output
At each SNP site, for every ordered pair of samples (donor S, recipient T), Sentinel computes:
score(S, T) = mean alt-VAF in T over informative sites - background(T)
Informative sites are those at which the recipient T is called
homozygous-reference at sufficient depth, the donor S is alt-bearing, and T
meets the recipient-depth threshold. The score reported as score_homalt
further restricts the informative set to sites where S is homozygous-alt
(not just heterozygous). At a donor-hom-alt / recipient-hom-ref site, the
expected alt fraction in a recipient contaminated by the donor at fraction
α equals α - the donor contributes alt-bearing reads, the recipient
contributes reference reads. At a donor-het site that expected value equals
α / 2. Restricting to hom-alt sites therefore gives a direct estimate of
the contamination fraction from that donor.
The background(T) term is the mean alt-VAF in T at T's own hom-ref sites,
T's per-sample sequencing-error floor. Subtracting it makes score(S, T)
zero when T has no contamination. The matrix is asymmetric: row sums over
score(S, T) give the candidate donors pointing into recipient T, column
sums give the samples T is leaking into. Within-identity-group pairs
(replicates of the same individual) are masked out so they don't generate
false donor inferences.
Around that score, Sentinel layers a CHARR-style hom-alt deflation metric, a VAF-tail anomaly score, a read-level 3+-haplotype check, and an identity-anchor cross-check. The verdict logic combines all of these with sample-type-aware thresholds: controls and FFPE samples are held to looser bars than fresh clinical samples.
Install
The detector ships as a Python package with a small CLI. Pick one:
# pip
pip install sentinel-cd
# conda (bioconda)
conda install -c bioconda sentinel-cd
# Docker
docker pull ghcr.io/<org>/sentinel:latest
samtools and bcftools must be on PATH (the conda and Docker images bundle
them).
Tested with Python 3.10-3.12 on Linux and WSL. macOS works but isn't part of the CI matrix.
What you need
| Aligned BAMs | one per sample, indexed (.bai), GRCh38 reference |
| Sample sheet | CSV, one row per sample, at minimum a sample_id column |
| Site catalog | TSV from sentinel build-panel, scoped to your panel |
| Reference FASTA | GRCh38, only required if you run the haplotype branch |
The site catalog is a one-off per panel: build it once, reuse it for every run that uses the same baits.
Quick start
# 1. Build the catalog for your panel (one-off)
sentinel build-panel \
--bed mypanel.bed \
--master-db gnomad_v41_common.vcf.gz \
--panel-name MyPanel \
--out catalogs/
# 2. Run the detector on a run
sentinel run \
--sample-sheet samples.csv \
--bams-dir /path/to/bams \
--catalog catalogs/MyPanel_sites.tsv \
--ref GRCh38.fa \
--out my_run/
After sentinel run finishes you'll find in my_run/results/:
report.html- the interactive single-file report (open in a browser)report.xlsx- the same per-sample table, plus a column legend, as Excelper_sample_report.tsv- one row per sample, machine-readableper_pair_report.tsv- long-form donor-to-recipient table for flagged pairs
Open report.html and you get a 96-well plate view, a flagged-samples table,
a per-sample contamination bar plot, and a sortable all-samples table. Click
any row to isolate that sample on the plate.
Sample sheet
Minimum: one column.
sample_id
SAMPLE001
SAMPLE002
SAMPLE003
BAMs are matched in --bams-dir by <sample_id>_sorted.bam or
<sample_id>.bam.
Optional columns:
| Column | Purpose |
|---|---|
sample_type |
One of control, reference_fresh, reference_ffpe, mix, clinical. Drives the verdict thresholds; controls are held to a different bar than clinical samples. Defaults to clinical if absent. |
well |
96-well coordinate (e.g. A1, H12). When present, the report shows a plate-layout view that highlights neighbouring-well cross-contamination. |
expected_identity |
The sample identity each BAM is labelled as. If the genetics don't match, the sample is flagged as a swap. Defaults to sample_id. |
patient_id |
Trio / family grouping. Only used if multiple samples share a patient_id. |
A template is in examples/sample_sheet_template.csv and an extended example
with the optional columns is in examples/sample_sheet_with_optional.csv.
Commands
| Command | Purpose |
|---|---|
sentinel build-panel |
Build a SNP catalog by intersecting your panel BED with a master VCF |
sentinel run |
Extract allele depths, score, post-process, render reports |
sentinel report |
Re-render reports from an existing results directory |
Each command has --help with the full set of options.
The master SNP database
sentinel build-panel needs a master VCF of common SNPs. The recommended
source is gnomAD v4.1, filtered to biallelic SNPs at MAF >= 5%.
Two pre-built distributions are published:
| File | Source | Sites | Size | Host |
|---|---|---|---|---|
gnomad_v41_exomes_common.vcf.gz |
gnomAD v4.1 exomes, MAF >= 5%, biallelic SNPs | ~2.6M | ~1.8 GB | GitHub Releases |
gnomad_v41_genomes_maf01.vcf.gz |
gnomAD v4.1 genomes, MAF >= 1%, biallelic SNPs | ~15M | ~15 GB | Zenodo, DOI 10.5281/zenodo.20264481 |
The exomes file works for most capture panels (gene panels, exome panels, targeted hybridisation-capture assays). Use the genomes file when your panel includes non-coding bait regions (custom panels with intronic content, WGS, etc.).
Both files are bgzipped and tabix-indexed. Download, drop next to your
analysis, point sentinel build-panel --master-db at them.
# exomes (1.8 GB) + index, from GitHub
curl -LO https://github.com/Trethewey/Sentinel/releases/download/v0.1.0/gnomad_v41_exomes_common.vcf.gz
curl -LO https://github.com/Trethewey/Sentinel/releases/download/v0.1.0/gnomad_v41_exomes_common.vcf.gz.tbi
# genomes (15 GB) + index, from Zenodo
curl -LO https://zenodo.org/records/20264481/files/gnomad_v41_genomes_maf01.vcf.gz
curl -LO https://zenodo.org/records/20264481/files/gnomad_v41_genomes_maf01.vcf.gz.tbi
To build your own from scratch see tools/build_master_db.sh.
Docker
docker run --rm -v $PWD:/data ghcr.io/<org>/sentinel:latest run \
--sample-sheet /data/samples.csv \
--bams-dir /data/bams \
--catalog /data/catalogs/MyPanel_sites.tsv \
--ref /data/GRCh38.fa \
--out /data/run_output
The image is based on python:3.11-slim with samtools, bcftools, and all
Python dependencies preinstalled.
DNA Nexus
A DNA Nexus app wrapper is shipped in dxapp/. Build with:
cd dxapp && dx build --upload
The app exposes the same inputs as the CLI: sample sheet, BAMs, catalog,
reference FASTA, and the optional thread count. Outputs land back in the
DNA Nexus project as report.html, per_sample_report.csv, and
per_pair_report.csv.
Output schema
per_sample_report.tsv columns (the headline ones):
| Column | Meaning |
|---|---|
sample_id |
From the sample sheet |
well |
Plate well (if supplied) |
sample_type |
From the sample sheet (defaults to clinical) |
verdict |
PASS, WARN, or FAIL |
top_score_homalt |
Headline contamination score; approximately equals the contamination fraction |
top_donor_sample_id |
Best guess at the source of the contamination |
homalt_deflation |
Drop in alt-VAF at the sample's own hom-alt sites |
identity_match |
TRUE if the genetics match the declared identity, FALSE if swapped |
concordance_to_expected |
Genetic similarity to the declared identity (0-1) |
The Excel report carries every column and a legend sheet explaining each one.
Citation
If you use Sentinel in published work, please cite the GitHub repository (a preprint is in preparation) and the papers that introduced the methods Sentinel builds on.
Methods
- Lu W, Gauthier LD, Poterba T et al. CHARR efficiently estimates
contamination from DNA sequencing samples. Bioinformatics, 2023.
[
charr.py] - Jun G, Flickinger M, Hetrick KN et al. Detecting and estimating contamination of human DNA samples in sequencing and array-based genotype data. Am J Hum Genet 91:839-848, 2012. [conceptual baseline for per-sample contamination QC]
- Pedersen BS, Bhetariya PJ, Brown J et al. Somalier: rapid relatedness
estimation for cancer and germline studies using efficient genome
sketches. Genome Medicine 12:62, 2020. [
identity_loh.py,anchor_check.py] - Schmitt MW, Kennedy SR, Salk JJ et al. Detection of ultra-rare mutations
by next-generation sequencing. PNAS 109:14508-14513, 2012.
[
read_haplotypes.py] - Weissensteiner H, Forer L, Fendt L et al. Contamination detection in
sequencing studies using the mitochondrial phylogeny (Haplocheck).
Genome Research 31:309-316, 2021. [
mtdna_mixture.py,sct_discriminator.py]
Reference data
- Chen S, Francioli LC, Goodrich JK et al. A genomic mutational constraint map using variation in 76,156 human genomes (gnomAD v4). Nature 625:92-100, 2024.
- Trethewey C. Sentinel reference panel: gnomAD v4.1 genomes biallelic SNPs at MAF >= 1% (2026). Zenodo. DOI: 10.5281/zenodo.20264481.
Tools
- Li H. The Sequence Alignment/Map format and SAMtools. Bioinformatics 25:2078-2079, 2009.
- Danecek P, Bonfield JK, Liddle J et al. Twelve years of SAMtools and BCFtools. GigaScience 10:giab008, 2021.
- pysam: github.com/pysam-developers/pysam
Scientific Python stack (if your venue requires it): NumPy (Harris et al. Nature 585:357-362, 2020), pandas (McKinney, SciPy 2010), PyArrow, Plotly.
License
See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sentinel_cd-0.1.0.tar.gz.
File metadata
- Download URL: sentinel_cd-0.1.0.tar.gz
- Upload date:
- Size: 14.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa0fdc284c2c53c35086ab4f6d624c4bf3cc266a21feff7f9fc66f9449a59acd
|
|
| MD5 |
10ddf83c8538a27b9c734da3a58765b3
|
|
| BLAKE2b-256 |
82c83a9cc56bd5b84d85127b288069a4a352ef4a43a82794c461315678303c49
|
File details
Details for the file sentinel_cd-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sentinel_cd-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1784fad3800181bb871baa2438abdc5a7b6f42ce5df3130b2c60d464d428106f
|
|
| MD5 |
df4a553b9ac86117b6fea2824d1f1af1
|
|
| BLAKE2b-256 |
498732b23b9b1ccccf39216e5214597ffa9e089e089768ed6f0faca0a1800e92
|