allelix 2.5.1__tar.gz → 2.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {allelix-2.5.1/allelix.egg-info → allelix-2.5.2}/PKG-INFO +42 -32
- {allelix-2.5.1 → allelix-2.5.2}/README.md +41 -31
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/__init__.py +12 -5
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/base.py +17 -3
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/clinvar.py +73 -7
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/gnomad.py +26 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/pharmgkb.py +11 -1
- allelix-2.5.2/allelix/annotators/reference_fallback.py +209 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/rsid_identity.py +29 -6
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/snpedia.py +12 -2
- allelix-2.5.2/allelix/attribution.py +99 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/__init__.py +11 -3
- {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/_helpers.py +149 -22
- {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/_options.py +39 -11
- {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/analyze.py +1 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/config.py +15 -19
- {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/db.py +55 -12
- {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/utility.py +375 -61
- {allelix-2.5.1 → allelix-2.5.2}/allelix/config.py +30 -6
- {allelix-2.5.1 → allelix-2.5.2}/allelix/data/high_value_snps.yaml +6 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/__init__.py +12 -2
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/manager.py +192 -37
- {allelix-2.5.1 → allelix-2.5.2}/allelix/exporters/plink.py +65 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/models.py +4 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/_helpers.py +56 -1
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/twentythreeandme.py +37 -10
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/vcf.py +208 -24
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/__init__.py +63 -6
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/_pipeline.py +284 -54
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/build_text.py +27 -1
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/clinvar_text.py +50 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/diff.py +107 -46
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/high_value.py +96 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/html.py +61 -6
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/json_report.py +25 -3
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/terminal.py +2 -1
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/vcf.py +143 -43
- {allelix-2.5.1 → allelix-2.5.2}/allelix/utils/build_detect.py +15 -4
- {allelix-2.5.1 → allelix-2.5.2/allelix.egg-info}/PKG-INFO +42 -32
- {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/SOURCES.txt +2 -0
- {allelix-2.5.1 → allelix-2.5.2}/pyproject.toml +3 -3
- {allelix-2.5.1 → allelix-2.5.2}/LICENSE +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/MANIFEST.in +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/__init__.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/_resolution.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/alphamissense.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/cadd.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/annotators/gwas.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/cli/focused.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/compare.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/data/__init__.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/data/clinvar_clnsig_snapshot.yaml +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/_versions.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/alphamissense_loader.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/cache_state.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/cadd_loader.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/cpic_loader.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/gnomad_loader.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/gwas_loader.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/loader_utils.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/pharmgkb_loader.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/rsid_identity_loader.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/schema.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/snpedia_loader.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/databases/snpedia_parser.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/exporters/__init__.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/__init__.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/ancestrydna.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/base.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/decodeme.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/ftdna.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/ftdna_famfinder.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/ftdna_illumina.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/genesforgood.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/livingdna.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/myhappygenes.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/parsers/myheritage.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/py.typed +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/_sources.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/association_text.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/reports/methylation.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/utils/__init__.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/utils/allele.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix/utils/plausibility.py +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/dependency_links.txt +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/entry_points.txt +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/requires.txt +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/allelix.egg-info/top_level.txt +0 -0
- {allelix-2.5.1 → allelix-2.5.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: allelix
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.2
|
|
4
4
|
Summary: Open-source genotype analysis toolkit. Format-agnostic ingestion, database-agnostic annotation, offline-first.
|
|
5
5
|
Author: Allelix
|
|
6
6
|
Maintainer-email: dial481 <dial481@users.noreply.github.com>
|
|
@@ -105,32 +105,34 @@ See [Development](#development) for source installs and running tests, [Managing
|
|
|
105
105
|
| Format | Status | Notes |
|
|
106
106
|
|---|---|---|
|
|
107
107
|
| MyHappyGenes (Tempus) | ✓ | Tab-delimited, 5 columns. **Build is auto-detected** — real-world MHG exports mislabel the header as "build 37.1" while shipping GRCh38 coordinates. Allelix decides the build from position data and warns on header/data disagreement (ADR-0021, ADR-0044). See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#myhappygenes-tempus). |
|
|
108
|
-
| 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype. Reads build 36, 37 or 38; the header's build is used when the known-SNP positions agree. I-prefixed probe IDs passed through; they and `D`/`I` indel calls are not matched against ClinVar, and the report counts them (ADR-0051). |
|
|
108
|
+
| 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype; a copy a spreadsheet re-saved as CSV (quoted or not) or with spaces is read too ([docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#23andme)). Reads build 36, 37 or 38; the header's build is used when the known-SNP positions agree. I-prefixed probe IDs passed through; they and `D`/`I` indel calls are not matched against ClinVar, and the report counts them (ADR-0051). A `D`/`I` call at a high-value rsID, or at one ClinVar classifies Pathogenic or Likely pathogenic, is also named as not checked (gene, variant name, rsID and call) on the console, in HTML and in JSON. |
|
|
109
109
|
| Genes for Good | ✓ | The 23andMe-style `.txt` from a Genes for Good download (23andMe's columns under a PLINK banner). The imputed text file (about 7.7 million rows) and the imputed VCF are labelled imputed in every report; the measured files are not. The download's VCFs are read as VCF; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#genes-for-good). |
|
|
110
110
|
| AncestryDNA | ✓ | Tab-delimited, 5 columns. Chromosome mapping: 23→X, 24→Y, 25→X (PAR), 26→MT. V1 and V2 chip layouts. |
|
|
111
|
-
| Family Tree DNA | ✓ | CSV, double-quoted fields, concatenated genotype. Build 37 default when known-SNP positions are present but do not decide; with none at all and no build line, the run stops (ADR-0044). |
|
|
111
|
+
| Family Tree DNA | ✓ | CSV, double-quoted fields, concatenated genotype. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)); with none at all and no build line, the run stops (ADR-0044). |
|
|
112
112
|
| MyHeritage DNA | ✓ | CSV, same structure as FTDNA. Detected by "MyHeritage" in the file's preamble. Handles double-double-quoted fields and files re-saved through a spreadsheet (padded rows, signature not on line 1, tab-delimited copies). Two exports: the **array** export (pre-2026 kits) and the **low-pass WGS** export (kits from January 2026; genotypes imputed from about 2x coverage), which the report labels as imputed and whose `##reference=build37` line is used as the header build. See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#myheritage). |
|
|
113
113
|
| Living DNA | ✓ | Tab-delimited despite `.csv` extension. Handles AX-, AFFX-prefixed and CHR:POS positional SNP IDs. |
|
|
114
114
|
| deCODEme | ✓ | `deCODEme_scan.csv` (deCODE genetics, service closed 2013): comma-separated, a per-row `Strand` column; minus-strand calls are complemented to the plus strand. Build 36: decided from known-SNP positions, then run with position lookups off (ADR-0025). See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#decodeme). |
|
|
115
|
-
| FTDNA Illumina raw | ✓ | Tab-delimited variant of the FTDNA export (distinct from the CSV format above). `RSID/CHROMOSOME/POSITION/RESULT` columns. Build 37 default when known-SNP positions are present but do not decide. |
|
|
116
|
-
| FTDNA FamFinder | ✓ | Third FTDNA file shape: tab-delimited with **separate `ALLELE1` / `ALLELE2` columns** instead of a concatenated `RESULT`. Detected by the `famfinder` substring in the file header plus the 5-column canonical header. Build 37 default when known-SNP positions are present but do not decide. |
|
|
117
|
-
| VCF / gVCF | ✓ | REF/ALT encoding, `0/1` genotype notation.
|
|
115
|
+
| FTDNA Illumina raw | ✓ | Tab-delimited variant of the FTDNA export (distinct from the CSV format above). `RSID/CHROMOSOME/POSITION/RESULT` columns. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)). |
|
|
116
|
+
| FTDNA FamFinder | ✓ | Third FTDNA file shape: tab-delimited with **separate `ALLELE1` / `ALLELE2` columns** instead of a concatenated `RESULT`. Detected by the `famfinder` substring in the file header plus the 5-column canonical header. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)). |
|
|
117
|
+
| VCF / gVCF | ✓ | REF/ALT encoding, `0/1` genotype notation. A homozygous-reference call (`0/0`) or a gVCF reference block (lines with `<NON_REF>` ALT and `END=` INFO) carries no variant allele, so it usually matches nothing. The exception is a site where the reference genome itself carries a ClinVar allele (Factor V Leiden on GRCh37, for example): there a `0/0` call, or a reference block that passes the genotype-quality gate, is assessed as homozygous for that allele. See [docs/formats.md, Reference calls in VCFs](https://github.com/allelix/allelix/blob/main/docs/formats.md#reference-calls-in-vcfs) (ADR-0039, ADR-0059). At high-value positions, a block with low genotype quality or no reads makes the position a no-call, and so does a gVCF position no record covers. High-value no-calls list the reason (for example `low_gq_block`, `no_coverage`, `filtered`, `low_gq_call`). A record counts for a high-value or panel rsID when it is at one of the rsID's identity placements with that placement's REF, whatever its ID or ALT; an rsID the identity table does not place is matched by ID (ADR-0055). A plain VCF has no reference blocks, so a position without a record is reported as unknown rather than as reference or as a no-call; at such a ClinVar site the report counts the positions no record covers, and names the high-value ones (such as CYP3A5*3 rs776746 on GRCh37) as not checked. Multi-sample files require `--sample <ID>`. Rows the variant caller rejected (FILTER other than `PASS` or `.`) are skipped and counted in the report; `--keep-filtered` keeps them. A call with genotype quality below `--min-gq` (default 20) or with no reads (DP 0) is reported as a no-call. The spanning-deletion allele `*` is treated as symbolic: such calls are not assessed, and the report counts them and lists their positions. A half call (`./1`) is assessed on its called allele by ClinVar and the GWAS Catalog and shown as `T/.` with zygosity "Half call"; ClinPGx and SNPedia, which need both alleles, skip it. Malformed rows, rows without a genotype, genotypes naming an allele the record lacks and genotypes naming a symbolic allele such as `<NON_REF>` beside a real ALT are counted in the report; a no-call written `./.` stays a no-call (ADR-0047). A VCF that holds only structural-variant or copy-number records (such as a provider's separate `sv` / `cnv` file) is rejected with a message naming the file to use instead; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#sequencingcom-30x-whole-genome) for per-provider deliverables. A VCF whose header declares an imputation tool's fields (Minimac, Beagle, GLIMPSE) is labelled imputed in every report; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#imputed-vcfs). Streams via stdlib; `.vcf.gz` handled transparently. Optional `pip install allelix[vcf-index]` enables pysam-backed tabix random access for fast `extract --snps` on huge VCFs. |
|
|
118
118
|
|
|
119
119
|
Files can be passed as delivered: gzipped (Family Tree DNA's `.csv.gz`, for example), or a zip holding one genotype file. A zip with several files, a password or an unusual compression stops with a message to unzip it first. Windows, Linux and old-Mac line endings are read; a leading byte order mark or blank line is ignored.
|
|
120
120
|
|
|
121
|
-
If Allelix does not recognise a file, use the original file from your testing company. `--format <name>` forces a reader at your own risk; every report
|
|
121
|
+
If Allelix does not recognise a file, use the original file from your testing company. `--format <name>` forces a reader at your own risk; when that reader does not recognise the file, every report carries a warning (see [docs/formats.md, Forcing a format](https://github.com/allelix/allelix/blob/main/docs/formats.md#forcing-a-format)).
|
|
122
122
|
|
|
123
123
|
Adding a new format means adding one file to `allelix/parsers/` and registering an instance in the `PARSERS` list in `allelix/parsers/__init__.py`.
|
|
124
124
|
|
|
125
125
|
### Roadmap
|
|
126
126
|
|
|
127
|
+
Shipped releases are listed for history; the [Changelog](https://github.com/allelix/allelix/blob/main/CHANGELOG.md) has each release's changes.
|
|
128
|
+
|
|
127
129
|
| Release | Theme |
|
|
128
130
|
|---|---|
|
|
129
|
-
| **v2.2** | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
|
|
130
|
-
| **v2.3** | Annotated VCF output plus v2.3.1 correctness hardening: cross-build enrichment guards, GWAS effect-type disambiguation, conservative build confidence, SNPedia orientation handling, and strict slow-suite drift verification. |
|
|
131
|
-
| **v2.4** | Correctness hardening: cross-build rsID identity table with explicit allele translation, plausibility flag for genotyping-artifact detection, VCF quality gate honouring FILTER and genotype quality, unified rsID-recovery step across all commands, and parser/format hygiene. |
|
|
132
|
-
| **v2.5** | ClinVar's own classification caps each row; ClinVar variants without an rsID; VCF calls counted, not dropped; read-only caches and named sources in every report; zip input; Genes for Good and deCODEme; imputed input labelled. |
|
|
133
|
-
| **Later** | Variant consequences, CNVs and repeat expansions, gnomAD genomes, supplemental genotype-file merging, ancestry estimation, and PRS support. These are directional themes rather than release commitments. |
|
|
131
|
+
| **v2.2** (shipped) | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
|
|
132
|
+
| **v2.3** (shipped) | Annotated VCF output plus v2.3.1 correctness hardening: cross-build enrichment guards, GWAS effect-type disambiguation, conservative build confidence, SNPedia orientation handling, and strict slow-suite drift verification. |
|
|
133
|
+
| **v2.4** (shipped) | Correctness hardening: cross-build rsID identity table with explicit allele translation, plausibility flag for genotyping-artifact detection, VCF quality gate honouring FILTER and genotype quality, unified rsID-recovery step across all commands, and parser/format hygiene. |
|
|
134
|
+
| **v2.5** (shipped) | ClinVar's own classification caps each row; ClinVar variants without an rsID; VCF calls counted, not dropped; read-only caches and named sources in every report; zip input; Genes for Good and deCODEme; imputed input labelled. |
|
|
135
|
+
| **Later** (planned) | Variant consequences, CNVs and repeat expansions, gnomAD genomes, supplemental genotype-file merging, ancestry estimation, and PRS support. These are directional themes rather than release commitments. |
|
|
134
136
|
|
|
135
137
|
Issues and milestones are tracked at [github.com/allelix/allelix/issues](https://github.com/allelix/allelix/issues).
|
|
136
138
|
|
|
@@ -138,15 +140,15 @@ Issues and milestones are tracked at [github.com/allelix/allelix/issues](https:/
|
|
|
138
140
|
|
|
139
141
|
| Database | Status | Notes |
|
|
140
142
|
|---|---|---|
|
|
141
|
-
| ClinVar (GRCh37 + GRCh38) | ✓ | Public domain (NCBI). SNVs + indels + multi-allelic sites. **Both builds cached**; `analyze` dispatches by the decided build (ADR-0044). Rows are matched by chromosome, position, REF and ALT, never by rsID alone,
|
|
142
|
-
| ClinPGx (formerly PharmGKB) | ✓ | CC BY-SA 4.0. Clinical annotations only — single-rsid SNVs; star alleles and haplotypes deferred (ADR-0009). **Primary non-finding filter is the ClinVar REF carrier rule (ADR-0023):**
|
|
143
|
+
| ClinVar (GRCh37 + GRCh38) | ✓ | Public domain (NCBI). SNVs + indels + multi-allelic sites. **Both builds cached**; `analyze` dispatches by the decided build (ADR-0044). Rows are matched by chromosome, position, REF and ALT, never by rsID alone. A record written REF = ALT, where this build's reference itself carries the record's allele (Factor V Leiden on GRCh37, or a record for the normal allele), is reported for a user who carries that allele; Pathogenic and Likely pathogenic classifications of a normal allele are dropped (ADR-0039). Carrier rule (ADR-0007) requires the user to carry the ALT allele. Indel-anchor protection (ADR-0011) prevents single-base array readouts from matching anchor-base indels. Array calls ClinVar cannot match are counted in the report: vendor probes without an rsID at a locus ClinVar classifies as Pathogenic or Likely pathogenic, and `D`/`I` calls (ADR-0051). A `D`/`I` call at a high-value or Pathogenic / Likely pathogenic rsID is named as not checked, not only counted; it is still not interpreted. Variants that ClinVar's Conflicting or Benign classification scored down (ADR-0045) are named with their submitter split. ClinVar variants without an rsID are matched for VCF input only, where REF comes from the file (ADR-0046). A VCF allele written with bases it shares with REF (`GTT>GT,G`, `CA>TA`) is trimmed per carried allele before matching, by ClinPGx, SNPedia and the GWAS Catalog as well as ClinVar, and a VCF homozygous deletion to one base matches; indels are not left-aligned, so one the file places at a different position from ClinVar's still does not match (ADR-0047). |
|
|
144
|
+
| ClinPGx (formerly PharmGKB) | ✓ | CC BY-SA 4.0. Clinical annotations only — single-rsid SNVs; star alleles and haplotypes deferred (ADR-0009). **Primary non-finding filter is the ClinVar REF carrier rule (ADR-0023):** where ClinVar for the input's build is installed and places the rsID at the call's position, a user homozygous for the normal allele ClinVar resolves there is not reported. That is usually ClinVar's single-base REF. The exception is a site where this build's reference itself carries ClinVar's variant allele (a REF = ALT row, F5 rs6025 on GRCh37): there the normal allele is the other build's REF, so a TT call is reported as a carrier and CC is the non-finding. Where that normal allele cannot be told, nothing is suppressed. Without that cache, only sites where the two builds' references differ (F5 rs6025) are decided: by the other build's ClinVar, else by gnomAD's major allele; if neither can tell, the row shows. Every other site is then not suppressed. CPIC's `(rsid, base) → function_class` join (ADR-0020) survives as a secondary tier for rsids ClinVar doesn't catalog. Earlier prose tiers (ADR-0013, ADR-0017, ADR-0018) are superseded. |
|
|
143
145
|
| CPIC (per-allele function table) | ✓ | Internal data source for the ClinPGx filter. Fetched from `api.cpicpgx.org` at `db update` time. Used to populate the `pharmgkb_allele_function` table — not surfaced to end users as its own annotator. |
|
|
144
146
|
| SNPedia | ✓ | CC BY-NC-SA 3.0 US. Pre-built cache downloaded via `db update` (~216K wiki pages, ~105K genotype rows). The cache carries SNPedia's orientation metadata: minus-strand alleles are complemented before matching. Single-base genotypes at rsIDs are then checked against the site's forward alleles in the rsID identity table: a page's stated orientation must put the genotype among them, an unknown orientation is read directly or else complemented, and A/T and C/G sites abstain. Sites that abstain are counted in the report. If the database is absent, analysis runs without it. For commercial use, pass `--exclude-snpedia`. The cache can also be rebuilt from source via `scripts/scrape_snpedia.py` + `scripts/parse_snpedia.py`. |
|
|
145
147
|
| GWAS Catalog | ✓ | Public domain (EBI/NHGRI). Trait–SNP associations with p-values and effect sizes. Carrier rule (ADR-0007) requires the user to carry the risk allele. The reported allele is the study's effect (risk) allele, which can be the reference allele (ADR-0039). P-value magnitude scoring maps continuous p-values to the 0–10 scale; only a confidence-interval field that positively identifies a bare odds ratio receives the odds-ratio modifier. Beta coefficients and ambiguous or unit-bearing effects receive no modifier (ADR-0038). Unknown-risk-allele entries fire on rsID match alone but are capped at 3.0. Risk alleles are read on the forward strand against the site's alleles in the rsID identity table: as written, else complemented; A/T and C/G sites and rsIDs the table does not place at the call abstain, and the report counts them. Every row says whether the effect allele raises or lowers the odds or the measured value (JSON `effect_direction`). GWAS rows must meet the larger of `--min-magnitude` and `--gwas-min-magnitude` (default 9.0); the must-include rsIDs skip the GWAS floor (ADR-0024). |
|
|
146
148
|
| gnomAD | ✓ | ODbL v1.0. **GRCh38-only enrichment annotator** — adds population allele frequency context to existing annotations. Shows how common each variant is in the general population (~57M exome variants with an rsID, from 730K individuals). A pathogenic variant that 35% of people carry reads very differently from one seen in 0.001%. Pre-built cache downloaded via `db update` (~7 GB on disk). GRCh37 input is enriched only through the rsID identity table's explicit allele translation (ADR-0040); gnomAD's own rsID resolution stays GRCh38-only, and GRCh36 input receives nothing. Use `--no-gnomad` to skip. |
|
|
147
149
|
| AlphaMissense | ✓ | CC BY 4.0. **GRCh38-only enrichment annotator** — adds DeepMind's protein-structure-based pathogenicity predictions to existing annotations. Scores 71M missense variants on a 0–1 scale: <0.34 = likely benign, >0.564 = likely pathogenic. Complements ClinVar's expert classifications with computational predictions — especially valuable for variants ClinVar hasn't reviewed yet. Pre-built cache downloaded via `db update` (~8GB on disk). GRCh37 input is enriched only through the rsID identity table (ADR-0040); GRCh36 input receives nothing. Use `--no-alphamissense` to skip. |
|
|
148
150
|
| CADD | ✓ | LicenseRef-CADD (non-commercial). **GRCh38-only enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7. **Opt-in**, disabled by default; GRCh37 input is scored only through the rsID identity table (ADR-0040) and GRCh36 input never; see [CADD modes](#cadd-modes) below for details. |
|
|
149
|
-
| dbSNP rsID identity | ✓ | Public domain (NCBI dbSNP). Not an annotator: a cross-build table of each annotator rsID's GRCh37 and GRCh38 placement and alleles (ADR-0040). Supplies the reference allele for GRCh37 and GRCh38 array data, recovers rsIDs for rsID-less VCFs on both builds, and translates a GRCh37 allele to its GRCh38 coordinates so gnomAD, AlphaMissense and CADD can be looked up. Strand-inverted loci are translated explicitly
|
|
151
|
+
| dbSNP rsID identity | ✓ | Public domain (NCBI dbSNP). Not an annotator: a cross-build table of each annotator rsID's GRCh37 and GRCh38 placement and alleles (ADR-0040). Supplies the reference allele for GRCh37 and GRCh38 array data, recovers rsIDs for rsID-less VCFs on both builds, and translates a GRCh37 allele to its GRCh38 coordinates so gnomAD, AlphaMissense and CADD can be looked up. Strand-inverted loci are translated explicitly. A row the table flags palindromic (its dbSNP allele set equals its own complement: A/T, C/G, or all four bases) abstains where the GRCh37 and GRCh38 reference bases differ, and translates as written where they are equal; rows the table does not flag translate by their alleles. Alleles that became the GRCh38 reference abstain. Pre-built cache downloaded via `db update` (~4.9M rows covering ~3.0M rsIDs, from dbSNP b157). |
|
|
150
152
|
|
|
151
153
|
### CADD modes
|
|
152
154
|
|
|
@@ -157,26 +159,26 @@ CADD ranks how deleterious any single-nucleotide variant is using 100+ annotatio
|
|
|
157
159
|
Two modes:
|
|
158
160
|
|
|
159
161
|
- **Cache mode** (default): pre-built ~5.8 GB SQLite cache (~120M GRCh38 variant keys). Covers nearly every GRCh38 position allelix can annotate from its other databases (gnomAD, AlphaMissense, ClinVar). GRCh37 input is scored only after translation through the rsID identity table; GRCh36 input never is.
|
|
160
|
-
- **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only).
|
|
162
|
+
- **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). It scores variants the cache lacks, such as novel or private variants in WGS/WES data. CADD adds scores to findings from the other sources; it never makes a finding of its own, so a variant no other source reports gains nothing. Requires `pip install allelix[cadd]` for the pysam dependency, and CADD's files, downloaded separately from [cadd.gs.washington.edu/download](https://cadd.gs.washington.edu/download) (`db update --cadd` installs only the SQLite cache): `whole_genome_SNVs.tsv.gz` (about 81 GB) and its `.tbi` index in the data directory, plus, optionally for indels, `gnomad.genomes.r4.0.indel.tsv.gz` (about 1.2 GB) and its `.tbi`.
|
|
161
163
|
|
|
162
164
|
If your input is a genotyping chip file, cache mode is normally all you need. GRCh37 chip files are scored through the rsID identity table; GRCh36 files are not scored.
|
|
163
165
|
|
|
164
166
|
### Build coverage asymmetry (GRCh37 vs GRCh38)
|
|
165
167
|
|
|
166
|
-
ClinVar dispatches per build and ships with both GRCh37 and GRCh38 caches. gnomAD v4.1 exomes, AlphaMissense, and CADD v1.7 are GRCh38 datasets. Every enrichment lookup is made at GRCh38 coordinates by chromosome, position, reference and alternate allele, never by rsID. For GRCh37 input, the cross-build rsID identity table (ADR-0040) translates the allele the user carries at its GRCh37 position into the matching GRCh38 allele, written explicitly on the GRCh38 forward strand where the reference strand was inverted between assemblies.
|
|
168
|
+
ClinVar dispatches per build and ships with both GRCh37 and GRCh38 caches. gnomAD v4.1 exomes, AlphaMissense, and CADD v1.7 are GRCh38 datasets. Every enrichment lookup is made at GRCh38 coordinates by chromosome, position, reference and alternate allele, never by rsID. For GRCh37 input, the cross-build rsID identity table (ADR-0040) translates the allele the user carries at its GRCh37 position into the matching GRCh38 allele, written explicitly on the GRCh38 forward strand where the reference strand was inverted between assemblies. A row the identity table flags palindromic (A/T, C/G, or all four bases in dbSNP, whose strand cannot be told from sequence) whose reference base differs between the builds, and an allele that is the GRCh38 reference, receive no values rather than a guess. Without the identity table installed, GRCh37 analyses receive no gnomAD, AlphaMissense or CADD values; GRCh36 analyses never do.
|
|
167
169
|
|
|
168
170
|
Position-based rsID recovery for rsID-less VCFs uses ClinVar (on the selected build), the identity table (on GRCh37 and GRCh38), and gnomAD (GRCh38 only). The same recovery step runs for `analyze`, `stats`, `extract` and `export plink`, so high-value warnings, extracted genotypes and PLINK allele coding all see the recovered rsIDs (ADR-0043). The identity table covers the rsIDs the annotators hold, so an rsID-less GRCh37 VCF now recovers ClinPGx, GWAS Catalog and SNPedia rsIDs as well as ClinVar ones. If you convert between assemblies, re-normalize REF/ALT as part of the conversion and pass `--build` explicitly when the file does not carry a trustworthy build signal.
|
|
169
171
|
|
|
170
|
-
### Known ClinPGx limitation:
|
|
172
|
+
### Known ClinPGx limitation: normal-genotype rows without usable normal-allele evidence
|
|
171
173
|
|
|
172
|
-
ADR-0022 + ADR-0023: a tiny residual of ClinPGx rows may appear in reports even when the user
|
|
174
|
+
ADR-0022 + ADR-0023: a tiny residual of ClinPGx rows may appear in reports even when the user carries the normal genotype. ClinPGx publishes one annotation per genotype including the normal homozygote, and for that row to be suppressed Allelix needs usable evidence of the normal allele from one of:
|
|
173
175
|
|
|
174
|
-
- **ClinVar's
|
|
176
|
+
- **ClinVar's normal allele** (the primary filter — see ADR-0023): usually its single-base REF, or the other build's REF where this build's reference carries ClinVar's variant allele (F5 rs6025 on GRCh37). Covers an rsID that ClinVar for the input's build places at the call's position. Without that cache, only a site whose reference differs between the builds is decided (by the other build's ClinVar, else gnomAD's major allele), and anything else shows.
|
|
175
177
|
- **CPIC's per-allele function table** (the secondary fallback — see ADR-0020). Covers rsIDs CPIC has classified.
|
|
176
178
|
|
|
177
|
-
For the rare rsID where ClinPGx has an annotation
|
|
179
|
+
So without ClinVar or CPIC data, gnomAD can still decide a site whose reference differs between the builds, at that placement only. For the rare rsID where ClinPGx has an annotation and none of these gives a usable normal allele (no ClinVar or CPIC data, and no decision from the other build's ClinVar or gnomAD, which abstains on a tie, an unknown frequency or an unlisted site), the row emits: Allelix shows it rather than guess. These are identifiable by a homozygous-reference genotype combined with "decreased risk," "may have a typical response," or similar comparative language. They are an upstream data gap, not an Allelix bug — we surface them honestly rather than hide them behind a curated exclusion list (which would recreate the maintenance trap the v0.5–v0.7 prose filters were trying to escape).
|
|
178
180
|
|
|
179
|
-
The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the ADR-0023 ClinVar REF check: CPIC's CFTR vocabulary (`"ivacaftor responsive"`) doesn't match the four-class enum the secondary tier expects, but ClinVar publishes REF for
|
|
181
|
+
The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the ADR-0023 ClinVar REF check: CPIC's CFTR vocabulary (`"ivacaftor responsive"`) doesn't match the four-class enum the secondary tier expects, but ClinVar publishes REF for the CFTR rsIDs, so with ClinVar for the input's build installed the primary tier catches them.
|
|
180
182
|
|
|
181
183
|
### Known ClinVar upstream data quality issues
|
|
182
184
|
|
|
@@ -254,7 +256,7 @@ The HTML report shows a ⚠ icon next to a flagged genotype, with the reason on
|
|
|
254
256
|
|
|
255
257
|
A call is not assessed when gnomAD has no frequency for the allele. Allelix's gnomAD cache covers exome sites with an rsID, so a missing value says nothing about rarity. Calls are also not assessed when the gnomAD database is skipped. Homozygous calls on X are not assessed because a male's single X reads as homozygous; heterozygous X calls are. A call the file itself declares as one copy (a one-letter array call such as 23andMe's male X calls, or a one-index VCF genotype) is shown as Hemizygous and assessed as one copy. Y and mitochondrial calls are not assessed. Confirm a flagged call with a clinical-grade test before relying on it.
|
|
256
258
|
|
|
257
|
-
When three or more variants on chromosomes 1 to 22 are called homozygous for an allele ClinVar classifies as Pathogenic or Likely pathogenic, and are rare in gnomAD or
|
|
259
|
+
When three or more variants on chromosomes 1 to 22 are called homozygous for an allele ClinVar classifies as Pathogenic or Likely pathogenic, and are rare in gnomAD or were looked up there and are absent, the report warns at the top. A call gnomAD was not asked about (gnomAD not installed, or a GRCh37 allele the identity table cannot translate) does not count. This is counted before any display filter, so `--min-magnitude` cannot hide it (ADR-0050).
|
|
258
260
|
|
|
259
261
|
### Database sizes and download times
|
|
260
262
|
|
|
@@ -285,6 +287,7 @@ Allelix keeps its caches in `~/.local/share/allelix/`, or `$XDG_DATA_HOME/alleli
|
|
|
285
287
|
├── cadd.sqlite ~5.8 GB (only if you opted into --cadd)
|
|
286
288
|
├── clinvar.GRCh37.sqlite ~1.2 GB
|
|
287
289
|
├── clinvar.GRCh38.sqlite ~1.2 GB
|
|
290
|
+
├── clinvar-build.log each ClinVar build's source hashes and counts
|
|
288
291
|
├── gwas_catalog_associations.tsv ~745 MB
|
|
289
292
|
├── rsid_identity.sqlite ~457 MB
|
|
290
293
|
├── gwas.sqlite ~202 MB
|
|
@@ -323,7 +326,7 @@ du -sh "$ALLELIX_DATA"
|
|
|
323
326
|
allelix db path --check
|
|
324
327
|
```
|
|
325
328
|
|
|
326
|
-
**The one thing worth backing up: `config.toml`.** It lives inside the resolved data directory (typically `~/.local/share/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings.
|
|
329
|
+
**The one thing worth backing up: `config.toml`.** It lives inside the resolved data directory (typically `~/.local/share/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. The caches will rebuild on the next `db update`; `config.toml` won't. `allelix db clean` always preserves it. `clinvar-build.log` (the source hashes and counts of past ClinVar builds) is not reproducible either, and `db clean` removes it: copy it elsewhere first if you want to keep that history.
|
|
327
330
|
|
|
328
331
|
To fully uninstall allelix and reclaim everything:
|
|
329
332
|
|
|
@@ -422,11 +425,11 @@ The banner says where the build came from, for example `Build: GRCh38 (from the
|
|
|
422
425
|
- a VCF has no contig lengths, no assembly and too few known-SNP positions;
|
|
423
426
|
- a chip states no build and carries none of the known-SNP positions at all (for example an X-chromosome-only export, since they are all on autosomes);
|
|
424
427
|
- a file states a build but carries none of the known-SNP positions to check it (for example an mtDNA-only export, or a small panel VCF without contig lengths);
|
|
425
|
-
- the file has known-SNP positions but none of them sits at any supported build's coordinate, whether or not it states a build (contig lengths, when a VCF declares them, still decide);
|
|
428
|
+
- the file has known-SNP positions but none of them sits at any supported build's coordinate, whether or not it states a build (contig lengths, when a VCF declares them, still decide). Choosing a build is not the fix here: the message says the positions cannot be trusted on any build and asks you to check that this is the original file from the testing company. If you force a build anyway with `--build`, the run goes ahead but prints a **Build warning** naming the evidence (0 of N known-SNP positions match), and every report keeps it (JSON `report_warnings` code `build_override_unmatched`);
|
|
426
429
|
- contig lengths decided, but the known-SNP positions met while annotating point to the other build (for example a lifted-over file that kept its old header). The message says the annotation pass ran; nothing is written;
|
|
427
430
|
- `--build grch37` or `--build grch38` was given, but the file's contig lengths, or three or more known-SNP positions with an 80% majority, say the other build. The message names both builds. Contrary positions that fall short of that (fewer than three, or under an 80% majority) only print a **Build warning**, and so does a tie that leaves out the forced build. `--build grch36` is never checked, because it turns position-keyed lookups off.
|
|
428
431
|
|
|
429
|
-
|
|
432
|
+
Otherwise the message names the evidence and asks you to say which build the file uses: `--build grch37` or `--build grch38`, or `--build grch36` for an old GRCh36 / hg18 file, which then runs with position-keyed lookups off (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md)). For `compare`, use `--build1` / `--build2`. A stop leaves no report, annotated VCF, diff, PLINK files or extract table behind. A forced build is recorded as such in every report.
|
|
430
433
|
|
|
431
434
|
### Keeping input and output files safe
|
|
432
435
|
|
|
@@ -435,13 +438,19 @@ must differ from the input, including symlink and hard-link aliases. These
|
|
|
435
438
|
checks run before cache setup or output creation. Inputs that alias the former
|
|
436
439
|
fixed report or PLINK `.tmp` path are also refused; unrelated old temp files
|
|
437
440
|
are left untouched. Annotated VCF still refuses any existing output at the CLI.
|
|
441
|
+
Two outputs of one run must name different files too: `--output` and
|
|
442
|
+
`--vcf-out`, and PLINK's `.bed`, `.bim` and `.fam`, are refused before any work
|
|
443
|
+
when they resolve to one file (another spelling, a symlink, a linked folder or a
|
|
444
|
+
hardlink).
|
|
438
445
|
|
|
439
446
|
Writers use unique temporary files in the destination directory and publish
|
|
440
447
|
only complete, closed content. Failure before publication preserves previous
|
|
441
448
|
outputs. PLINK stages all three files first, then publishes each atomically;
|
|
442
449
|
a later publication failure can leave complete new files next to unchanged old
|
|
443
450
|
ones. Existing ordinary permission bits are retained when replacement is
|
|
444
|
-
permitted; new staging files start private
|
|
451
|
+
permitted; new staging files start private, and a new output file gets the mode
|
|
452
|
+
your umask gives any new file (`0666 & ~umask`, so `0644` under `022`).
|
|
453
|
+
Ownership, ACLs, extended attributes,
|
|
445
454
|
crash durability and concurrent path swaps are outside this guarantee.
|
|
446
455
|
|
|
447
456
|
### Is my database stale? How fresh is the data?
|
|
@@ -478,13 +487,13 @@ Notable load-bearing ADRs:
|
|
|
478
487
|
- **ADR-0042 — VCF quality gate.** The variant caller's FILTER and genotype quality are honoured, low-quality reference blocks are no-calls, and every exclusion is counted in the report.
|
|
479
488
|
- **ADR-0043 — One rsID-recovery step.** Every command that needs rsIDs recovers them for rsID-less input through the same shared step; build detection votes by position instead.
|
|
480
489
|
- **ADR-0044 — Evidence before labels.** The build is decided before annotation from contig lengths or whole-file known-SNP positions; evidence that contradicts the label, or no usable evidence, stops the run instead of producing a report on the wrong build.
|
|
481
|
-
- **ADR-0045: ClinVar's own classification.** A ClinVar row is capped by ClinVar's aggregate classification of the variant (Conflicting, Benign, Likely benign), with the submitter split shown; outranked P/LP submissions are kept by ClinVar's tiered rule where the aggregate is not a disease call.
|
|
490
|
+
- **ADR-0045: ClinVar's own classification.** A ClinVar row is capped by ClinVar's aggregate classification of the variant (Conflicting, Benign, Likely benign), with the submitter split shown, and the variants scored down are named on the console, in HTML and in JSON (`clinvar.scored_down_variants`); outranked P/LP submissions are kept by ClinVar's tiered rule where the aggregate is not a disease call.
|
|
482
491
|
- **ADR-0046: ClinVar variants without an rsID.** Kept in the cache and matched for VCF input only, by locus and the file's REF; reports name such a finding by `chrom:pos ref>alt`.
|
|
483
|
-
- **ADR-0047: VCF calls no longer dropped silently.** Untrimmed alleles are trimmed per carried allele, half calls are assessed by the sources that need only a carried allele, VCF homozygous deletions to one base match ClinVar, and dropped calls are counted with their reason (a genotype on a gVCF reference block's own `<NON_REF>` is not checked).
|
|
492
|
+
- **ADR-0047: VCF calls no longer dropped silently.** Untrimmed alleles are trimmed per carried allele (by every annotator since 2.5.2), half calls are assessed by the sources that need only a carried allele, VCF homozygous deletions to one base match ClinVar, and dropped calls are counted with their reason (a genotype on a gVCF reference block's own `<NON_REF>` is not checked).
|
|
484
493
|
- **ADR-0048: Caches are read-only at analyze time.** With `--no-update`, `analyze` writes nothing to the data directory; every cache opens read-only, installs in rollback-journal mode, and a skipped source is named with its reason (missing, outdated, unreadable, damaged, or WAL mode from an older Allelix). Local migrations run in the update step.
|
|
485
494
|
- **ADR-0049: Every report names its sources.** JSON `sources` gives every registered source's part in the run (used, ready but not used, or skipped with a reason); `report_warnings` carries report-level warnings, starting with ClinVar not used; the annotated VCF names skipped sources.
|
|
486
495
|
- **ADR-0050: Zygosity codes and haploid calls.** JSON zygosity and plausibility are codes, a call the file declares haploid is shown as hemizygous, "not assessed" carries a reason, and three rare homozygous P/LP calls give a report warning.
|
|
487
|
-
- **ADR-0051: Array calls ClinVar cannot match are counted.** Vendor probes at P/LP loci and `D`/`I` calls are counted in the report, and panel coverage puts each requested rsID in one of four states.
|
|
496
|
+
- **ADR-0051: Array calls ClinVar cannot match are counted.** Vendor probes at P/LP loci and `D`/`I` calls are counted in the report; since 2.5.2, a `D`/`I` call at a high-value or P/LP rsID is also named as not checked (JSON `high_value_unchecked` and `report_warnings` code `di_calls_not_checked`), and panel coverage puts each requested rsID in one of four states.
|
|
488
497
|
- **ADR-0052: Zero-star P/LP on a common allele.** A ClinVar P/LP row without assertion criteria whose allele has a gnomAD frequency of 5% or more is scored down, marked and counted.
|
|
489
498
|
|
|
490
499
|
Release history: see [`CHANGELOG.md`](https://github.com/allelix/allelix/blob/main/CHANGELOG.md).
|
|
@@ -500,12 +509,13 @@ pre-commit install --hook-type pre-commit
|
|
|
500
509
|
|
|
501
510
|
ruff check .
|
|
502
511
|
ruff format --check .
|
|
503
|
-
pytest
|
|
512
|
+
pytest -m "not slow and not integration" # the fast suite CI runs
|
|
513
|
+
pytest # every test, including slow and integration
|
|
504
514
|
```
|
|
505
515
|
|
|
506
516
|
The pre-commit hook enforces `ruff check` + `ruff format --check`. If a commit is blocked, fix the underlying problem rather than skipping the hook.
|
|
507
517
|
|
|
508
|
-
For the full real-data release-validation battery — every parser format, VCF/gVCF, PLINK round-trip, edge cases, the GIAB/1000G corpus — see [`test_data/FULL_TEST_PROTOCOL.md`](https://github.com/allelix/allelix/blob/main/test_data/FULL_TEST_PROTOCOL.md). That document is the project's real-data testing story;
|
|
518
|
+
For the full real-data release-validation battery — every parser format, VCF/gVCF, PLINK round-trip, edge cases, the GIAB/1000G corpus — see [`test_data/FULL_TEST_PROTOCOL.md`](https://github.com/allelix/allelix/blob/main/test_data/FULL_TEST_PROTOCOL.md). That document is the project's real-data testing story; `pytest -m "not slow and not integration"` is the fast suite that gates every commit, as in CI.
|
|
509
519
|
|
|
510
520
|
## License
|
|
511
521
|
|
|
@@ -65,32 +65,34 @@ See [Development](#development) for source installs and running tests, [Managing
|
|
|
65
65
|
| Format | Status | Notes |
|
|
66
66
|
|---|---|---|
|
|
67
67
|
| MyHappyGenes (Tempus) | ✓ | Tab-delimited, 5 columns. **Build is auto-detected** — real-world MHG exports mislabel the header as "build 37.1" while shipping GRCh38 coordinates. Allelix decides the build from position data and warns on header/data disagreement (ADR-0021, ADR-0044). See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#myhappygenes-tempus). |
|
|
68
|
-
| 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype. Reads build 36, 37 or 38; the header's build is used when the known-SNP positions agree. I-prefixed probe IDs passed through; they and `D`/`I` indel calls are not matched against ClinVar, and the report counts them (ADR-0051). |
|
|
68
|
+
| 23andMe | ✓ | Tab-delimited, 4 columns, concatenated genotype; a copy a spreadsheet re-saved as CSV (quoted or not) or with spaces is read too ([docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#23andme)). Reads build 36, 37 or 38; the header's build is used when the known-SNP positions agree. I-prefixed probe IDs passed through; they and `D`/`I` indel calls are not matched against ClinVar, and the report counts them (ADR-0051). A `D`/`I` call at a high-value rsID, or at one ClinVar classifies Pathogenic or Likely pathogenic, is also named as not checked (gene, variant name, rsID and call) on the console, in HTML and in JSON. |
|
|
69
69
|
| Genes for Good | ✓ | The 23andMe-style `.txt` from a Genes for Good download (23andMe's columns under a PLINK banner). The imputed text file (about 7.7 million rows) and the imputed VCF are labelled imputed in every report; the measured files are not. The download's VCFs are read as VCF; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#genes-for-good). |
|
|
70
70
|
| AncestryDNA | ✓ | Tab-delimited, 5 columns. Chromosome mapping: 23→X, 24→Y, 25→X (PAR), 26→MT. V1 and V2 chip layouts. |
|
|
71
|
-
| Family Tree DNA | ✓ | CSV, double-quoted fields, concatenated genotype. Build 37 default when known-SNP positions are present but do not decide; with none at all and no build line, the run stops (ADR-0044). |
|
|
71
|
+
| Family Tree DNA | ✓ | CSV, double-quoted fields, concatenated genotype. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)); with none at all and no build line, the run stops (ADR-0044). |
|
|
72
72
|
| MyHeritage DNA | ✓ | CSV, same structure as FTDNA. Detected by "MyHeritage" in the file's preamble. Handles double-double-quoted fields and files re-saved through a spreadsheet (padded rows, signature not on line 1, tab-delimited copies). Two exports: the **array** export (pre-2026 kits) and the **low-pass WGS** export (kits from January 2026; genotypes imputed from about 2x coverage), which the report labels as imputed and whose `##reference=build37` line is used as the header build. See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#myheritage). |
|
|
73
73
|
| Living DNA | ✓ | Tab-delimited despite `.csv` extension. Handles AX-, AFFX-prefixed and CHR:POS positional SNP IDs. |
|
|
74
74
|
| deCODEme | ✓ | `deCODEme_scan.csv` (deCODE genetics, service closed 2013): comma-separated, a per-row `Strand` column; minus-strand calls are complemented to the plus strand. Build 36: decided from known-SNP positions, then run with position lookups off (ADR-0025). See [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#decodeme). |
|
|
75
|
-
| FTDNA Illumina raw | ✓ | Tab-delimited variant of the FTDNA export (distinct from the CSV format above). `RSID/CHROMOSOME/POSITION/RESULT` columns. Build 37 default when known-SNP positions are present but do not decide. |
|
|
76
|
-
| FTDNA FamFinder | ✓ | Third FTDNA file shape: tab-delimited with **separate `ALLELE1` / `ALLELE2` columns** instead of a concatenated `RESULT`. Detected by the `famfinder` substring in the file header plus the 5-column canonical header. Build 37 default when known-SNP positions are present but do not decide. |
|
|
77
|
-
| VCF / gVCF | ✓ | REF/ALT encoding, `0/1` genotype notation.
|
|
75
|
+
| FTDNA Illumina raw | ✓ | Tab-delimited variant of the FTDNA export (distinct from the CSV format above). `RSID/CHROMOSOME/POSITION/RESULT` columns. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)). |
|
|
76
|
+
| FTDNA FamFinder | ✓ | Third FTDNA file shape: tab-delimited with **separate `ALLELE1` / `ALLELE2` columns** instead of a concatenated `RESULT`. Detected by the `famfinder` substring in the file header plus the 5-column canonical header. Build 37 default when known-SNP positions are present but do not decide (subject to the stops and the GRCh36 exception in [How the genome build is decided](#how-the-genome-build-is-decided-and-why-a-run-can-stop)). |
|
|
77
|
+
| VCF / gVCF | ✓ | REF/ALT encoding, `0/1` genotype notation. A homozygous-reference call (`0/0`) or a gVCF reference block (lines with `<NON_REF>` ALT and `END=` INFO) carries no variant allele, so it usually matches nothing. The exception is a site where the reference genome itself carries a ClinVar allele (Factor V Leiden on GRCh37, for example): there a `0/0` call, or a reference block that passes the genotype-quality gate, is assessed as homozygous for that allele. See [docs/formats.md, Reference calls in VCFs](https://github.com/allelix/allelix/blob/main/docs/formats.md#reference-calls-in-vcfs) (ADR-0039, ADR-0059). At high-value positions, a block with low genotype quality or no reads makes the position a no-call, and so does a gVCF position no record covers. High-value no-calls list the reason (for example `low_gq_block`, `no_coverage`, `filtered`, `low_gq_call`). A record counts for a high-value or panel rsID when it is at one of the rsID's identity placements with that placement's REF, whatever its ID or ALT; an rsID the identity table does not place is matched by ID (ADR-0055). A plain VCF has no reference blocks, so a position without a record is reported as unknown rather than as reference or as a no-call; at such a ClinVar site the report counts the positions no record covers, and names the high-value ones (such as CYP3A5*3 rs776746 on GRCh37) as not checked. Multi-sample files require `--sample <ID>`. Rows the variant caller rejected (FILTER other than `PASS` or `.`) are skipped and counted in the report; `--keep-filtered` keeps them. A call with genotype quality below `--min-gq` (default 20) or with no reads (DP 0) is reported as a no-call. The spanning-deletion allele `*` is treated as symbolic: such calls are not assessed, and the report counts them and lists their positions. A half call (`./1`) is assessed on its called allele by ClinVar and the GWAS Catalog and shown as `T/.` with zygosity "Half call"; ClinPGx and SNPedia, which need both alleles, skip it. Malformed rows, rows without a genotype, genotypes naming an allele the record lacks and genotypes naming a symbolic allele such as `<NON_REF>` beside a real ALT are counted in the report; a no-call written `./.` stays a no-call (ADR-0047). A VCF that holds only structural-variant or copy-number records (such as a provider's separate `sv` / `cnv` file) is rejected with a message naming the file to use instead; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#sequencingcom-30x-whole-genome) for per-provider deliverables. A VCF whose header declares an imputation tool's fields (Minimac, Beagle, GLIMPSE) is labelled imputed in every report; see [docs/formats.md](https://github.com/allelix/allelix/blob/main/docs/formats.md#imputed-vcfs). Streams via stdlib; `.vcf.gz` handled transparently. Optional `pip install allelix[vcf-index]` enables pysam-backed tabix random access for fast `extract --snps` on huge VCFs. |
|
|
78
78
|
|
|
79
79
|
Files can be passed as delivered: gzipped (Family Tree DNA's `.csv.gz`, for example), or a zip holding one genotype file. A zip with several files, a password or an unusual compression stops with a message to unzip it first. Windows, Linux and old-Mac line endings are read; a leading byte order mark or blank line is ignored.
|
|
80
80
|
|
|
81
|
-
If Allelix does not recognise a file, use the original file from your testing company. `--format <name>` forces a reader at your own risk; every report
|
|
81
|
+
If Allelix does not recognise a file, use the original file from your testing company. `--format <name>` forces a reader at your own risk; when that reader does not recognise the file, every report carries a warning (see [docs/formats.md, Forcing a format](https://github.com/allelix/allelix/blob/main/docs/formats.md#forcing-a-format)).
|
|
82
82
|
|
|
83
83
|
Adding a new format means adding one file to `allelix/parsers/` and registering an instance in the `PARSERS` list in `allelix/parsers/__init__.py`.
|
|
84
84
|
|
|
85
85
|
### Roadmap
|
|
86
86
|
|
|
87
|
+
Shipped releases are listed for history; the [Changelog](https://github.com/allelix/allelix/blob/main/CHANGELOG.md) has each release's changes.
|
|
88
|
+
|
|
87
89
|
| Release | Theme |
|
|
88
90
|
|---|---|
|
|
89
|
-
| **v2.2** | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
|
|
90
|
-
| **v2.3** | Annotated VCF output plus v2.3.1 correctness hardening: cross-build enrichment guards, GWAS effect-type disambiguation, conservative build confidence, SNPedia orientation handling, and strict slow-suite drift verification. |
|
|
91
|
-
| **v2.4** | Correctness hardening: cross-build rsID identity table with explicit allele translation, plausibility flag for genotyping-artifact detection, VCF quality gate honouring FILTER and genotype quality, unified rsID-recovery step across all commands, and parser/format hygiene. |
|
|
92
|
-
| **v2.5** | ClinVar's own classification caps each row; ClinVar variants without an rsID; VCF calls counted, not dropped; read-only caches and named sources in every report; zip input; Genes for Good and deCODEme; imputed input labelled. |
|
|
93
|
-
| **Later** | Variant consequences, CNVs and repeat expansions, gnomAD genomes, supplemental genotype-file merging, ancestry estimation, and PRS support. These are directional themes rather than release commitments. |
|
|
91
|
+
| **v2.2** (shipped) | Custom-panel UX improvements, methylation panel retune + citations, ClinVar source switch (VCF → `variant_summary.txt`), CLI cleanups (`db clean` / `db path`). |
|
|
92
|
+
| **v2.3** (shipped) | Annotated VCF output plus v2.3.1 correctness hardening: cross-build enrichment guards, GWAS effect-type disambiguation, conservative build confidence, SNPedia orientation handling, and strict slow-suite drift verification. |
|
|
93
|
+
| **v2.4** (shipped) | Correctness hardening: cross-build rsID identity table with explicit allele translation, plausibility flag for genotyping-artifact detection, VCF quality gate honouring FILTER and genotype quality, unified rsID-recovery step across all commands, and parser/format hygiene. |
|
|
94
|
+
| **v2.5** (shipped) | ClinVar's own classification caps each row; ClinVar variants without an rsID; VCF calls counted, not dropped; read-only caches and named sources in every report; zip input; Genes for Good and deCODEme; imputed input labelled. |
|
|
95
|
+
| **Later** (planned) | Variant consequences, CNVs and repeat expansions, gnomAD genomes, supplemental genotype-file merging, ancestry estimation, and PRS support. These are directional themes rather than release commitments. |
|
|
94
96
|
|
|
95
97
|
Issues and milestones are tracked at [github.com/allelix/allelix/issues](https://github.com/allelix/allelix/issues).
|
|
96
98
|
|
|
@@ -98,15 +100,15 @@ Issues and milestones are tracked at [github.com/allelix/allelix/issues](https:/
|
|
|
98
100
|
|
|
99
101
|
| Database | Status | Notes |
|
|
100
102
|
|---|---|---|
|
|
101
|
-
| ClinVar (GRCh37 + GRCh38) | ✓ | Public domain (NCBI). SNVs + indels + multi-allelic sites. **Both builds cached**; `analyze` dispatches by the decided build (ADR-0044). Rows are matched by chromosome, position, REF and ALT, never by rsID alone,
|
|
102
|
-
| ClinPGx (formerly PharmGKB) | ✓ | CC BY-SA 4.0. Clinical annotations only — single-rsid SNVs; star alleles and haplotypes deferred (ADR-0009). **Primary non-finding filter is the ClinVar REF carrier rule (ADR-0023):**
|
|
103
|
+
| ClinVar (GRCh37 + GRCh38) | ✓ | Public domain (NCBI). SNVs + indels + multi-allelic sites. **Both builds cached**; `analyze` dispatches by the decided build (ADR-0044). Rows are matched by chromosome, position, REF and ALT, never by rsID alone. A record written REF = ALT, where this build's reference itself carries the record's allele (Factor V Leiden on GRCh37, or a record for the normal allele), is reported for a user who carries that allele; Pathogenic and Likely pathogenic classifications of a normal allele are dropped (ADR-0039). Carrier rule (ADR-0007) requires the user to carry the ALT allele. Indel-anchor protection (ADR-0011) prevents single-base array readouts from matching anchor-base indels. Array calls ClinVar cannot match are counted in the report: vendor probes without an rsID at a locus ClinVar classifies as Pathogenic or Likely pathogenic, and `D`/`I` calls (ADR-0051). A `D`/`I` call at a high-value or Pathogenic / Likely pathogenic rsID is named as not checked, not only counted; it is still not interpreted. Variants that ClinVar's Conflicting or Benign classification scored down (ADR-0045) are named with their submitter split. ClinVar variants without an rsID are matched for VCF input only, where REF comes from the file (ADR-0046). A VCF allele written with bases it shares with REF (`GTT>GT,G`, `CA>TA`) is trimmed per carried allele before matching, by ClinPGx, SNPedia and the GWAS Catalog as well as ClinVar, and a VCF homozygous deletion to one base matches; indels are not left-aligned, so one the file places at a different position from ClinVar's still does not match (ADR-0047). |
|
|
104
|
+
| ClinPGx (formerly PharmGKB) | ✓ | CC BY-SA 4.0. Clinical annotations only — single-rsid SNVs; star alleles and haplotypes deferred (ADR-0009). **Primary non-finding filter is the ClinVar REF carrier rule (ADR-0023):** where ClinVar for the input's build is installed and places the rsID at the call's position, a user homozygous for the normal allele ClinVar resolves there is not reported. That is usually ClinVar's single-base REF. The exception is a site where this build's reference itself carries ClinVar's variant allele (a REF = ALT row, F5 rs6025 on GRCh37): there the normal allele is the other build's REF, so a TT call is reported as a carrier and CC is the non-finding. Where that normal allele cannot be told, nothing is suppressed. Without that cache, only sites where the two builds' references differ (F5 rs6025) are decided: by the other build's ClinVar, else by gnomAD's major allele; if neither can tell, the row shows. Every other site is then not suppressed. CPIC's `(rsid, base) → function_class` join (ADR-0020) survives as a secondary tier for rsids ClinVar doesn't catalog. Earlier prose tiers (ADR-0013, ADR-0017, ADR-0018) are superseded. |
|
|
103
105
|
| CPIC (per-allele function table) | ✓ | Internal data source for the ClinPGx filter. Fetched from `api.cpicpgx.org` at `db update` time. Used to populate the `pharmgkb_allele_function` table — not surfaced to end users as its own annotator. |
|
|
104
106
|
| SNPedia | ✓ | CC BY-NC-SA 3.0 US. Pre-built cache downloaded via `db update` (~216K wiki pages, ~105K genotype rows). The cache carries SNPedia's orientation metadata: minus-strand alleles are complemented before matching. Single-base genotypes at rsIDs are then checked against the site's forward alleles in the rsID identity table: a page's stated orientation must put the genotype among them, an unknown orientation is read directly or else complemented, and A/T and C/G sites abstain. Sites that abstain are counted in the report. If the database is absent, analysis runs without it. For commercial use, pass `--exclude-snpedia`. The cache can also be rebuilt from source via `scripts/scrape_snpedia.py` + `scripts/parse_snpedia.py`. |
|
|
105
107
|
| GWAS Catalog | ✓ | Public domain (EBI/NHGRI). Trait–SNP associations with p-values and effect sizes. Carrier rule (ADR-0007) requires the user to carry the risk allele. The reported allele is the study's effect (risk) allele, which can be the reference allele (ADR-0039). P-value magnitude scoring maps continuous p-values to the 0–10 scale; only a confidence-interval field that positively identifies a bare odds ratio receives the odds-ratio modifier. Beta coefficients and ambiguous or unit-bearing effects receive no modifier (ADR-0038). Unknown-risk-allele entries fire on rsID match alone but are capped at 3.0. Risk alleles are read on the forward strand against the site's alleles in the rsID identity table: as written, else complemented; A/T and C/G sites and rsIDs the table does not place at the call abstain, and the report counts them. Every row says whether the effect allele raises or lowers the odds or the measured value (JSON `effect_direction`). GWAS rows must meet the larger of `--min-magnitude` and `--gwas-min-magnitude` (default 9.0); the must-include rsIDs skip the GWAS floor (ADR-0024). |
|
|
106
108
|
| gnomAD | ✓ | ODbL v1.0. **GRCh38-only enrichment annotator** — adds population allele frequency context to existing annotations. Shows how common each variant is in the general population (~57M exome variants with an rsID, from 730K individuals). A pathogenic variant that 35% of people carry reads very differently from one seen in 0.001%. Pre-built cache downloaded via `db update` (~7 GB on disk). GRCh37 input is enriched only through the rsID identity table's explicit allele translation (ADR-0040); gnomAD's own rsID resolution stays GRCh38-only, and GRCh36 input receives nothing. Use `--no-gnomad` to skip. |
|
|
107
109
|
| AlphaMissense | ✓ | CC BY 4.0. **GRCh38-only enrichment annotator** — adds DeepMind's protein-structure-based pathogenicity predictions to existing annotations. Scores 71M missense variants on a 0–1 scale: <0.34 = likely benign, >0.564 = likely pathogenic. Complements ClinVar's expert classifications with computational predictions — especially valuable for variants ClinVar hasn't reviewed yet. Pre-built cache downloaded via `db update` (~8GB on disk). GRCh37 input is enriched only through the rsID identity table (ADR-0040); GRCh36 input receives nothing. Use `--no-alphamissense` to skip. |
|
|
108
110
|
| CADD | ✓ | LicenseRef-CADD (non-commercial). **GRCh38-only enrichment annotator** — adds PHRED-scaled deleteriousness scores from CADD v1.7. **Opt-in**, disabled by default; GRCh37 input is scored only through the rsID identity table (ADR-0040) and GRCh36 input never; see [CADD modes](#cadd-modes) below for details. |
|
|
109
|
-
| dbSNP rsID identity | ✓ | Public domain (NCBI dbSNP). Not an annotator: a cross-build table of each annotator rsID's GRCh37 and GRCh38 placement and alleles (ADR-0040). Supplies the reference allele for GRCh37 and GRCh38 array data, recovers rsIDs for rsID-less VCFs on both builds, and translates a GRCh37 allele to its GRCh38 coordinates so gnomAD, AlphaMissense and CADD can be looked up. Strand-inverted loci are translated explicitly
|
|
111
|
+
| dbSNP rsID identity | ✓ | Public domain (NCBI dbSNP). Not an annotator: a cross-build table of each annotator rsID's GRCh37 and GRCh38 placement and alleles (ADR-0040). Supplies the reference allele for GRCh37 and GRCh38 array data, recovers rsIDs for rsID-less VCFs on both builds, and translates a GRCh37 allele to its GRCh38 coordinates so gnomAD, AlphaMissense and CADD can be looked up. Strand-inverted loci are translated explicitly. A row the table flags palindromic (its dbSNP allele set equals its own complement: A/T, C/G, or all four bases) abstains where the GRCh37 and GRCh38 reference bases differ, and translates as written where they are equal; rows the table does not flag translate by their alleles. Alleles that became the GRCh38 reference abstain. Pre-built cache downloaded via `db update` (~4.9M rows covering ~3.0M rsIDs, from dbSNP b157). |
|
|
110
112
|
|
|
111
113
|
### CADD modes
|
|
112
114
|
|
|
@@ -117,26 +119,26 @@ CADD ranks how deleterious any single-nucleotide variant is using 100+ annotatio
|
|
|
117
119
|
Two modes:
|
|
118
120
|
|
|
119
121
|
- **Cache mode** (default): pre-built ~5.8 GB SQLite cache (~120M GRCh38 variant keys). Covers nearly every GRCh38 position allelix can annotate from its other databases (gnomAD, AlphaMissense, ClinVar). GRCh37 input is scored only after translation through the rsID identity table; GRCh36 input never is.
|
|
120
|
-
- **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only).
|
|
122
|
+
- **Full mode** (`options.cadd_full = true`): tabix queries against the complete CADD v1.7 file via pysam (GRCh38 only). It scores variants the cache lacks, such as novel or private variants in WGS/WES data. CADD adds scores to findings from the other sources; it never makes a finding of its own, so a variant no other source reports gains nothing. Requires `pip install allelix[cadd]` for the pysam dependency, and CADD's files, downloaded separately from [cadd.gs.washington.edu/download](https://cadd.gs.washington.edu/download) (`db update --cadd` installs only the SQLite cache): `whole_genome_SNVs.tsv.gz` (about 81 GB) and its `.tbi` index in the data directory, plus, optionally for indels, `gnomad.genomes.r4.0.indel.tsv.gz` (about 1.2 GB) and its `.tbi`.
|
|
121
123
|
|
|
122
124
|
If your input is a genotyping chip file, cache mode is normally all you need. GRCh37 chip files are scored through the rsID identity table; GRCh36 files are not scored.
|
|
123
125
|
|
|
124
126
|
### Build coverage asymmetry (GRCh37 vs GRCh38)
|
|
125
127
|
|
|
126
|
-
ClinVar dispatches per build and ships with both GRCh37 and GRCh38 caches. gnomAD v4.1 exomes, AlphaMissense, and CADD v1.7 are GRCh38 datasets. Every enrichment lookup is made at GRCh38 coordinates by chromosome, position, reference and alternate allele, never by rsID. For GRCh37 input, the cross-build rsID identity table (ADR-0040) translates the allele the user carries at its GRCh37 position into the matching GRCh38 allele, written explicitly on the GRCh38 forward strand where the reference strand was inverted between assemblies.
|
|
128
|
+
ClinVar dispatches per build and ships with both GRCh37 and GRCh38 caches. gnomAD v4.1 exomes, AlphaMissense, and CADD v1.7 are GRCh38 datasets. Every enrichment lookup is made at GRCh38 coordinates by chromosome, position, reference and alternate allele, never by rsID. For GRCh37 input, the cross-build rsID identity table (ADR-0040) translates the allele the user carries at its GRCh37 position into the matching GRCh38 allele, written explicitly on the GRCh38 forward strand where the reference strand was inverted between assemblies. A row the identity table flags palindromic (A/T, C/G, or all four bases in dbSNP, whose strand cannot be told from sequence) whose reference base differs between the builds, and an allele that is the GRCh38 reference, receive no values rather than a guess. Without the identity table installed, GRCh37 analyses receive no gnomAD, AlphaMissense or CADD values; GRCh36 analyses never do.
|
|
127
129
|
|
|
128
130
|
Position-based rsID recovery for rsID-less VCFs uses ClinVar (on the selected build), the identity table (on GRCh37 and GRCh38), and gnomAD (GRCh38 only). The same recovery step runs for `analyze`, `stats`, `extract` and `export plink`, so high-value warnings, extracted genotypes and PLINK allele coding all see the recovered rsIDs (ADR-0043). The identity table covers the rsIDs the annotators hold, so an rsID-less GRCh37 VCF now recovers ClinPGx, GWAS Catalog and SNPedia rsIDs as well as ClinVar ones. If you convert between assemblies, re-normalize REF/ALT as part of the conversion and pass `--build` explicitly when the file does not carry a trustworthy build signal.
|
|
129
131
|
|
|
130
|
-
### Known ClinPGx limitation:
|
|
132
|
+
### Known ClinPGx limitation: normal-genotype rows without usable normal-allele evidence
|
|
131
133
|
|
|
132
|
-
ADR-0022 + ADR-0023: a tiny residual of ClinPGx rows may appear in reports even when the user
|
|
134
|
+
ADR-0022 + ADR-0023: a tiny residual of ClinPGx rows may appear in reports even when the user carries the normal genotype. ClinPGx publishes one annotation per genotype including the normal homozygote, and for that row to be suppressed Allelix needs usable evidence of the normal allele from one of:
|
|
133
135
|
|
|
134
|
-
- **ClinVar's
|
|
136
|
+
- **ClinVar's normal allele** (the primary filter — see ADR-0023): usually its single-base REF, or the other build's REF where this build's reference carries ClinVar's variant allele (F5 rs6025 on GRCh37). Covers an rsID that ClinVar for the input's build places at the call's position. Without that cache, only a site whose reference differs between the builds is decided (by the other build's ClinVar, else gnomAD's major allele), and anything else shows.
|
|
135
137
|
- **CPIC's per-allele function table** (the secondary fallback — see ADR-0020). Covers rsIDs CPIC has classified.
|
|
136
138
|
|
|
137
|
-
For the rare rsID where ClinPGx has an annotation
|
|
139
|
+
So without ClinVar or CPIC data, gnomAD can still decide a site whose reference differs between the builds, at that placement only. For the rare rsID where ClinPGx has an annotation and none of these gives a usable normal allele (no ClinVar or CPIC data, and no decision from the other build's ClinVar or gnomAD, which abstains on a tie, an unknown frequency or an unlisted site), the row emits: Allelix shows it rather than guess. These are identifiable by a homozygous-reference genotype combined with "decreased risk," "may have a typical response," or similar comparative language. They are an upstream data gap, not an Allelix bug — we surface them honestly rather than hide them behind a curated exclusion list (which would recreate the maintenance trap the v0.5–v0.7 prose filters were trying to escape).
|
|
138
140
|
|
|
139
|
-
The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the ADR-0023 ClinVar REF check: CPIC's CFTR vocabulary (`"ivacaftor responsive"`) doesn't match the four-class enum the secondary tier expects, but ClinVar publishes REF for
|
|
141
|
+
The CFTR × ivacaftor leak (~30+ rows on real data, pre-v0.7.3) is fixed by the ADR-0023 ClinVar REF check: CPIC's CFTR vocabulary (`"ivacaftor responsive"`) doesn't match the four-class enum the secondary tier expects, but ClinVar publishes REF for the CFTR rsIDs, so with ClinVar for the input's build installed the primary tier catches them.
|
|
140
142
|
|
|
141
143
|
### Known ClinVar upstream data quality issues
|
|
142
144
|
|
|
@@ -214,7 +216,7 @@ The HTML report shows a ⚠ icon next to a flagged genotype, with the reason on
|
|
|
214
216
|
|
|
215
217
|
A call is not assessed when gnomAD has no frequency for the allele. Allelix's gnomAD cache covers exome sites with an rsID, so a missing value says nothing about rarity. Calls are also not assessed when the gnomAD database is skipped. Homozygous calls on X are not assessed because a male's single X reads as homozygous; heterozygous X calls are. A call the file itself declares as one copy (a one-letter array call such as 23andMe's male X calls, or a one-index VCF genotype) is shown as Hemizygous and assessed as one copy. Y and mitochondrial calls are not assessed. Confirm a flagged call with a clinical-grade test before relying on it.
|
|
216
218
|
|
|
217
|
-
When three or more variants on chromosomes 1 to 22 are called homozygous for an allele ClinVar classifies as Pathogenic or Likely pathogenic, and are rare in gnomAD or
|
|
219
|
+
When three or more variants on chromosomes 1 to 22 are called homozygous for an allele ClinVar classifies as Pathogenic or Likely pathogenic, and are rare in gnomAD or were looked up there and are absent, the report warns at the top. A call gnomAD was not asked about (gnomAD not installed, or a GRCh37 allele the identity table cannot translate) does not count. This is counted before any display filter, so `--min-magnitude` cannot hide it (ADR-0050).
|
|
218
220
|
|
|
219
221
|
### Database sizes and download times
|
|
220
222
|
|
|
@@ -245,6 +247,7 @@ Allelix keeps its caches in `~/.local/share/allelix/`, or `$XDG_DATA_HOME/alleli
|
|
|
245
247
|
├── cadd.sqlite ~5.8 GB (only if you opted into --cadd)
|
|
246
248
|
├── clinvar.GRCh37.sqlite ~1.2 GB
|
|
247
249
|
├── clinvar.GRCh38.sqlite ~1.2 GB
|
|
250
|
+
├── clinvar-build.log each ClinVar build's source hashes and counts
|
|
248
251
|
├── gwas_catalog_associations.tsv ~745 MB
|
|
249
252
|
├── rsid_identity.sqlite ~457 MB
|
|
250
253
|
├── gwas.sqlite ~202 MB
|
|
@@ -283,7 +286,7 @@ du -sh "$ALLELIX_DATA"
|
|
|
283
286
|
allelix db path --check
|
|
284
287
|
```
|
|
285
288
|
|
|
286
|
-
**The one thing worth backing up: `config.toml`.** It lives inside the resolved data directory (typically `~/.local/share/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings.
|
|
289
|
+
**The one thing worth backing up: `config.toml`.** It lives inside the resolved data directory (typically `~/.local/share/allelix/config.toml`) and stores your license assertions (commercial-mode toggle, CADD opt-in confirmation) and per-source enable/disable settings. The caches will rebuild on the next `db update`; `config.toml` won't. `allelix db clean` always preserves it. `clinvar-build.log` (the source hashes and counts of past ClinVar builds) is not reproducible either, and `db clean` removes it: copy it elsewhere first if you want to keep that history.
|
|
287
290
|
|
|
288
291
|
To fully uninstall allelix and reclaim everything:
|
|
289
292
|
|
|
@@ -382,11 +385,11 @@ The banner says where the build came from, for example `Build: GRCh38 (from the
|
|
|
382
385
|
- a VCF has no contig lengths, no assembly and too few known-SNP positions;
|
|
383
386
|
- a chip states no build and carries none of the known-SNP positions at all (for example an X-chromosome-only export, since they are all on autosomes);
|
|
384
387
|
- a file states a build but carries none of the known-SNP positions to check it (for example an mtDNA-only export, or a small panel VCF without contig lengths);
|
|
385
|
-
- the file has known-SNP positions but none of them sits at any supported build's coordinate, whether or not it states a build (contig lengths, when a VCF declares them, still decide);
|
|
388
|
+
- the file has known-SNP positions but none of them sits at any supported build's coordinate, whether or not it states a build (contig lengths, when a VCF declares them, still decide). Choosing a build is not the fix here: the message says the positions cannot be trusted on any build and asks you to check that this is the original file from the testing company. If you force a build anyway with `--build`, the run goes ahead but prints a **Build warning** naming the evidence (0 of N known-SNP positions match), and every report keeps it (JSON `report_warnings` code `build_override_unmatched`);
|
|
386
389
|
- contig lengths decided, but the known-SNP positions met while annotating point to the other build (for example a lifted-over file that kept its old header). The message says the annotation pass ran; nothing is written;
|
|
387
390
|
- `--build grch37` or `--build grch38` was given, but the file's contig lengths, or three or more known-SNP positions with an 80% majority, say the other build. The message names both builds. Contrary positions that fall short of that (fewer than three, or under an 80% majority) only print a **Build warning**, and so does a tie that leaves out the forced build. `--build grch36` is never checked, because it turns position-keyed lookups off.
|
|
388
391
|
|
|
389
|
-
|
|
392
|
+
Otherwise the message names the evidence and asks you to say which build the file uses: `--build grch37` or `--build grch38`, or `--build grch36` for an old GRCh36 / hg18 file, which then runs with position-keyed lookups off (see [`docs/grch36-liftover.md`](https://github.com/allelix/allelix/blob/main/docs/grch36-liftover.md)). For `compare`, use `--build1` / `--build2`. A stop leaves no report, annotated VCF, diff, PLINK files or extract table behind. A forced build is recorded as such in every report.
|
|
390
393
|
|
|
391
394
|
### Keeping input and output files safe
|
|
392
395
|
|
|
@@ -395,13 +398,19 @@ must differ from the input, including symlink and hard-link aliases. These
|
|
|
395
398
|
checks run before cache setup or output creation. Inputs that alias the former
|
|
396
399
|
fixed report or PLINK `.tmp` path are also refused; unrelated old temp files
|
|
397
400
|
are left untouched. Annotated VCF still refuses any existing output at the CLI.
|
|
401
|
+
Two outputs of one run must name different files too: `--output` and
|
|
402
|
+
`--vcf-out`, and PLINK's `.bed`, `.bim` and `.fam`, are refused before any work
|
|
403
|
+
when they resolve to one file (another spelling, a symlink, a linked folder or a
|
|
404
|
+
hardlink).
|
|
398
405
|
|
|
399
406
|
Writers use unique temporary files in the destination directory and publish
|
|
400
407
|
only complete, closed content. Failure before publication preserves previous
|
|
401
408
|
outputs. PLINK stages all three files first, then publishes each atomically;
|
|
402
409
|
a later publication failure can leave complete new files next to unchanged old
|
|
403
410
|
ones. Existing ordinary permission bits are retained when replacement is
|
|
404
|
-
permitted; new staging files start private
|
|
411
|
+
permitted; new staging files start private, and a new output file gets the mode
|
|
412
|
+
your umask gives any new file (`0666 & ~umask`, so `0644` under `022`).
|
|
413
|
+
Ownership, ACLs, extended attributes,
|
|
405
414
|
crash durability and concurrent path swaps are outside this guarantee.
|
|
406
415
|
|
|
407
416
|
### Is my database stale? How fresh is the data?
|
|
@@ -438,13 +447,13 @@ Notable load-bearing ADRs:
|
|
|
438
447
|
- **ADR-0042 — VCF quality gate.** The variant caller's FILTER and genotype quality are honoured, low-quality reference blocks are no-calls, and every exclusion is counted in the report.
|
|
439
448
|
- **ADR-0043 — One rsID-recovery step.** Every command that needs rsIDs recovers them for rsID-less input through the same shared step; build detection votes by position instead.
|
|
440
449
|
- **ADR-0044 — Evidence before labels.** The build is decided before annotation from contig lengths or whole-file known-SNP positions; evidence that contradicts the label, or no usable evidence, stops the run instead of producing a report on the wrong build.
|
|
441
|
-
- **ADR-0045: ClinVar's own classification.** A ClinVar row is capped by ClinVar's aggregate classification of the variant (Conflicting, Benign, Likely benign), with the submitter split shown; outranked P/LP submissions are kept by ClinVar's tiered rule where the aggregate is not a disease call.
|
|
450
|
+
- **ADR-0045: ClinVar's own classification.** A ClinVar row is capped by ClinVar's aggregate classification of the variant (Conflicting, Benign, Likely benign), with the submitter split shown, and the variants scored down are named on the console, in HTML and in JSON (`clinvar.scored_down_variants`); outranked P/LP submissions are kept by ClinVar's tiered rule where the aggregate is not a disease call.
|
|
442
451
|
- **ADR-0046: ClinVar variants without an rsID.** Kept in the cache and matched for VCF input only, by locus and the file's REF; reports name such a finding by `chrom:pos ref>alt`.
|
|
443
|
-
- **ADR-0047: VCF calls no longer dropped silently.** Untrimmed alleles are trimmed per carried allele, half calls are assessed by the sources that need only a carried allele, VCF homozygous deletions to one base match ClinVar, and dropped calls are counted with their reason (a genotype on a gVCF reference block's own `<NON_REF>` is not checked).
|
|
452
|
+
- **ADR-0047: VCF calls no longer dropped silently.** Untrimmed alleles are trimmed per carried allele (by every annotator since 2.5.2), half calls are assessed by the sources that need only a carried allele, VCF homozygous deletions to one base match ClinVar, and dropped calls are counted with their reason (a genotype on a gVCF reference block's own `<NON_REF>` is not checked).
|
|
444
453
|
- **ADR-0048: Caches are read-only at analyze time.** With `--no-update`, `analyze` writes nothing to the data directory; every cache opens read-only, installs in rollback-journal mode, and a skipped source is named with its reason (missing, outdated, unreadable, damaged, or WAL mode from an older Allelix). Local migrations run in the update step.
|
|
445
454
|
- **ADR-0049: Every report names its sources.** JSON `sources` gives every registered source's part in the run (used, ready but not used, or skipped with a reason); `report_warnings` carries report-level warnings, starting with ClinVar not used; the annotated VCF names skipped sources.
|
|
446
455
|
- **ADR-0050: Zygosity codes and haploid calls.** JSON zygosity and plausibility are codes, a call the file declares haploid is shown as hemizygous, "not assessed" carries a reason, and three rare homozygous P/LP calls give a report warning.
|
|
447
|
-
- **ADR-0051: Array calls ClinVar cannot match are counted.** Vendor probes at P/LP loci and `D`/`I` calls are counted in the report, and panel coverage puts each requested rsID in one of four states.
|
|
456
|
+
- **ADR-0051: Array calls ClinVar cannot match are counted.** Vendor probes at P/LP loci and `D`/`I` calls are counted in the report; since 2.5.2, a `D`/`I` call at a high-value or P/LP rsID is also named as not checked (JSON `high_value_unchecked` and `report_warnings` code `di_calls_not_checked`), and panel coverage puts each requested rsID in one of four states.
|
|
448
457
|
- **ADR-0052: Zero-star P/LP on a common allele.** A ClinVar P/LP row without assertion criteria whose allele has a gnomAD frequency of 5% or more is scored down, marked and counted.
|
|
449
458
|
|
|
450
459
|
Release history: see [`CHANGELOG.md`](https://github.com/allelix/allelix/blob/main/CHANGELOG.md).
|
|
@@ -460,12 +469,13 @@ pre-commit install --hook-type pre-commit
|
|
|
460
469
|
|
|
461
470
|
ruff check .
|
|
462
471
|
ruff format --check .
|
|
463
|
-
pytest
|
|
472
|
+
pytest -m "not slow and not integration" # the fast suite CI runs
|
|
473
|
+
pytest # every test, including slow and integration
|
|
464
474
|
```
|
|
465
475
|
|
|
466
476
|
The pre-commit hook enforces `ruff check` + `ruff format --check`. If a commit is blocked, fix the underlying problem rather than skipping the hook.
|
|
467
477
|
|
|
468
|
-
For the full real-data release-validation battery — every parser format, VCF/gVCF, PLINK round-trip, edge cases, the GIAB/1000G corpus — see [`test_data/FULL_TEST_PROTOCOL.md`](https://github.com/allelix/allelix/blob/main/test_data/FULL_TEST_PROTOCOL.md). That document is the project's real-data testing story;
|
|
478
|
+
For the full real-data release-validation battery — every parser format, VCF/gVCF, PLINK round-trip, edge cases, the GIAB/1000G corpus — see [`test_data/FULL_TEST_PROTOCOL.md`](https://github.com/allelix/allelix/blob/main/test_data/FULL_TEST_PROTOCOL.md). That document is the project's real-data testing story; `pytest -m "not slow and not integration"` is the fast suite that gates every commit, as in CI.
|
|
469
479
|
|
|
470
480
|
## License
|
|
471
481
|
|
|
@@ -13,6 +13,7 @@ from allelix.annotators.clinvar import CLINVAR_SUPPORTED_BUILDS, ClinVarAnnotato
|
|
|
13
13
|
from allelix.annotators.gnomad import GnomadAnnotator
|
|
14
14
|
from allelix.annotators.gwas import GWASCatalogAnnotator
|
|
15
15
|
from allelix.annotators.pharmgkb import PharmGKBAnnotator
|
|
16
|
+
from allelix.annotators.reference_fallback import ReferenceCarriedFallback
|
|
16
17
|
from allelix.annotators.rsid_identity import RsidIdentityAnnotator
|
|
17
18
|
from allelix.annotators.snpedia import SNPediaAnnotator
|
|
18
19
|
|
|
@@ -55,11 +56,17 @@ def get_annotators(
|
|
|
55
56
|
include_benign=include_benign,
|
|
56
57
|
other_build_refs=rsid_identity.normal_alleles_on_other_build,
|
|
57
58
|
)
|
|
59
|
+
gnomad = GnomadAnnotator(data_dir)
|
|
60
|
+
# ClinVar's REF where its cache for the call's build is readable; at a
|
|
61
|
+
# reference-carried site without it, the other build's ClinVar or
|
|
62
|
+
# gnomAD's major allele, else nothing (2.5.2).
|
|
63
|
+
references = ReferenceCarriedFallback(clinvar, rsid_identity, gnomad)
|
|
58
64
|
pharmgkb = PharmGKBAnnotator(
|
|
59
65
|
data_dir,
|
|
60
|
-
clinvar_ref_provider=
|
|
66
|
+
clinvar_ref_provider=references.reference_at,
|
|
61
67
|
palindromic_provider=rsid_identity.palindromic_rsids,
|
|
62
|
-
normal_allele_provider=
|
|
68
|
+
normal_allele_provider=references.normal_allele_for,
|
|
69
|
+
reference_prefetch=references.prefetch,
|
|
63
70
|
)
|
|
64
71
|
gwas = GWASCatalogAnnotator(
|
|
65
72
|
data_dir,
|
|
@@ -68,11 +75,11 @@ def get_annotators(
|
|
|
68
75
|
)
|
|
69
76
|
snpedia = SNPediaAnnotator(
|
|
70
77
|
data_dir,
|
|
71
|
-
clinvar_ref_provider=
|
|
72
|
-
normal_allele_provider=
|
|
78
|
+
clinvar_ref_provider=references.reference_at,
|
|
79
|
+
normal_allele_provider=references.normal_allele_for,
|
|
73
80
|
forward_site_provider=rsid_identity.forward_sites,
|
|
81
|
+
reference_prefetch=references.prefetch,
|
|
74
82
|
)
|
|
75
|
-
gnomad = GnomadAnnotator(data_dir)
|
|
76
83
|
alphamissense = AlphaMissenseAnnotator(data_dir)
|
|
77
84
|
cadd = CaddAnnotator(data_dir, full_mode=cadd_full)
|
|
78
85
|
return [clinvar, pharmgkb, gwas, snpedia, gnomad, alphamissense, cadd, rsid_identity]
|