Skip to content

Phenopackets

phenopackets_etl.py reads dashboard_data.json and writes one GA4GH Phenopacket v2 JSON document per participant, using the Human Phenotype Ontology (HPO) for phenotypes and the Mondo Disease Ontology for diseases. Phenopackets are the exchange format used by rare disease networks such as Matchmaker Exchange, GA4GH Beacon networks and the Monarch Initiative, and they sit alongside the OMOP tables as a second standard output.

Every field comes from a coded record in the bundle. Note text is used only when you ask for it, and genetic results come only from a curated file you supply.

Demo Phenopacket (JSON) Module source (phenopackets_etl.py)

De-identified by default

De-identified (default) Identified (--also-write-identified)
Subject ID PP-NNNN, assigned in order of participant ID Registry participant ID
Date of birth Omitted Included
Time elements Age as an ISO 8601 duration, capped at 90 years Calendar dates
Folder phenopackets_output/deidentified/ phenopackets_output/identified/, with crosswalk.csv

Both folders are restricted: even without names or dates, each file describes one person with a rare disease. run_all.py writes them under restricted/. Review a file against the receiving network's rules before it is submitted anywhere.

Back to top

How the fields are filled

Phenopacket field Source in the bundle How it is mapped
subject patients[]: sex, vital status, and date of birth in the identified folder only Direct
diseases[] problems[] (ICD-10-CM, SNOMED CT), every coding Seed table or Athena "Maps to" to Mondo; a motor neuron disease code (ICD-10-CM G12.2x) with no Mondo mapping is written as its ICD-10-CM term
phenotypicFeatures[] Other problems[] codes Seed table or Athena "Maps to" to HPO
measurements[] labs_vitals[] (LOINC) Passed through with the value and unit
medicalActions[].treatment medications[] (RxNorm), immunizations[] (CVX) Passed through
medicalActions[].procedure procedures[] (SNOMED CT, CPT, HCPCS) Passed through; a procedure whose coding system has no declared resource is left out
interpretations[] A curated genetics CSV you supply See Genetic results
metaData.resources[] The prefixes used in the file One entry per ontology, with the release when Athena records it

A diagnosis such as ALS is written once, as a disease, and not repeated as a phenotypic feature. Only fields the Phenopacket v2 schema defines are written, and no placeholder codes or placeholder interpretations are added. Immunizations are written without a route unless the record states one.

Back to top

Seed tables

The module ships hand-curated mappings for the motor neuron disease spectrum, epilepsy, and autoimmune neurologic and rheumatic disease, in the dictionaries at the top of phenopackets_etl.py. Matching is exact for SNOMED CT and by code prefix for ICD-10-CM, with the longest matching prefix winning. For broader coverage, pass an Athena vocabulary folder with HPO and Mondo selected; its "Maps to" relationships add to the seed tables.

unmapped_codes.csv lists every code that matched neither HPO nor Mondo, with the number of records, which makes it the worklist for extending the seed tables. Mondo and HPO IDs change between releases, so check the seed IDs against the current releases before results are used. The Mondo IDs in the seed tables were checked in November 2026.

Back to top

Medication codes

One FHIR MedicationRequest often holds several RxNorm codes for the same prescription: an ingredient (IN), a clinical drug (SCD) and a brand name (BN). When an Athena folder is supplied, the code is chosen by RxNorm term type in this order:

SCD > SBD > SCDC/SBDC > GPCK/BPCK > SCDF/SBDF > IN > PIN > BN > DF

So a request with both RxNorm:6809 (ingredient) and RxNorm:860975 (clinical drug) is written with the clinical drug. Without Athena, the module uses the coding chosen during parsing, which prefers a display name with a strength and dose form.

Back to top

Note measurements

With --include-note-measurements, ALSFRS-R totals and FVC percent predicted values found by note extraction are added as measurements:

Note pattern Measurement code Unit
alsfrs_r_total LOINC 82953-1 (ALSFRS-R total score) none (UCUM unity)
fvc_percent_predicted LOINC 19872-1 (FVC measured/predicted) %

Each one has a description saying it is a candidate from note text that needs review, and summary.csv counts them separately. LOINC has no codes for the ALSFRS-R domain subscores, and ECAS scores are not mapped, so neither is written. The option is off by default; turn it on only after the patterns have been validated against your documents.

Back to top

Genetic results

Genetic results usually live outside the EHR export: in a genetic test report, a research data set or a genetic counseling record. Supply them as a curated CSV, one row per variant, and the module writes them as interpretations[]. Results are never created from note text.

participant_id,gene_symbol,hgnc_id,hgvs,origin,pathogenicity,zygosity,source
R-000123,SOD1,HGNC:11179,NM_000454.4:c.272A>C,germline,Likely pathogenic,heterozygous,Clinical ALS gene panel report
R-000456,C9orf72,HGNC:28337,,germline,Pathogenic,heterozygous,Repeat-primed PCR report
Column Contents
participant_id Must match patient_id in the bundle (patient_id is also accepted as the column name)
gene_symbol HGNC symbol. The HGNC ID is filled in for C9orf72, SOD1, FUS, TARDBP, TBK1, VCP, UBQLN2, PFN1, MATR3, CHCHD10, ANG, OPTN and ATXN2; give hgnc_id for other genes
hgnc_id With or without the HGNC: prefix
hgvs HGVS expression, written as given. Without it the interpretation names the gene only
origin germline or somatic
pathogenicity Pathogenic, Likely pathogenic, Uncertain significance (or VUS), Likely benign, Benign
actionability actionable or not actionable (optional)
zygosity heterozygous, homozygous or hemizygous, mapped to Genotype Ontology (GENO) terms
source Where the result came from; written to the variant description

Column names are not case-sensitive. A pathogenic or likely pathogenic result is marked CONTRIBUTORY; others are UNKNOWN_STATUS. A participant with no row in the file has no interpretations[] block.

Back to top

Running it

run_all.py writes de-identified Phenopackets to restricted/phenopackets_output/. To run the module on its own:

python pipeline/phenopackets_etl.py --bundle dashboard_data.json --out restricted/
python pipeline/phenopackets_etl.py --bundle dashboard_data.json --out restricted/ \
    --vocab /path/to/athena --external-genetics genetics.csv
Option Default Effect
--bundle ./dashboard_data.json Bundle from run_pipeline.py
--out ./ Folder in which phenopackets_output/ is written
--vocab none Athena vocabulary folder for broader HPO and Mondo mapping and RxNorm term types
--external-genetics none Curated genetic results CSV
--note-extractions ./note_extractions.csv Used only with the next option
--include-note-measurements off Adds ALSFRS-R totals and FVC percent predicted from notes, marked as candidates
--also-write-identified off Adds the identified folder and crosswalk

Back to top

Output

Each folder under phenopackets_output/ holds:

File Contents
<subject_id>.json One Phenopacket per participant
cohort.json All participants as a Phenopacket Cohort
summary.csv One row per subject: counts of diseases, phenotypic features, measurements, note-derived measurements, medical actions and interpretations
unmapped_codes.csv Each vocabulary and code with no HPO or Mondo mapping, the mapping it lacks, and the number of records
crosswalk.csv Identified folder only: registry participant ID and PP-NNNN

Back to top

Checks

The test suite checks that Phenopackets use only fields the GA4GH Phenopacket v2 schema defines, and that de-identified files hold no names, dates of birth or calendar dates (see Quality control). Before results are used, a clinician can compare a participant's Phenopacket with the same participant in the record viewer to catch mappings that are valid but clinically wrong.

Back to top