Detect and quantify plasmid DNA contamination in short-read sequencing data (BAM and paired/single-end FASTQ).
Overview • Quick Start • Installation • CLI Reference • Pipeline Architecture • Outputs & Metrics • Development • License
PlasmiCheck identifies recombinant plasmid DNA and cDNA contamination in clinical and research sequencing datasets without requiring prior annotation of the cloned insert. Given a human reference genome, one or more plasmid construct files (.gb, .xdna, .dna, .fasta), and sequencing reads (.bam or .fastq.gz), PlasmiCheck extracts the target human locus via spliced alignment, aligns reads competitively against both references, and produces interactive HTML, static PNG, TSV, and Excel reports.
Key Capabilities:
- Annotation-Free cDNA Insert Mapping: Spliced alignment (
minimap2 -ax splice) automatically identifies the target human locus, exon/intron structure, minus-strand orientation, and circular-origin wrap-around coordinates. - Paired-End BAM & FASTQ Support: Coordinate-sorted BAM inputs are collated once per sample (
samtools collate -u -O | samtools fastq) for paired-end alignment (minimap2 -ax sr -p) alongside native paired FASTQ support. - Competitive Read Scoring & Tie-Breaking: Evaluates primary read pairs by mapping quality, edit distance (
NM), clipping penalties, circular-origin overhangs, and index-sizeMAPQsymmetry. - Orthogonal Contamination Evidence: Quantifies vector-only reads (
Backbone_Only), base-levelCoverageOutsideINSERT, vector resistance gene depth/breadth, and direct exon–exonSpliceJunctionReads. - Single-Sample & Cohort Reporting: Generates interactive Plotly and static Matplotlib HTML reports with a 3-zone Contamination Scale (
Clean < 0.6,Unclear 0.6–1.0,Contaminated > 1.0) and cohort Benjamini–Hochberg FDR outlier statistics. - Content-Addressable Caching: Lock-protected MD5 cache (
~/.plasmicheck/cache) reuses reference indexes and spliced alignments across batch and Snakemake runs.
# View CLI help and version
plasmicheck --help
plasmicheck --version
# Screen a BAM sample against a GenBank plasmid construct
plasmicheck pipeline \
-hf reference/GRCh38.fa \
-pf reference/plasmid/pCMV6_CACNA1H.gb \
-sf1 data/sample.bam \
-o output/ \
--static-report
# Screen paired-end FASTQ files against a list of plasmids
plasmicheck pipeline \
-hf reference/GRCh38.fa \
-pf plasmid_list.txt \
-sf1 data/sample_R1.fastq.gz \
-sf2 data/sample_R2.fastq.gz \
-o output/ \
--static-report
# Aggregate multi-sample / multi-plasmid results into a cohort summary report
plasmicheck summary_reports \
-i output/ \
-o output/summary/ \
--static-reportRequires Python >= 3.10, bash, minimap2 (>= 2.17), and samtools (>= 1.19, required for lexicographic samtools sort -N):
git clone https://github.com/scholl-lab/plasmicheck.git
cd plasmicheck
uv sync --locked --no-dev
source .venv/bin/activatePrebuilt container images bundling Python 3.12, minimap2, and samtools are published to GHCR:
docker pull ghcr.io/scholl-lab/plasmicheck:latest
docker run --rm --user $(id -u):$(id -g) \
-v $(pwd)/data:/data:ro \
-v $(pwd)/output:/output \
ghcr.io/scholl-lab/plasmicheck:latest \
pipeline -hf /data/GRCh38.fa -pf /data/plasmid.gb -sf1 /data/sample.bam -o /outputgit clone https://github.com/scholl-lab/plasmicheck.git
cd plasmicheck
conda env create -f conda/plasmicheck_full_conda.yml
conda activate plasmicheck
uv sync --locked --no-dev --python "$CONDA_PREFIX/bin/python"| Subcommand | Description | Key Flags |
|---|---|---|
pipeline |
Run end-to-end contamination detection & reporting | -hf, -pf, -sf1, [-sf2], -o, [--threads], [--static-report] |
convert |
Convert GenBank / SnapGene / xDNA plasmid file to FASTA | -i, -o, [-f {genbank,xdna,snapgene,fasta}], [-w] |
index |
Build atomic .mmi and .fai indexes for a FASTA file |
-f, [-w] |
spliced |
Spliced-align plasmid to human genome & extract cDNA coordinates | -i, -p, -b, -o, [-hf], [--cDNA_output], [-d 1000] |
align |
Align sequencing reads to plasmid or spliced human index | -r, -i [FILE ...], -o, -a {plasmid,human} |
compare |
Stream-compare name-sorted alignments and compute metrics | -p, -m, -o, [-t 0.8] |
report |
Generate single-sample interactive & static HTML reports | -r, -s, -o, [--static-report], [--plot-backend {matplotlib,plotly}] |
summary_reports |
Aggregate cohort comparisons into heatmaps, boxplots & Excel | -i, -o, [--static-report], [--plot-backend {matplotlib,plotly}] |
cache |
Inspect or clean ~/.plasmicheck/cache (info, clean) |
plasmicheck cache info, plasmicheck cache clean --all |
version |
Print ASCII banner and package version | plasmicheck version |
# 1. Convert plasmid construct to FASTA and index references
plasmicheck convert -i plasmid.gb -o plasmid.fasta
plasmicheck index -f plasmid.fasta
plasmicheck index -f GRCh38.fa
# 2. Extract spliced human locus and cDNA insert boundaries
plasmicheck spliced \
-i GRCh38.mmi \
-hf GRCh38.fa \
-p plasmid.fasta \
-b spliced_alignment.bam \
-o spliced_reference.fasta \
--cDNA_output cDNA_positions.txt
plasmicheck index -f spliced_reference.fasta
# 3. Align reads and compare competitive assignments
plasmicheck align -r plasmid.mmi -i sample_R1.fastq.gz sample_R2.fastq.gz -o plasmid_alignment.bam -a plasmid
plasmicheck align -r spliced_reference.mmi -i sample_R1.fastq.gz sample_R2.fastq.gz -o spliced_human_alignment.bam -a human
plasmicheck compare -p plasmid_alignment.bam -m spliced_human_alignment.bam -o results/comparison_result
# 4. Generate HTML report
plasmicheck report \
-r results/comparison_result.reads_assignment.tsv \
-s results/comparison_result.summary.tsv \
-o results/reports/ \
--static-reportsnakemake/plasmicheck.smk screens a cohort of BAMs against all plasmid constructs in a 3-rule DAG (prepare_constructs → scan_sample → summary_reports) with one job per sample BAM (avoiding the N_samples × N_plasmids cross-product), shared content-addressed construct and spliced-alignment caching (forwarding gtf_file, padding, shift_bases, and generate_shifted), samtools bedcov target-BED depth normalization (target_bed), and <2 h per-job walltime. See snakemake/config.example.yaml, snakemake/config.schema.yaml, and snakemake/README.md:
snakemake -s snakemake/plasmicheck.smk \
--configfile snakemake/config.example.yaml \
--use-conda \
--profile cubi-v1 \
-j 100flowchart TD
subgraph Prep["1. Plasmid Preparation & Spliced Locus Extraction"]
P1["Plasmid Construct<br/>(.gb / .xdna / .dna / .fasta)"] --> P2["convert & index<br/>plasmid.fasta + .mmi"]
H1["Human Reference<br/>(GRCh38.fa + .mmi)"] --> P3["spliced_alignment<br/>(minimap2 -ax splice)"]
P2 --> P3
P3 --> P4["Target Locus & Insert Bounds<br/>spliced_reference.fasta + cDNA_positions.txt"]
end
subgraph Align["2. Sample Collation & Parallel Competitive Alignment"]
S1["Sequencing Input<br/>(BAM or Paired FASTQ)"] --> S2["BAM Collation (once per sample)<br/>samtools collate | samtools fastq"]
S2 --> A1["Plasmid Alignment<br/>plasmid_alignment.bam"]
S2 --> A2["Spliced Human Alignment<br/>spliced_human_alignment.bam (sort -N)"]
P2 --> A1
P4 --> A2
end
subgraph Compare["3. Streaming Comparison & Orthogonal Metrics"]
A1 & A2 --> C1["compare_alignments<br/>Lexical Merge & Mate-Summed Scoring"]
C1 --> C2["Read Categories<br/>Plasmid / Human / Tied / Backbone_Only"]
C1 --> C3["Coverage & Junction Scan<br/>CoverageOutsideINSERT, SpliceJunctionReads, ResGenes"]
end
subgraph Report["4. Verdict & Reporting"]
C2 & C3 --> R1["Single-Sample Report<br/>3-Zone Scale (<0.6 | 0.6–1.0 | >1.0)"]
C2 & C3 --> R2["Cohort Summary Report<br/>Heatmaps, LOO BH-FDR p-values & Excel"]
end
Each (sample, plasmid) comparison writes <basename>.summary.tsv, <basename>.reads_assignment.tsv, cDNA_positions.txt, and HTML reports:
| Metric | Description | Interpretation |
|---|---|---|
Ratio |
Plasmid / Human read count ratio (excluding Tied and Backbone_Only by default) |
< 0.6: Clean · 0.6 – 1.0: Unclear · > 1.0: Contaminated |
Plasmid / Human / Tied |
Primary reads/pairs scoring higher on plasmid, human, or tied (CIGAR/NM identical) |
Shared cDNA exons tie; junction-spanning or vector-overlapping reads favor Plasmid |
Backbone_Only |
Plasmid-assigned reads with zero overlap with INSERT_REGION |
Direct evidence of vector backbone DNA |
CoverageOutsideINSERT |
Total aligned bases outside INSERT_REGION divided by insert length |
0.0000 in clean samples; elevated when vector backbone is present |
SpliceJunctionReads |
Reads spanning cDNA exon–exon boundaries without soft-clipping (SpliceJunctionsCovered / SpliceJunctionCount) |
Sensitive marker for low-level spliced cDNA contamination even when Ratio < 0.6 |
ResistanceGenes |
Mean depth and breadth (>=1x) across annotated AmpR/KanR/NeoR/HygR/PuroR/Bla genes |
Confirms specific vector construct identity |
Use Python 3.12 (pinned in .python-version) and uv.
Python 3.10 remains the compatibility floor; CI tests 3.10, 3.12, and 3.13.
The committed uv.lock supplies the same dependencies to local commands and CI.
make install-dev # uv sync --locked; no activation needed
make ci-check # lint, formatting, strict typing, coverage, build
make test-fast # quick portable tests without coverage
make test-integration # synthetic pipeline; minimap2 + samtools >=1.19
make lint-actions # actionlint via a pinned Docker image
make format # safe Ruff fixes and formatting
uv run --locked pre-commit installmake test enforces 70% combined statement and branch coverage across the whole package, including the CLI. Keep this baseline stable and raise it when meaningful tests support it; do not exclude difficult modules or lower the floor to make a change pass. Add focused regression tests for behavior changes. make test-fast avoids coverage overhead during iteration. Synthetic integration data is committed under tests/data/synthetic/ and needs no external downloads.
For a dependency change, edit pyproject.toml (or use uv add / uv add --dev), run uv lock, then run make ci-check, and commit both pyproject.toml and uv.lock together.
CI also installs the built wheel and sdist outside the checkout, checks package resources and CLI entry points, and builds the Docker smoke-test stage. Tagged releases (vX.Y.Z matching plasmicheck/version.py) run all CI checks before publishing the runtime container image to GHCR. See AGENTS.md for the code map and engineering guidelines.
MIT License — see LICENSE for details.