Skip to content

Commit 2e4ecbb

Browse files
authored
Merge pull request #325 from bigbio/dev
release: drift probes, HPC container path, and README deduplication
2 parents 5a42b16 + c2b5d42 commit 2e4ecbb

47 files changed

Lines changed: 2593 additions & 367 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 25 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,17 @@ eval "$(poetry env activate)"
2323

2424
### Optional extras
2525

26-
The base install is intentionally lean — heavy plotting and ML dependencies are
27-
opt-in via Poetry extras. Install only what a given workflow needs:
28-
29-
| Extra | Enables | Pulls in |
30-
| --- | --- | --- |
31-
| `viz` | Static and interactive plotting | matplotlib, seaborn, plotly |
32-
| `interactive` | Interactive plots / dashboards | plotly |
33-
| `duckdb` | DuckDB-backed queries | duckdb |
34-
| `hgc` | Joint genotyping plots (genotype adjustment needs no extra) | matplotlib, seaborn |
35-
| `psroc` | Pathogenicity Score ROC analysis | matplotlib, plotly, scikit-learn, scipy |
36-
| `ptm` | CPTAC proteomics builders | cptac, sorted-nearest |
37-
| `constraint` | Tissue-specificity / constraint metrics | tspex, matplotlib, seaborn, scipy |
38-
| `enrichex` | `hvantk enrichex overlap` / `burden` and their plots | scipy, matplotlib, seaborn |
39-
| `cohort` | `hvantk cohort burden` (Fisher gene burden) | scipy |
40-
| `ancestry` | Ancestry inference (PCA + Random Forest + plots) | scikit-learn, matplotlib, seaborn, scipy |
41-
| `ml` | scikit-learn-backed features only | scikit-learn, scipy |
42-
| `expression` | `hvantk expression summarize` / `markers`, and `ptm constraint --expression-metric mean` | scanpy, scipy |
26+
The base install is intentionally lean — plotting, machine-learning, and a few
27+
provider-specific dependencies are opt-in Poetry extras:
4328

4429
```bash
45-
# One or more extras at once
46-
poetry install --extras "ancestry psroc"
47-
48-
# Or a single extra
49-
poetry install --extras ml
30+
poetry install --extras "ancestry psroc" # one or more
31+
poetry install --all-extras # everything
5032
```
5133

