Screening contigs¶
gapit screen aligns contig files against a gene database with BLAST and reports which genes
are present, using the same pipeline and hit rules as abricate. Output is abricate-compatible
TSV by default; JSON and Markdown are first-class alternatives (see ./outputs.md).
Database setup is covered in ./databases.md; a full walk-through lives in ./quickstart.md.
Options¶
Transcribed from gapit screen --help (gapit 0.5.6). Flags marked reads mode apply only when
you pass --r1/--r2; they are documented in ./reads.md.
Every option also accepts the single-dash short form listed in the Short column (e.g.
-d for --db); long forms remain the canonical spelling, and boolean negative halves like
--no-merge-fragments stay long-only. --db alone carries a second, word-style short alias:
-db ncbi (space-separated; an attached -dbncbi still binds to -d plus a value). A
positional argument that names a known database but is missing on disk is rejected with a
targeted usage error pointing at --db.
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
FILE... |
— | path(s) | required* | Input contig file(s) to screen. An all-FASTQ wildcard enters reads mode with samples auto-paired from filenames (see ./reads.md). |
--db |
-d, -db |
str | required | Database to screen against (datadir subdir). No default: a screen never silently materializes a bundled database — pick one explicitly (gapit db list). |
--datadir |
-D |
path | $GAPIT_DATADIR, then ~/.local/share/gapit/db |
Database directory. |
--minid |
-i |
float | 80.0 |
Minimum %identity, 0 < x <= 100. Enforced inside BLAST via -perc_identity. |
--mincov |
-c |
float | 80.0 |
Minimum %coverage, 0 <= x <= 100. Post-filter on the unrounded float. |
--threads |
-t |
int | 1 |
BLAST worker threads (passed to -num_threads). |
--jobs |
-j |
int | 1 |
Screen N input files concurrently. Output order is always input order. |
--merge-fragments |
-m |
flag | off | Merge gene fragments split across contigs (gapit extension; see below). |
--fofn |
-F |
path | none | File of filenames; replaces the positional FILEs. |
--quiet |
-q |
flag | off | Silence stderr diagnostics. |
--noheader |
-n |
flag | off | Suppress the #FILE ... header row. |
--nopath |
-p |
flag | off | Basename the FILE column. |
--debug |
-v |
flag | off | Verbose stderr diagnostics; echoes each external command line. |
--format |
-f |
tsv|csv|json|md | tsv |
Output format (tsv is the default everywhere: reads mode streams the table per completed file or sample). json/md are the explicit agent opt-ins; json is written once at the end (single document). |
--output |
-o |
path | stdout | Write the report to PATH instead of stdout (truncates any existing file). Streaming formats flush per file; stdout then carries no data. |
--aligner |
-a |
blastn|minimap2 | input-based | Alignment engine (default: blastn for contig files, minimap2 for --r1/--r2 reads). --aligner minimap2 routes positional FASTA assemblies through the minimap2 engine (FASTA content required; see ./reads.md). |
--r1 |
-1 |
str | none | Reads mode. Reads or assembly FASTA file(s), comma-separated, one per lane. |
--r2 |
-2 |
str | none | Reads mode. Comma-separated mate FASTQ file(s); must match --r1 count. |
--read-type |
-x |
sr|map-ont|map-hifi | sr for FASTQ, map-ont for FASTA |
Reads mode. minimap2 preset; resolved from the detected input when omitted. |
--min-breadth |
-b |
float | 90.0 |
Reads mode. Minimum %breadth for presence. |
--min-identity |
-I |
float | 0.0 |
Reads mode. Minimum %identity per alignment, 0 <= x <= 100; 0 = off. Any nonzero value emits gapit.reads/2 (./outputs.md). |
--min-mapq |
-M |
int | 0 |
Reads mode. Minimum MAPQ per alignment; 0 = off. Any nonzero value emits gapit.reads/2. |
--min-gene-cov |
-g |
float | 90.0 |
Cluster databases only. Minimum %coverage for a gene present verdict. |
--min-gene-id |
-G |
float | 90.0 |
Cluster databases only. Minimum %identity for a gene present verdict. |
--min-cluster-cov |
-C |
float | 96.0 |
Cluster databases only. Minimum locus %coverage for a best-locus call. |
* Positional FILEs or --fofn, or reads mode via --r1. Positional files and --r1/--r2
are mutually exclusive.
Input files¶
Normalization is native (no external any2fasta): plain FASTA, gzipped and bzip2-compressed
FASTA, FASTQ, GBK, and EMBL all work. The converted FASTA is buffered in memory (genome-scale
assemblies are a few MB) and fed to blastn on stdin. If normalization fails (not a sequence
file), gapit prints a gapit.error/1 envelope on stderr and exits 5.
One exception predates normalization: when every positional file is FASTQ (.fastq/.fq
± .gz, or content-sniffed FASTQ under an ambiguous extension) and no --aligner is given,
the whole invocation routes to the reads engine — gapit screen -d ecoli_dec *.gz screens
the glob as auto-paired samples instead of contigs. Mixing FASTA and FASTQ positionals is a
usage error naming the reads files. See ./reads.md for the pairing
conventions and the per-sample output.
A --fofn file lists one path per line and replaces positional arguments entirely.
Worked example¶
The tinyamr fixture db ships in the repo test data (three short AMR genes). Copy it to a
temp datadir, index it, and point $GAPIT_DATADIR at it. Never write into
~/.local/share/gapit/db for experiments.
$ mkdir -p /tmp/gapit-demo/datadir/tinyamr
$ cp tests/data/db/tinyamr/sequences /tmp/gapit-demo/datadir/tinyamr/sequences
$ gapit setupdb --datadir /tmp/gapit-demo/datadir
Indexed tinyamr (3 sequences, nucl)
$ export GAPIT_DATADIR=/tmp/gapit-demo/datadir
Default TSV¶
$ gapit screen tests/data/contigs/full.fa --db tinyamr
Processing: tests/data/contigs/full.fa
Found 1 genes in tests/data/contigs/full.fa
#FILE SEQUENCE START END STRAND GENE COVERAGE COVERAGE_MAP GAPS %COVERAGE %IDENTITY DATABASE ACCESSION PRODUCT RESISTANCE
tests/data/contigs/full.fa contig1 1 79 + tetA 1-79/79 =============== 0/0 100.00 100.00 tinyamr NC_000913.3:100-900 tetracycline efflux pump TetA TETRACYCLINE
Processing: and Found N genes lines go to stderr; the header and hit rows are stdout. With
stderr discarded the output is pure data:
$ gapit screen tests/data/contigs/full.fa --db tinyamr 2>/dev/null
#FILE SEQUENCE START END STRAND GENE COVERAGE COVERAGE_MAP GAPS %COVERAGE %IDENTITY DATABASE ACCESSION PRODUCT RESISTANCE
tests/data/contigs/full.fa contig1 1 79 + tetA 1-79/79 =============== 0/0 100.00 100.00 tinyamr NC_000913.3:100-900 tetracycline efflux pump TetA TETRACYCLINE
JSON¶
$ gapit screen tests/data/contigs/full.fa --db tinyamr --format json
{
"schema": "gapit.report/1",
"tool": {
"name": "gapit",
"version": "0.5.6"
},
"created_at": "2026-09-19T01:12:04Z",
"params": {
"db": "tinyamr",
"minid": 80.0,
"mincov": 80.0,
"threads": 1
},
"files": [
{
"file": "tests/data/contigs/full.fa",
"hits": [
{
"sequence": "contig1",
"start": 1,
"end": 79,
"strand": "+",
"gene": "tetA",
"coverage": "1-79/79",
"coverage_map": "===============",
"gaps": "0/0",
"coverage_pct": 100.0,
"identity_pct": 100.0,
"database": "tinyamr",
"accession": "NC_000913.3:100-900",
"product": "tetracycline efflux pump TetA",
"resistance": "TETRACYCLINE"
}
]
}
]
}
Field names are snake_case with units explicit (identity_pct, coverage_pct). The schema is
introspectable: gapit schema report. See ./outputs.md.
How --mincov filters¶
tests/data/contigs/partial.fa carries the first 44 bases of the 88 bp blaTEM-1 reference,
so coverage is 50%. The default threshold drops it:
$ gapit screen tests/data/contigs/partial.fa --db tinyamr --quiet
#FILE SEQUENCE START END STRAND GENE COVERAGE COVERAGE_MAP GAPS %COVERAGE %IDENTITY DATABASE ACCESSION PRODUCT RESISTANCE
Lowering the threshold keeps it:
$ gapit screen tests/data/contigs/partial.fa --db tinyamr --mincov 50 --quiet
#FILE SEQUENCE START END STRAND GENE COVERAGE COVERAGE_MAP GAPS %COVERAGE %IDENTITY DATABASE ACCESSION PRODUCT RESISTANCE
tests/data/contigs/partial.fa contig1 1 44 + blaTEM-1 1-44/88 ========....... 0/0 50.00 100.00 tinyamr J01749.1:1-861 class A beta-lactamase TEM-1 BETA-LACTAM
Compressed input, CSV, path and header control¶
$ gzip -c tests/data/contigs/gap.fa > /tmp/gapit-demo/gap.fa.gz
$ gapit screen /tmp/gapit-demo/gap.fa.gz --db tinyamr --quiet --nopath
#FILE SEQUENCE START END STRAND GENE COVERAGE COVERAGE_MAP GAPS %COVERAGE %IDENTITY DATABASE ACCESSION PRODUCT RESISTANCE
gap.fa.gz contig1 1 97 + sul1 1-94/94 ========/====== 1/3 100.00 96.91 tinyamr U12338.4:1-940 sulfonamide-resistant dihydropteroate synthase Sul1 SULFONAMIDE
$ gapit screen tests/data/contigs/full.fa --db tinyamr --format csv --quiet --nopath
#FILE,SEQUENCE,START,END,STRAND,GENE,COVERAGE,COVERAGE_MAP,GAPS,%COVERAGE,%IDENTITY,DATABASE,ACCESSION,PRODUCT,RESISTANCE
full.fa,contig1,1,79,+,tetA,1-79/79,===============,0/0,100.00,100.00,tinyamr,NC_000913.3:100-900,tetracycline efflux pump TetA,TETRACYCLINE
Many files: --fofn and --jobs¶
$ printf '%s\n' tests/data/contigs/full.fa tests/data/contigs/gap.fa > /tmp/gapit-demo/files.txt
$ gapit screen --fofn /tmp/gapit-demo/files.txt --db tinyamr --quiet
#FILE SEQUENCE START END STRAND GENE COVERAGE COVERAGE_MAP GAPS %COVERAGE %IDENTITY DATABASE ACCESSION PRODUCT RESISTANCE
tests/data/contigs/full.fa contig1 1 79 + tetA 1-79/79 =============== 0/0 100.00 100.00 tinyamr NC_000913.3:100-900 tetracycline efflux pump TetA TETRACYCLINE
tests/data/contigs/gap.fa contig1 1 97 + sul1 1-94/94 ========/====== 1/3 100.00 96.91 tinyamr U12338.4:1-940 sulfonamide-resistant dihydropteroate synthase Sul1 SULFONAMIDE
$ gapit screen tests/data/contigs/full.fa tests/data/contigs/gap.fa --db tinyamr --jobs 2 --quiet
#FILE SEQUENCE START END STRAND GENE COVERAGE COVERAGE_MAP GAPS %COVERAGE %IDENTITY DATABASE ACCESSION PRODUCT RESISTANCE
tests/data/contigs/full.fa contig1 1 79 + tetA 1-79/79 =============== 0/0 100.00 100.00 tinyamr NC_000913.3:100-900 tetracycline efflux pump TetA TETRACYCLINE
tests/data/contigs/gap.fa contig1 1 97 + sul1 1-94/94 ========/====== 1/3 100.00 96.91 tinyamr U12338.4:1-940 sulfonamide-resistant dihydropteroate synthase Sul1 SULFONAMIDE
With --jobs 2 the two files are screened concurrently but stdout stays in input order, so the
output is identical to the sequential run.
Progress bar on long batch runs¶
On an interactive terminal (stderr is a TTY, no --quiet) a multi-file batch shows a rich
progress bar on stderr — spinner, the current file's basename, bar, M/N count, percent,
elapsed time — replacing the per-file Processing:/Found N genes notes while it runs (the
--jobs × --threads oversubscription note still prints). Piped or redirected stderr and
--quiet keep the plain notes byte-identically — progress bars in logs are noise. The bar
lives entirely on stderr: stdout bytes are identical with it on or off. The positional-FASTQ
wildcard batch (./reads.md) and gapit db fetch all carry the same bar.
Typed gene databases: designation is a second command¶
When --db names a gene database built with a typing.json (the bundled ecoli_dec, or
any gapit db build --typing build), screening is pure gene detection: the output is the
frozen 15-column abricate table byte-identically in every format — typed and untyped gene
databases are indistinguishable on the screen surface. Designations come from the
two-stage pipeline's second stage: write the table with --output (or redirect), then run
gapit typing on it — or pipe it
straight in, the canonical one-liner:
$ gapit screen dec_s3_stx2a_escV_aggR_uidA.fasta dec_s2_pic_astA_uidA.fasta --db ecoli_dec --output dec.tsv --nopath --quiet
$ gapit typing dec.tsv --quiet
FILE SCHEME PHENOTYPE GENES CONFIDENCE SCORE RUNNER_UP NOTES
dec_s3_stx2a_escV_aggR_uidA.fasta gb4789_6 EHEC aggR;escV;stx2a;uidA high 1.0000 EAEC (1.0000) GB 4789.6-2016: EHEC = stx (any subunit) + escV (LEE); severity order EHEC>STEC/EPEC>ETEC>EIEC>EAEC: rules are declared in severity order so hybrids surface as runner_up
dec_s3_stx2a_escV_aggR_uidA.fasta risk_monitoring EHEC aggR;escV;stx2a;uidA high 1.0000 EAEC (1.0000) GB 4789.6-2016: EHEC = stx (any subunit) + escV (LEE); severity order EHEC>STEC/EPEC>ETEC>EIEC>EAEC: rules are declared in severity order so hybrids surface as runner_up
dec_s2_pic_astA_uidA.fasta gb4789_6 EAEC astA;pic;uidA high 1.0000 EHEC (0.0000) GB 4789.6: any of aggR/pic/astA
dec_s2_pic_astA_uidA.fasta risk_monitoring non-DEC astA;pic;uidA low 0.0000 STEC (0.0000) GB 4789.6-2016: EHEC = stx (any subunit) + escV (LEE); severity order EHEC>STEC/EPEC>ETEC>EIEC>EAEC: rules are declared in severity order so hybrids surface as runner_up
The piped form skips the intermediate file entirely — screen's stdout is typing's stdin (stdout purity holds on both sides: data on the pipe, diagnostics on stderr):
$ gapit screen dec_s3_stx2a_escV_aggR_uidA.fasta --db ecoli_dec --nopath --quiet | gapit typing --quiet
gapit typing reads stdin whenever it is not a terminal; gapit typing - is the explicit
stdin marker (valid with a terminal attached — it reads until EOF). The full input
contract lives in the typing page.
The same pipe feeds the matrix view. Screening many assemblies in one call batched all
their rows into one table (every row carries the same DATABASE; the FILE column spans
the whole batch), so gapit summary summarizes it straight off the pipe
into the gene×file presence matrix:
$ gapit screen -d ecoli_dec *.fna --quiet | gapit summary
Cluster databases are the exception: their typing is integrated into the screen itself
(the typed cluster TSV's PHENOTYPE column, below).
Streaming and --output¶
Long batch runs give feedback as they go, and the report can go to a file:
- tsv/csv/md stream per file. The header (or Markdown frontmatter) prints once screening
starts, and each file's rows/section print the moment that file finishes — in input order,
always. Under
--jobs N > 1emission is head-of-line: file i's output waits until files 1..i are all done (the pool yields in input order), so bytes on stdout are identical to the sequential run. The positional-FASTQ wildcard path (screen -d db *.fastq.gz, ./reads.md) has the same contract sample-wise:--jobsparallelizes samples, md streams the static frontmatter plus one## <sample>section per completed sample, and--outputpersists the streamed prefix on a mid-batch failure. - md streams; json is written once, at the end. The Markdown frontmatter is STATIC
metadata (schema, tool,
created_at, db, thresholds — no run totals), so it can lead the document and every file's section follows the moment the file completes, exactly like the tsv rows. On cluster databases each file's section carries its own summary row and gene table. Run totals (files,hits, ...) live in the JSON document, which is a single object written once at the end. --output PATH(all engines, including reads mode) writes the report to PATH instead of stdout: the file opens on the first output byte (truncating any existing file — v1 overwrite semantics, never append), every streamed chunk is flushed, and stdout then carries no data (stderr diagnostics are unchanged).- Errors mid-batch. If file k fails after files 1..k-1 streamed, the already-emitted
output persists — on stdout it is already printed; with
--outputthe file keeps the header/frontmatter plus files 1..k-1 — and then the typedgapit.error/1envelope prints on stderr with the documented exit code. A run that fails before any output (usage, dependency, db errors) creates no--outputfile at all.
--output¶
$ gapit screen tests/data/contigs/full.fa tests/data/contigs/gap.fa --db tinyamr --output report.tsv
Processing: tests/data/contigs/full.fa
Found 1 genes in tests/data/contigs/full.fa
Processing: tests/data/contigs/gap.fa
Found 1 genes in tests/data/contigs/gap.fa
$ cat report.tsv
#FILE SEQUENCE START END STRAND GENE COVERAGE COVERAGE_MAP GAPS %COVERAGE %IDENTITY DATABASE ACCESSION PRODUCT RESISTANCE
tests/data/contigs/full.fa contig1 1 79 + tetA 1-79/79 =============== 0/0 100.00 100.00 tinyamr NC_000913.3:100-900 tetracycline efflux pump TetA TETRACYCLINE
tests/data/contigs/gap.fa contig1 1 97 + sul1 1-94/94 ========/====== 1/3 100.00 96.91 tinyamr U12338.4:1-940 sulfonamide-resistant dihydropteroate synthase Sul1 SULFONAMIDE
The Processing:/Found lines are stderr; stdout is empty and report.tsv holds exactly the
bytes the same run would print.
--debug¶
Echoes the native normalization step and every external command line to stderr:
$ gapit screen tests/data/contigs/full.fa --db tinyamr --debug 2>&1 >/dev/null
Processing: tests/data/contigs/full.fa
gapit: normalize: tests/data/contigs/full.fa (fasta)
gapit: run: blastn -task blastn -dust no -perc_identity 80.0 -db /tmp/gapit-demo/datadir/tinyamr/sequences -outfmt '6 qseqid qstart qend qlen sseqid sstart send slen sstrand evalue length pident gaps gapopen stitle' -num_threads 1 -evalue 1E-20 -culling_limit 1 -max_target_seqs 10000
Found 1 genes in tests/data/contigs/full.fa
stdout is unchanged by --debug; it is a stderr-only flag.
Fragment merging (--merge-fragments)¶
Draft assemblies often break one gene across a contig boundary: each contig then carries only
a partial fragment of the gene, every fragment fails --mincov 80 on its own, and the gene
goes unreported. --merge-fragments (a gapit extension, off by default; blastn contig mode
only — it is a usage error in reads mode or with --aligner minimap2) rescues exactly those
genes:
- After the usual per-hit filtering, gapit groups the sub-
mincovfragments of a gene that were deduped on(contig, start, end)and passed--minid. - If the union of the fragments' subject intervals covers
>= mincovof the gene (100 * union_length / slen, compared on the unrounded float, same boundary rule as the per-hit filter) and at least two fragments are involved, one merged hit is reported. - Genes that already have one individually passing hit are never merged — their stray
partials are ignored. Groups whose union stays below
mincovstill report nothing.
Merged-row semantics:
%COVERAGEis the union coverage;%IDENTITYis the aligned-length-weighted mean of the fragment identities;GAPSand theCOVERAGE_MAP'/' marker come from summed gap counts, with the map binned over the interval union (same 15-char arithmetic as normal rows).SEQUENCEis the comma-joined list of contig names carrying the fragments (sorted; fine for TSV — avoid--format csvfor merged rows, where a comma inside a field is ambiguous).START/END/STRANDcome from the anchor: the fragment with the largest aligned length (ties: first in(contig, start)order).COVERAGEis the union's bounding span.- JSON (
gapit.report/1) additionally carries"merged": trueand afragmentsarray with each fragment's{contig, start, end, strand, identity_pct, coverage_pct}— additive optional fields, absent from every non-merged row. Markdown adds one detail line per merged gene under the table.
Without the flag nothing about this path runs: default output stays byte-identical to abricate (the Notes below still apply in full).
Cluster databases (kind: cluster)¶
When --db names a cluster database — built from GBK/GFF via
gapit db build or fetched from a kaptive
provider — gapit screen dispatches to the cluster engine instead of the blastn gene
pipeline. Nothing about the gene path changes: cluster-kind dispatch is additive and
gene-kind screening is byte-identical to the pre-cluster behavior.
How it works:
- One minimap2
asm20invocation per input file (query = the file's contigs, target = the database's locus FASTA,--csshort-form alignment trace). - Per-locus union coverage and identity come from the cs walk across all primary alignments — records from multiple contigs union, so a locus fragmented across contig boundaries still screens as fully present.
- Every annotated gene gets a verdict:
present(≥--min-gene-covcoverage AND ≥--min-gene-ididentity),partial(≥ 50% coverage but failing a present threshold), orabsent. - Loci rank by coverage, identity, covered bases, then id; the rank-1 locus becomes the
file's best call when its coverage reaches
--min-cluster-cov(default 96, a kaptive-style confidence floor). Below the floor no call is made.
Guards (usage errors, exit 2): --minid/--mincov/--merge-fragments/--jobs/--aligner
are gene-engine options and are rejected on cluster databases; the three --min-gene-*/
--min-cluster-cov flags are cluster-only and rejected elsewhere, including reads mode
(cluster screening is assembly-FASTA only in v1).
Phenotype calls (typing.json)¶
A cluster database may carry a gapit.typing/1 scoring spec (installed with
gapit db build --typing FILE). When it does, each file's best call is annotated with a
phenotype:
- Rules score independently —
weighted_genes(gene presence with an identity floor and optional negative markers),cluster_match(weighted locus coverage/identity/key-genes components with per-component floors), orlearned_linear(a trained sigmoid model over named features) — and the decision layer applies the document'scutoffandambiguity_margin: a clear winner is called withhighconfidence, two rules inside the margin yield anambiguouscall (phenotype null, both candidates listed), and a sub-cutoff best falls back to the document's fallback string. - JSON (
gapit.cluster/1) carriesbest.phenotypeplus an additivebest.phenotype_detailblock:{score, confidence, components[], runner_up, ambiguous[]}— the explainable per-rule/per-component breakdown. - The TSV/CSV header gains a PHENOTYPE column after TYPE (
-when ambiguous or uncalled); untyped cluster databases keep the untyped header byte-identically. - A rule referencing a gene/locus the database lacks fails up front with the typed
TYPING_UNKNOWN_GENEerror (also enforced atdb build --typingtime).
Untyped cluster databases (the kaptive fetches, for now) report locus calls only — the
kaptive-style output — with phenotype null.
Notes¶
- Coverage filter on the unrounded float.
%COVERAGE = 100 * (length - gaps) / slenis compared to--mincovbefore rounding, while display is%.2f. A hit at 79.996% prints as80.00but fails the default--mincov 80and is dropped. This is deliberate abricate parity, not a rounding bug. - Identity is never re-filtered.
%IDENTITYis the BLASTpident, printed as%.2f.--minidis enforced inside blastn via-perc_identity. - Dedup, no merging. BLAST rows with an identical
(contig, start, end)query span are collapsed and the first row wins. Overlapping hits at different spans are all reported; gapit does not merge overlapping intervals. This matches abricate exactly and is a feature. (The opt-in--merge-fragmentsmode above is the one sanctioned exception; the default path never merges.) - Protein databases. Databases with
dbtype prot(for examplebacmet2) screen throughblastx, which accepts no-perc_identity. gapit then prints--minid is not applied to protein databases (abricate parity)on stderr and keeps going. - Ordering. Within a file, rows sort by SEQUENCE then START, stable. Rows for one file are
printed (or flushed to
--output) once that file finishes; files emit in input order even with--jobs > 1(head-of-line: file i waits for files 1..i). - Exit codes. 2 usage, 3 missing dependency, 4 database error, 5 input error, 1 unexpected.
Failures print a
gapit.error/1JSON envelope on stderr; see ./outputs.md.