genoABO Workflow: Loading to Output¶
This document describes how genotype data moves through genoABO from file load to written results. For CLI flags and the Python API see usage.md; for inference rules see methods.md; for marker coordinates see markers.md; for limitations see assumptions.md.
Research use only. genoABO is not a clinical blood-transfusion typing tool.
Overview¶
genoABO reads four ABO tag SNPs (rs8176719, rs8176746, rs8176747, rs505922) from a cohort genotype file, detects the genome build, normalizes alleles to biological marker states, infers haplotypes and diplotypes, and writes per-sample ABO calls with QC metrics. Indexed VCF/BCF fetches those sites by region. Unindexed VCF is scanned into an in-memory cache. PGEN and BGEN parse full variant-metadata indexes, then read genotypes only for the matched markers.
The package is organized into four modules plus YAML registries:
flowchart TB
Input[Genotype file]
Readers[readers/]
Core[core/]
Methods[methods/]
CLI[cli/]
Data[data/*.yaml]
OutABO["PREFIX.abo.tsv"]
OutQC["PREFIX.qc.tsv"]
OutSum["PREFIX.summary.txt"]
OutRep["PREFIX.report.html"]
Input --> Readers
Readers --> Core
Core --> Data
Core --> Methods
Methods --> CLI
CLI --> OutABO
CLI --> OutQC
CLI --> OutSum
CLI --> OutRep
| Module | Role |
|---|---|
readers/ |
Format detection and indexed marker fetch (VCF, BGEN, PGEN, PLINK1) |
core/ |
Types, YAML registry, build detection, genotype interpretation, normalization, haplotype logic |
methods/ |
Inference methods and ABOCaller orchestration |
cli/ |
Command-line interface, output writers, citations |
data/ |
YAML single source of truth for coordinates, states, methods |
Entry points¶
Both paths run the same pipeline via ABOCaller. See Usage for the full CLI and Python API.
| Entry | Code path |
|---|---|
| CLI | genoabo DATA -o PREFIX → cli/cli_main.py → ABOCaller.call() → cli/cli_qc.py writers |
| Python API | from genoabo import ABOCaller → methods/methods_caller.py |
# Full inference + output
genoabo cohort.pgen --out abo
# Marker inspection only (stops after step 7; no .abo.tsv)
genoabo cohort.pgen --inspect
from genoabo import ABOCaller
caller = ABOCaller(method="auto", build="auto", genotype_mode="auto", phase="auto")
result = caller.call("cohort.pgen")
for call in result.calls:
print(call.sample.iid, call.abo_group, call.abo_genotype)
Pipeline stages¶
The orchestrator is ABOCaller._run() in methods/methods_caller.py. Stages run in this order:
| Step | Module | What happens |
|---|---|---|
| 1. Format detection | readers/ |
open_reader(path, format) auto-detects VCF/BCF, PGEN, PLINK1, or BGEN from file extensions and companion files (.pvar/.psam, .bim/.fam, .sample) |
| 2. Sample list | readers/ |
reader.samples() returns the cohort SampleID list (FID/IID) |
| 3. Marker probe | readers/ + core/ |
For each rsID, build a MarkerQuery from assemblies.yaml + markers.yaml and fetch using multi-key lookup (rsID, contig:pos, contig:pos:nea:ea; contigs may be 9, chr9, or NC accessions) |
| 4. Build detection | core/ |
detect_build() matches observed positions and VCF ##reference= / similar header tokens; returns build + confidence. Ambiguous results raise an error unless --build is set explicitly. Chromosome-length scoring exists in detect_build but the caller does not pass contig lengths. PGEN/BGEN headers are not assembly metadata |
| 5. Full marker fetch | readers/ |
Re-fetch all four markers using the same multi-key lookup at the resolved build |
| 6. Method selection | methods/ |
select_method() applies the auto-selection hierarchy from data/methods.yaml, or honors --method / --strict-method |
| 7. Inspection report | core/ + cli/ |
inspect_markers() builds an InspectReport (positions, call rates, recommended method). Printed with --inspect; also embedded in QC output |
| 8. Phase resolution | methods/ |
resolve_phase() — auto is cohort-wide (any phased GT → all samples phased); phased / unphased force the mode |
| 9. Per-sample normalization | core/ |
For each sample × marker: interpret_genotype() resolves hardcall from dosage/probability, then normalize_marker() maps REF/ALT → biological MarkerAlleleState using data/markers.yaml |
| 10. Inference | methods/ + core/ |
get_method(name).call_sample() applies method rules (methods/methods_infer.py); haplotype/diplotype logic lives in core/core_haplotype.py |
| 11. Output | cli/ |
write_abo_tsv, write_qc_tsv, write_summary, and write_report write the four output files |
Per-sample sub-flow¶
For each sample, marker data passes through normalization before method-specific inference:
flowchart LR
GT[GenotypeCall per marker]
Geno[interpret_genotype]
Norm[normalize_marker]
Hap[infer_haplotype]
Method[method.call_sample]
Call[ABOCall]
GT --> Geno --> Norm --> Hap --> Method --> Call
Layers are strictly separated: readers know file formats but not ABO biology; normalization knows biological states but not inference rules; methods know decision tables but not VCF/PGEN syntax.
Genotype and phase modes¶
CLI flags map to CallerConfig and affect step 9:
| Flag | Values | Behavior |
|---|---|---|
--geno-mode |
auto, hardcall, dosage, probability |
How to resolve discrete allele copies from GT, DS, or GP |
--dosage-hardcall-threshold |
float (default 0.1) | Dosage bands for 0/1/2 alt copies |
--min-genotype-prob |
float (default 0.9) | Minimum max(GP) for probability mode |
--phase |
auto, phased, unphased |
auto is cohort-wide: any phased GT switches the whole run to phased mode |
--assume-bo / --no-assume-bo |
flag (default on) | Unphased double-het (O_DEL/NON_O × A/B): assign B/O, or leave ambiguous |
In auto genotype mode, GP is used when available, else DS, else hardcall GT. GP and DS replace leftover phased GT so the requested mode wins.
Missing genotypes and dosage/GP below threshold become MISSING_MARKER with confidence=none on methods that require that marker. Conflicting A/B SNPs under three-marker become CONFLICT with confidence=low. The geno_mode column stores the requested mode (auto stays auto).
Marker identification (probe and fetch)¶
When locating variants in input files, genoABO tries keys in priority order (first hit wins):
- rsID — e.g.
rs8176719 - contig + position — all equivalent contig names for the build:
- numeric:
9:133257521 - chr-prefixed:
chr9:133257521 - RefSeq NC:
NC_000009.12:133257521(GRCh38 chr9) - contig + position + alleles —
chr:pos:nea:eafor each representation inmarkers.yaml, both orientations (e.g.9:133257521:G:GCand9:133257521:GC:G)
Contig aliases are defined per build in assemblies.yaml (contigs section). Input files are indexed under every alias derived from their CHROM field, so a VCF using NC_000009.12 matches the same marker as one using 9 or chr9.
For indexed VCF/BCF, region queries are attempted for each contig alias. Allele-aware keys disambiguate when they match. A position-only key (chr:pos) keeps the first indexed variant at that site, so rsID or allele keys are preferred when several records share a position.
Output files¶
Default CLI invocation:
writes four files (if -o is omitted, the prefix is the input path with its last suffix stripped):
| File | Contents |
|---|---|
PREFIX.abo.tsv |
One row per sample with ABO call, haplotypes, method, QC fields |
PREFIX.qc.tsv |
Cohort-level metrics: build, method, marker call rates, group counts |
PREFIX.summary.txt |
Human-readable summary of counts and marker availability |
PREFIX.report.html |
Self-contained run report (results, output, then input, methods, citations, limitations) |
--report PATH.html overrides the HTML path. Column descriptions in the report come from data/report.yaml (same schema as .abo.tsv).
Python:
from genoabo import ABOCaller
from genoabo.visualize import write_report
result = ABOCaller().call("cohort.pgen")
write_report(result, "abo.report.html")
# result.stats.n_input_samples, result.stats.n_group_A, ...
# result.config, result.input_path, result.phase_resolved
See also Examples for runnable demos across formats.
PREFIX.abo.tsv columns¶
| Column | Description |
|---|---|
FID, IID |
Sample identifiers |
ABO_group |
Phenotype: A, B, AB, or O. Unassigned is NA in the TSV |
ABO_genotype |
Diplotype label (e.g. A/O), ambiguous, or NA |
ABO_allele1, ABO_allele2 |
Sorted haplotype alleles (A < B < O) |
haplotype1, haplotype2 |
Chromosome copies when phased; sorted alleles when unphased |
method |
Inference method used |
geno_mode, phase_mode |
Requested genotype mode; phased vs unphased |
build |
Genome assembly |
confidence |
high / medium / low / none |
qc_status, qc_reason |
QC flags and explanation |
possible_diplotypes |
Compatible diplotypes when ambiguous |
ukb_haplotype |
UKB Field 23165 code (ukb method only) |
isbt_alleles |
ISBT allele IDs (extended method only) |
Example header row:
FID IID ABO_group ABO_genotype ABO_allele1 ABO_allele2 haplotype1 haplotype2 method geno_mode phase_mode build confidence qc_status qc_reason possible_diplotypes ukb_haplotype isbt_alleles
PREFIX.qc.tsv¶
Key-value metrics including n_samples, genome_build, input_format, method, per-marker call rates, group counts (n_A, n_B, …), and percentages.
PREFIX.summary.txt¶
Plain-text cohort summary with ABO group counts and marker availability — suitable for logs.
PREFIX.report.html¶
Self-contained HTML run report: ABO pie and run settings at the top, then results (sample-flow and ABO charts), output column dictionary, input settings, selected-method rules, citations, and limitations. Always written; --report only changes the path.
YAML registries¶
All coordinates and biological mappings live in src/genoabo/data/, loaded at runtime via importlib.resources:
| File | Used in | Purpose |
|---|---|---|
assemblies.yaml |
Steps 3–5 | Build aliases, chromosome lengths (registry / unused caller signal), marker coordinates per assembly |
markers.yaml |
Step 9 | Biological states and REF/ALT → state normalization rules |
methods.yaml |
Step 6 | Method metadata, required markers, auto-selection hierarchy, report logic |
alleles.yaml |
Step 10 | ISBT allele patterns (extended method only) |
citations.yaml |
CLI / report | Full bibliographic records for --method-info and the HTML report |
report.yaml |
HTML report | Pipeline narrative, column dictionary, confidence table, limitations |
Error handling and early exit¶
| Condition | Behavior |
|---|---|
Ambiguous build with --build auto |
RuntimeError; CLI exits with code 1 |
--strict-method with missing required markers |
Error for a user-selected method, or for --method auto when no entry matches; no substitution of a smaller method |
--inspect |
Pipeline succeeds after step 7; inspection printed; no .abo.tsv / .qc.tsv / .summary.txt / .report.html |
| Unreadable input format | Error from open_reader() |
When build detection conflicts (e.g. mixed GRCh37/GRCh38 signal), set --build hg19, --build hg38, or --build chm13 explicitly.
Related documentation¶
- methods.md — formal decision tables per inference method
- markers.md — coordinate provenance, biological states, and 1000 Genomes LD r²
- assumptions.md — rare-allele limits and unresolved edge cases
- Home — installation and quick start