34+
For the full table — what each extra pulls in and which commands need it — see
35+
[Installation → Optional features](docs_site/getting-started/installation.md#optional-features-extras).
36+
5237
Verify it works:
5338

5439
```bash
@@ -122,105 +107,24 @@ returns `(native_obj, Provenance)` zero-cost.
122107

123108
### Plugin contract — adding a new data source
124109

125-
Each plugin under `hvantk/skills/<plugin>/` declares itself via
126-
[`plugin.yaml`](hvantk/skills/clinvar/plugin.yaml) and provides a builder
127-
that returns a typed artifact:
128-
129-
```python
130-
# hvantk/skills/clinvar/builder.py
131-
def build_clinvar(parsed_input, ctx: BuildContext, **params) -> AnnotationTable:
132-
ht = hl.import_vcf(str(parsed_input), force=True, ...).rows().key_by("locus", "alleles")
133-
return AnnotationTable.from_hail(
134-
ht, provenance=ctx.provenance(schema_id="clinvar-variants-v1")
135-
)
136-
```
137-
138-
The platform orchestrator [`run_builder_for_spec`](hvantk/core/plugin/run_builder.py)
139-
ties it all together at build time:
140-
141-
```mermaid
142-
sequenceDiagram
143-
participant CLI as hvantk reprocess
144-
participant Reg as plugin registry
145-
participant Probe as drift_probe()
146-
participant Build as build_fn(parsed, ctx)
147-
participant IO as core/io
148-
149-
CLI->>Reg: get_dataset("clinvar:variants")
150-
Reg-->>CLI: DatasetSpec (lazy bind on first access)
151-
CLI->>Probe: compute source fingerprint
152-
Probe-->>CLI: probe dict
153-
CLI->>CLI: BuildContext(plugin, version, fingerprint, …)
154-
CLI->>Build: (parsed_input, ctx, **params)
155-
Build-->>CLI: AnnotationTable(provenance=ctx.provenance(schema_id=…))
156-
CLI->>CLI: validate artifact_type + schema_id
157-
CLI->>IO: artifact.save(path)
158-
IO-->>IO: write data + sidecar .provenance.json
159-
```
160-
161-
Twenty-one plugins ship today: `clinvar`, `clingen`, `gencc`, `gwas-catalog`,
162-
`hgnc`, `gtex-eqtl`, `insider`, `msigdb`, `uniprot-ptm`, `peptideatlas`,
163-
`expression-atlas`, `cptac`, `ucsc-cellbrowser`, `gevir`, `gnomad-metrics`,
164-
`ensembl-gene`, `dbnsfp`, `cosmic-cgc`, `pqtl`, `alphagenome`, `onek-genomes`.
165-
166-
### Project structure
167-
168-
```
169-
hvantk/
170-
├── core/ # platform substrate — stable contracts
171-
│ ├── models/ # AnnotationTable, ExpressionMatrix, VariantMatrix, GeneSet,
172-
│ │ # Provenance, BuildContext, Expr DSL,
173-
│ │ # AlgorithmMeta (@algorithm decorator)
174-
│ ├── io/ # save / load / save_native / load_native,
175-
│ │ # sidecar provenance manifests, legacy shim
176-
│ ├── plugin/ # plugin registry, run_builder_for_spec,
177-
│ │ # two-pass discovery (DatasetManifest → DatasetSpec)
178-
│ ├── tool/ # tool manifest discovery (descriptive)
179-
│ ├── streamers/ # Streamer ABCs — query/iterate built tables
180-
│ │ # (concrete subclasses live in skills/<plugin>/)
181-
│ ├── ontology/ # OBO / MONDO parsers
182-
│ └── utils/ # generic helpers (hail context, hail_helpers,
183-
│ # file utils, gene sets)
184-
185-
├── algorithms/ # analytics — consume artifacts, return artifacts
186-
│ ├── ancestry/ # PCA + Random Forest ancestry inference
187-
│ ├── enrichex/ # gene set enrichment + burden testing
188-
│ ├── expression/ # tissue specificity (tau, gini, etc.)
189-
│ ├── hgc/ # joint genotyping (gvcf combine, VDS, QC)
190-
│ ├── ptm/ # PTM coordinate mapping + atlas
191-
│ ├── psroc/ # pathogenicity score ROC analysis
192-
│ ├── qtlcascade/ # eQTL → pQTL cascade + colocalization
193-
│ ├── annotation/ # spine / prepare / compose annotation pipeline
194-
│ ├── burden/, cohort/ # rare-variant burden + external cohort handling
195-
│ ├── rerank/ # multi-omic gene re-ranking (feature axes + audit)
196-
│ ├── statistics/ # multiple-testing correction, shared stats
197-
│ └── visualization/ # shared figure helpers (empty_figure, save_figure)
198-
199-
├── skills/ # data-source plugins (21 total)
200-
│ ├── <plugin>/
201-
│ │ ├── plugin.yaml # declarative manifest (drives discovery + CLI)
202-
│ │ ├── builder.py # Phase B: (parsed, ctx) → AnnotationTable / …
203-
│ │ ├── drift_probe.py # upstream fingerprint
204-
│ │ ├── cli.py # downloader (auto-wired via manifest cli: block)
205-
│ │ └── tests/ # per-plugin conformance tests + fixtures
206-
│ └── _conventions/SKILL.md # contract documentation
207-
208-
├── tools/ # CLI wiring + workflow orchestration
209-
│ ├── plugins/ # download, drift, reprocess, plugins/tools list
210-
│ ├── hgc/ # joint-genotyping CLI (lazy-loaded)
211-
│ ├── infra/ # catalog, utils (check-install, bgzf)
212-
│ ├── annotation/ # annotate spine / prepare / compose
213-
│ ├── cohort/ # cohort validate / burden / attach
214-
│ ├── rerank/ # rerank CLI
215-
│ ├── genesets/ # gene set extraction / preparation
216-
│ ├── training_sets/ # TrainingSetBuilder — library only, no CLI command
217-
│ └── ancestry/, enrichex/, expression/, ptm/, qtl/ # one package per domain
218-
219-
├── resources/ # platform metadata (unified catalog registry)
220-
└── tests/ # cross-cutting tests (dependency directions,
221-
# plugin conformance, io round-trips,
222-
# Expr algebra parity, etc.)
223-
```
110+
Each data source ships as a self-contained plugin under `hvantk/skills/<plugin>/`,
111+
declared by a [`plugin.yaml`](hvantk/skills/clinvar/plugin.yaml) manifest naming its
112+
builder and drift probe, plus an optional downloader for sources that permit an
113+
automated fetch. Sources behind a license gate, or too large to mirror, ship a
114+
documented acquisition procedure instead. The platform orchestrator
115+
[`run_builder_for_spec`](hvantk/core/plugin/run_builder.py) resolves the manifest,
116+
computes the source fingerprint, calls the builder, validates the returned artifact
117+
against the manifest's `artifact_type` and `schema_id`, and saves it alongside a
118+
sidecar `.provenance.json`. The loader discovers manifests on its own — there is no
119+
registry to edit.
120+
121+
The full contract and the annotated directory tree live in the architecture guide:
122+
123+
- [Plugin contract](docs_site/architecture.md#3-plugin-contract--adding-a-data-source)
124+
— build sequence diagram, annotated `plugin.yaml`, two-pass loader, streamer
125+
placement rule
126+
- [Project structure](docs_site/architecture.md#project-structure) — what lives in
127+
each package, layer by layer
224128

225129
### How to extend
226130

containers/hvantk.def

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Bootstrap: docker
2+
From: python:3.10-slim-bullseye
3+
4+
# hvantk container image, built from the committed poetry.lock
5+
# (hail 0.2.137 + pyspark 3.5.8). See docs_site/guide/hpc-migration.md section 3.
6+
#
7+
# BASE IMAGE: bullseye, not bookworm. Debian 12 (bookworm) has no openjdk-11 package
8+
# at all -- apt reports it is replaced by openjdk-17-jre-headless, and Hail 0.2.x /
9+
# Spark 3.5 support Java 8 or 11 ONLY. Debian 11 (bullseye) still ships
10+
# openjdk-11-jdk-headless. Do not "modernise" this base without checking `java -version`
11+
# inside the built image.
12+
#
13+
# EXTRAS: checked against [project.optional-dependencies] in pyproject.toml. This set
14+
# covers every unique package across all extras. `expression` is required or
15+
# `hvantk expression ...` has no scanpy. `ml` and `interactive` are deliberately
16+
# omitted as redundant: scikit-learn arrives via ancestry/psroc, plotly via viz.
17+
18+
%files
19+
pyproject.toml /opt/hvantk/pyproject.toml
20+
poetry.lock /opt/hvantk/poetry.lock
21+
hvantk /opt/hvantk/hvantk
22+
README.md /opt/hvantk/README.md
23+
24+
%post
25+
set -e
26+
# --- Java 11 (Hail 0.2.137 / Spark 3.5 support Java 8 or 11 ONLY) + native libs ---
27+
apt-get update && apt-get install -y --no-install-recommends \
28+
openjdk-11-jdk-headless \
29+
build-essential g++ \
30+
zlib1g-dev libbz2-dev liblzma-dev libcurl4-openssl-dev libdeflate-dev \
31+
libopenblas-dev liblapack-dev liblz4-dev libhdf5-dev git
32+
# --- hvantk from the committed lock (reproducible; not unpinned pip install) ---
33+
pip install --no-cache-dir "poetry>=2.0"
34+
cd /opt/hvantk
35+
poetry config virtualenvs.create false
36+
poetry install --no-interaction --no-root \
37+
--extras "hgc ptm ancestry psroc constraint enrichex cohort viz duckdb expression"
38+
poetry install --no-interaction --only-root
39+
apt-get purge -y build-essential g++ git && apt-get autoremove -y
40+
apt-get clean && rm -rf /var/lib/apt/lists/*
41+
# fail the build here rather than shipping a broken image
42+
java -version 2>&1 | head -1
43+
python -c "import hail; print('hail', hail.__version__)"
44+
45+
%environment
46+
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
47+
export PATH=$JAVA_HOME/bin:$PATH
48+
export LC_ALL=C.UTF-8 LANG=C.UTF-8
49+
export NO_PROXY=localhost,127.0.0.1,0.0.0.0,::1
50+
export OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1
51+
52+
%runscript
53+
exec hvantk "$@"
54+
55+
%labels
56+
org hvantk
57+
hail 0.2.137
58+
pyspark 3.5.8

containers/hvantk_run.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/bin/bash
2+
# Run hvantk from the Apptainer/Singularity image with Spark scratch set up correctly.
3+
#
4+
# Without SPARK_LOCAL_DIRS on a writable NODE-LOCAL dir, Hail init inside the container
5+
# fails with "DiskBlockManager: Failed to create any local dir" followed by a misleading
6+
# "[Errno 111] Connection refused" from py4j. Always go through this wrapper.
7+
#
8+
# bash hvantk_run.sh utils check-install
9+
# bash hvantk_run.sh reprocess clinvar:variants --raw-dir ... --output ...
10+
# HVANTK_SIF=/path/to/hvantk.sif bash hvantk_run.sh --help
11+
set -euo pipefail
12+
13+
HVANTK_SIF="${HVANTK_SIF:-${WORK:?WORK unset}/containers/hvantk.sif}"
14+
[[ -f "$HVANTK_SIF" ]] || { echo "ERROR: image not found: $HVANTK_SIF" >&2; exit 1; }
15+
16+
# node-local scratch, never home/Lustre/GPFS (guide section 4.1)
17+
SPARK_SCRATCH="${SLURM_TMPDIR:-/tmp/spark-$USER-${SLURM_JOB_ID:-$$}}"
18+
mkdir -p "$SPARK_SCRATCH"
19+
cleanup(){ rm -rf "$SPARK_SCRATCH"; }
20+
trap cleanup EXIT
21+
22+
# Bind any extra data roots the command needs, e.g. HVANTK_BIND="$WORK:$WORK"
23+
BINDS=(-B "$SPARK_SCRATCH:$SPARK_SCRATCH")
24+
[[ -n "${HVANTK_BIND:-}" ]] && BINDS+=(-B "$HVANTK_BIND")
25+
26+
exec singularity exec "${BINDS[@]}" \
27+
--env SPARK_LOCAL_DIRS="$SPARK_SCRATCH" \
28+
--env TMPDIR="$SPARK_SCRATCH" \
29+
--env HAIL_TMPDIR="$SPARK_SCRATCH" \
30+
"$HVANTK_SIF" hvantk "$@"

docs_site/architecture.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ def build_my_source_variants(parsed_input, ctx: BuildContext, **params) -> Annot
294294
)
295295
```
296296
297+
A shipped one to copy from: `hvantk/skills/clinvar/builder.py` defines
298+
`build_clinvar`, which `hvantk/skills/clinvar/plugin.yaml` binds to the
299+
`clinvar:variants` dataset under schema `clinvar-variants-v1`. The builder name
300+
is whatever `plugin.yaml`'s `builder.function` declares — plugins in the tree use
301+
both the short form (`build_clinvar`) and the per-dataset form
302+
(`build_clingen_gene_disease`).
303+
297304
The plugin loader (`hvantk/core/plugin/loader.py`) discovers manifests via a
298305
**two-pass mechanism**:
299306

docs_site/guide/data-sources.md

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,22 @@ hvantk reprocess gnomad-metrics:metrics --skip-download \
179179
--plugin-arg key=transcript
180180
```
181181
182-
### INSIDER interactome (~100 MB)
182+
### INSIDER interactome (~1.2 GB genomic BED; ~49 MB pair table)
183183
184-
Protein-protein interaction sites from the INSIDER database.
184+
Protein-protein interaction interface residues from the INSIDER database.
185185
URL: http://interactomeinsider.yulab.org/downloads.html
186186
187-
**Download**: Visit http://interactomeinsider.yulab.org/downloads.html and download the interaction site BED file.
187+
INSIDER ships **two** products, and hvantk builds a dataset from each:
188+
189+
| dataset | file | size | direct URL |
190+
| --- | --- | --- | --- |
191+
| `insider:variants` | `Whole_Human_Interactome_Interface_hg38.bed` | ~1.17 GB | `http://interactomeinsider.yulab.org/bed/all.bed` |
192+
| `insider:interfaces` | `H_sapiens_interfacesALL.txt` | ~49 MB | `http://interactomeinsider.yulab.org/downloads/interfacesALL/H_sapiens_interfacesALL.txt` |
193+
194+
**Download**: the downloads page carries no links in its markup, so use the direct
195+
URLs above (they are also recorded in the plugin catalog — `hvantk catalog show
196+
INSIDER_v1.0`). Both are served over plain HTTP; the site has no HTTPS listener.
197+
The BED is >1 GB, so acquisition is manual per the downloader framework in CLAUDE.md.
188198
189199
**Build**:
190200
@@ -239,11 +249,25 @@ pathogenicity score. Abramovs, Brass & Tassabehji, 2020, Nature Genetics
239249
52(1):35-39 (PMID 31873297, DOI 10.1038/s41588-019-0560-2).
240250
URL: https://www.nature.com/articles/s41588-019-0560-2
241251
242-
**Download**: Small supplementary table from the Nature Genetics publication
243-
(https://www.nature.com/articles/s41588-019-0560-2) or the authors' repository
244-
(https://github.com/gevirank/gevir). At ~1-2 MB with a stable, public URL, GeVIR
245-
qualifies for a real downloader under the framework in CLAUDE.md — a recommended
246-
follow-up (not yet implemented).
252+
**Download**: the metric table is **Supplementary Table 2** of the Nature Genetics
253+
paper, served as the article's MOESM3 object:
254+
255+
```
256+
https://static-content.springer.com/esm/art%3A10.1038%2Fs41588-019-0560-2/MediaObjects/41588_2019_560_MOESM3_ESM.xlsx
257+
```
258+
259+
That is a ~10.3 MB `.xlsx` workbook (only MOESM3 of the six supplementary slots is
260+
public; the rest return 403). The builder reads a bgzipped TSV, so extract sheet
261+
`table_2` and BGZF-compress it before building.
262+
263+
> **Note:** the authors' repository at https://github.com/gevirank/gevir ships the
264+
> **analysis code only** — its `tables/` directory holds a placeholder file — so it
265+
> is not a source for the metric table. Earlier revisions of this guide pointed
266+
> there.
267+
268+
A real downloader would have to do the extract-and-convert step, not just fetch the
269+
URL, so it is more than the usual thin wrapper — a recommended follow-up (not yet
270+
implemented).
247271
248272
**Build**:
249273

0 commit comments

Comments
 (0)