Skip to content

clay-good/hypnos

Repository files navigation

Hypnos

A curated, citation-backed dataset of anesthetic and perioperative PK/PD model parameters — annotated with explicit confidence tiers and applicability envelopes, with envelope-aware forward simulation and exports into the standard pharmacometric formats (PharmML, NONMEM, nlmixr2/rxode2, Pumas, SBML).

CI  Code: MIT · Dataset: CC-BY-4.0 · Python ≥ 3.9

⚠️ NOT FOR CLINICAL USE

Hypnos is not a clinical decision-support tool, not a TCI pump driver, not a dosing calculator, and not validated for any decision affecting a real patient. It is for research, method development, education, and simulation only. Every export carries a machine-readable clinicalUse = "PROHIBITED" flag. There is no inverse control — Hypnos simulates forward (dose → predicted concentration/effect) and will never compute the infusion required to reach a target. See spec §10.

Hypnos (Greek god of sleep) is the root of hypnotic, the drug class at this dataset's core — a piece of mythology that is also a precise technical term in the field. Hypnos is the sibling of Nidus, reusing its architecture, tier philosophy, and "infrastructure, not a simulator; honest about uncertainty by default" stance.


The problem: model-selection risk

Total intravenous anesthesia, target-controlled infusion (TCI), closed-loop control, depth-of-anesthesia modeling, and the machine-learning-on-VitalDB community all rest on a small set of population PK/PD models. For propofol alone a researcher routinely picks between Marsh, Schnider, Kataria/Paedfusor, and Eleveld. These models:

  • live in PDFs from 1991–2018, often paywalled;
  • publish parameters in inconsistent parameterizations (volumes/clearances vs. micro-rate constants);
  • have different, often unstated, applicability envelopes and documented failure modes outside them (Schnider's lean-body-mass term misbehaves at high BMI; weight-only Marsh performs poorly in the elderly);
  • are re-typed — and quietly mis-typed — into the next paper and the next simulator config.

The field has said so in print: the availability of multiple PK/PD models for a single drug increases the risk of invalid model selection. There is open raw data (VitalDB, Open TCI) and there are individual published models, but nothing curated, tiered, and machine-readable in between that says, honestly, which numbers, for which patient, with what confidence, citing what evidence, and where they break.

Hypnos is that layer.

The headline feature: the model-divergence view

Pick a virtual patient and a dose schedule. Hypnos overlays the predicted plasma and effect-site curves from every eligible model, greys out the ones whose envelope the patient violates, and reports the quantitative divergence between them — both the pooled peak spread (how much they disagree) and the driver pair, the two models furthest apart at the instant of peak disagreement (which model is the outlier). This makes model-selection risk visible and measurable.

Hypnos model-divergence view

Both panels are real output from this repo's compare() API (the same one the dashboard drives), now overlaying all three adult propofol models (Marsh, Schnider, Eleveld):

  • Left — elderly patient (72 y, 60 kg, F). All three are in-envelope yet their predicted effect-site peaks span 5.6 µg/mL (169%): Schnider spikes to ~8 µg/mL while Marsh and Eleveld rise slowly to ~3.7, driven largely by their different ke0 (Marsh 0.26, Eleveld ~0.15, Schnider 0.456 min⁻¹). Same drug, same dose — a clinically enormous disagreement a single-model simulator hides.
  • Right — obese patient (40 y, 140 kg, M). Schnider is greyed out and auto-tiered to D: the patient's BMI (47) is outside its derivation envelope and triggers the documented James-LBM failure mode (the non-physical early spike). Eleveld — built for broad application including the obese — stays in-envelope at Tier A alongside Marsh. The envelope picks the right tool automatically.
$ hypnos compare --drug propofol --age 40 --weight 140 --height 172 --sex M
included (2):
  - hypnotics_iv.propofol.eleveld_2018         tier A  Ce peak 3.974 ug/mL   MDAPE 22-30%
  - hypnotics_iv.propofol.marsh_1991           tier B  Ce peak 3.741 ug/mL   MDAPE 25%
excluded for envelope (2):
  - hypnotics_iv.propofol.paedfusor_2005       tier D  (pediatric model used in an adult ...)
  - hypnotics_iv.propofol.schnider_1998        tier D  (ENVELOPE: bmi=47.3 outside [20, 42]
        -> tiered down to D; FAILURE MODE [James LBM term inverts] -> tiered down to D)

Each included model also reports its published in-envelope inaccuracy (MDAPE) next to its curve, so the view answers not just how much do the models disagree? but and how accurate is each one where it's valid? — the two halves of model-selection risk. The badge deliberately shows only the in-envelope MDAPE: a model's out-of-envelope/failure-mode number (e.g. Minto's 53.4% in morbid obesity) applies precisely where that model would itself be greyed out, so it is never attached to an included model.

The same view works for remifentanil (--drug remifentanil), now with all three models the spec names (Minto, Eleveld, Kim): they agree closely for a standard adult (a useful cross-check), but their envelopes differ sharply at the extremes. For a morbidly-obese patient, only Kim (derived in obesity, BMI to ~70) stays in-envelope while Eleveld (BMI to 52) and Minto (James-LBM failure > 40) are greyed; for a child, only Eleveld (neonate→adult) covers, with Minto and Kim greyed as adult-only. Agreement where models are jointly valid, honest exclusion where one is not: exactly what a model-selection instrument should show.

v0.2: the population-variability layer — bands and the second hidden uncertainty

The divergence view above answers which model did you pick?structural uncertainty. But a single typical-value curve hides a second, equally real uncertainty: even within one model, real individuals scatter around the typical patient. That is the entire reason these are fitted as non-linear mixed-effects models — a fixed-effect layer plus a random-effect layer (Ω for between-subject variability, Σ for residual error). v0.1 curated the first layer and silently dropped the second. v0.2 curates the second with the same tiering, citation, and verification discipline — and, crucially, makes the relationship between the two measurable. (Full design spec.)

Hypnos population-variability layer — prediction band + variance decomposition

Both panels are real, seeded, byte-reproducible output from compare(..., bands=True, seed=7) on the same elderly patient:

  • Left — the never-synthesize rule, made visible. Of the three eligible propofol models, only Eleveld publishes the random-effects structure, so only it earns a 5–95% prediction band (shaded). Marsh and Schnider are drawn as bare median lines and named as excluded from the band math — Hypnos will not borrow a sibling's Ω or impute a "typical" CV. A missing band is a true statement; a borrowed one is a lie with error bars. Notice Schnider's peak (~8 µg/mL) sits clearly above Eleveld's 95th percentile — a structural disagreement no amount of within-Eleveld variability explains away.
  • Right — where does the uncertainty come from? A time-resolved variance decomposition of Eleveld's prediction: at the peak (t* ≈ 3.3 min) ~88% of the within-model predictive variance is between-subject (η / Ω) and only ~12% is residual assay/model noise (ε / Σ). The honest reading: for this patient the patient is the uncertainty, not the measurement — a regime where curating yet another model helps less than the line-spread suggests.

Two machine-readable readouts fall out, both added to divergence["cp"]/["ce"]:

  • Separation index — at the instant of peak median spread, are the two driver models' bands disjoint? separation > 0 means a genuine, irreducible structural disagreement that neither model's own stated variability explains; the reported fraction_trajectory_disjoint is the share of the curve where the bands separate — model-selection risk you cannot variability-away. (It computes once ≥ 2 models carry curated BSV; today Eleveld is the only propofol model that does, so it is reported for drugs/comparisons where two band-eligible models overlap.)
  • Variance decomposition — the structural / BSV / residual share of the total predictive variance, time-resolved. It tells a researcher when curating more models helps (structural-dominated regimes) versus when the patient is the irreducible uncertainty (BSV-dominated).

The static figure above is exactly what the Streamlit dashboard renders live: toggle Seeded 5–95% prediction bands and each band-eligible model gets a shaded Altair ribbon over its median line (no-BSV models stay bare lines and are named), with the separation index and the structural/BSV/residual decomposition beside it. Same seeded compare(..., bands=True) call, so the view never drifts from the CLI.

$ hypnos compare --drug propofol --age 72 --weight 60 --height 162 --sex F \
                 --bands --percentile 5,95 --samples 2000 --seed 7
included (3):
  - hypnotics_iv.propofol.eleveld_2018   tier A  Ce peak 3.57  band-tier B  Ce 2.98 [0.87, 6.90]
  - hypnotics_iv.propofol.marsh_1991     tier B  Ce peak 3.74  band-tier — (no published BSV; line only)
  - hypnotics_iv.propofol.schnider_1998  tier B  Ce peak 8.15  band-tier — (no published BSV; line only)
effect-site divergence … peak rel 169%  (driver: schnider_1998 vs marsh_1991)
  variance share @ t*=3.33 min: structural 0.00 | BSV 0.88 | residual 0.12
  note: marsh_1991, schnider_1998 excluded from band metrics (variability_status: none)

Curated for Eleveld 2018 propofol (the model with the most completely published random-effects structure): the full Ω diagonal (ω² on V1/V2/V3/CL/Q2/Q3/ke0, η-scale log-variances — exactly a NONMEM $OMEGA diagonal) plus the log-additive Σ (σ = 0.191). Cross-checked against the tci R package (the same source Hypnos already cross-checks its kernels against) and curated unverified: the random effects are more error-prone to transcribe than the fixed effects (variance vs SD vs CV%, log vs natural scale), so hypnos validate recomputes each cv_percent from omega2 and the human checklist confirms the five §4 traps against the source table. The band carries its own tier (B), one rung below the Tier-A median line it surrounds — honest, and visible in the output.

The effect band — PK variability propagated into BIS space

Compose a band-eligible PK model with a PD model and the same curated Ω that scatters the concentration scatters the predicted effect: each virtual individual's true effect-site curve is pushed through the (deterministic) Hill model to a BIS band. Quantiles are taken on the effect draws directly, so they stay correct under the monotone, non-linear transform — and the population-median BIS sits below the typical-individual line, exactly as a non-linear average should.

Hypnos PD effect band — PK BSV propagated through the Hill link

Because PD-parameter BSV (Ce50, γ) is not curated, the effect ribbon reports only the propagated PK variability and is labeled an honest lower bound on true effect spread — the never-invent rule, carried into effect space: report what is curated, name what is not. A no-BSV PK model draws no effect band at all (never-synthesize), even with a PD model attached. The dashboard renders this same ribbon live (the band-eligible PK model composed with its matching PD model) whenever the prediction-band toggle is on.

$ hypnos simulate hypnotics_iv.propofol.eleveld_2018 --pd pd_effect.propofol.eleveld_bis \
                  --bands --age 72 --weight 60 --height 162 --sex F --seed 7
Cp peak: 20.141 ug/mL   Ce peak: 3.569 ug/mL
  band-tier B  Ce peak 2.980 [0.872, 6.898] ug/mL  (5-95%, seeded)
effect (Propofol PD — Eleveld two-slope BIS sigmoid): min 30.4
  effect band @ peak: 37.7 [11.4, 76.2]  (5-95%, PK-BSV propagated — lower bound on true effect spread)

Safety, tightened in proportion. A band makes the output look more like a clinical tool, so the guardrails tighten: no quantile-targeting ("the dose that keeps the 95th percentile below X" is inverse control wearing a statistical hat — still forbidden), and every band is labeled a statement about the model's stated uncertainty, not a claim about a real individual. clinicalUse = "PROHIBITED" remains universal.

v0.3: the estimation-uncertainty layer — the third hidden uncertainty

A v0.2 band answers two questions at once — which model? (the divergence view) and which patient, within a model? (the prediction band, drawn from Ω). A single band still hides a third uncertainty, and conflating it with the second is the most common uncertainty error in applied pharmacometrics:

Question Symbol Reducible? By what?
Structural which model? (v0.1) yes curate/validate more models
Estimation how well is this model pinned down? SE / RSE on θ yes more data per model
Between-subject (BSV) which patient, within a model? Ω no the population is the limit
Residual assay + model misspecification Σ no the assay is noisy

A tabulated "25%" next to a parameter is either an estimation RSE (reducible — shrinks with more data) or a between-subject CV (irreducible — a bigger study measures it more precisely but does not make people more alike). They are different numbers, in adjacent columns, with nothing but a header to tell them apart — and Hypnos has already tripped over this in print (the Schnider "CV%s look like RSEs"). v0.3 gives the dataset the vocabulary to record the two apart, and closes the conflation by construction: a per-θ estimation_uncertainty block (se, scale, rse_percent, ci95, method) lives beside the v0.2 variability block, never inside it, so an RSE can never be silently filed as a BSV CV. hypnos validate enforces the numeric traps a machine can catch — scale is mandatory (a log-scale SE applied as natural is silently wrong), and rse_percent/ci95 must be consistent with se — while the cardinal RSE-vs-CV disambiguation stays a human checklist line item (both magnitudes are plausible; the machine must not guess).

The headline (v0.3 §7): the reducible/irreducible decomposition. compare(..., bands=True) already splits the predictive variance into structural / BSV / residual; it now reframes that same variance along the axis that decides what to do about it:

Var_total = Var_structural + Var_estimation + Var_BSV + Var_residual
            \_____________ reducible ______________/   \___ irreducible ___/
              (more models)    (more data)              (the population is the limit)

cmp.divergence["ce"]["reducibility"]
  → {"reducible": 0.61, "irreducible": 0.39, "estimation_curated": false, ...}

The punch-line a single curve can never give: for this patient at this instant, how much of the predictive uncertainty could research buy down — and how much is the patient and the assay? That tells a researcher whether the answer is "fund a bigger study / curate more models" or "no model will ever pin this person down." The estimation (more-data) component contributes 0 until per-θ SEs are curated, and the readout says so honestly (estimation_curated: false) rather than overstating what is reducible.

What is and isn't shipped. The machinery — the schema vocabulary, the validate traps, the estimation verification group, and the reducible/irreducible split — is live. The per-model RSE/covariance values themselves are not curated: that is a transcription from each source PDF (the Eleveld 2018 full text is paywalled to automated fetch), exactly the human-verification step the project refuses to let an LLM perform. So Hypnos ships the layer that closes the conflation by construction and leaves the numbers — and the confidence bands (distinct from prediction bands) they would unlock — as the explicit human-PDF step. A missing confidence band is a true statement; a fabricated one is a lie with error bars.

v0.4: the external-validation metric engine — claims vs. evidence

v0.1 curates what a model claims about itself (the point estimate, its envelope); v0.2 adds which patient, within a model (the prediction band). Both are the model's own story. v0.4 builds the engine that asks the orthogonal question only data can answer: when run forward against real recorded cases, how well does each model actually predict? This is the external-validation layer; VE0 — the metric engine — ships now.

The field's standard is Varvel's framework, the canonical anesthesia PK/PD validation methodology. From a series of observed vs. predicted concentrations it computes, per sample j of subject i:

performance error   PE_ij = 100 · (C_obs,ij − C_pred,ij) / C_pred,ij     (%)
                                                ▲ prediction is the denominator
per subject:
  MDPE_i      = median_j(PE_ij)                — bias        (signed)
  MDAPE_i     = median_j(|PE_ij|)              — inaccuracy  (magnitude)
  wobble_i    = median_j(|PE_ij − MDPE_i|)     — intra-individual variability
  divergence_i= slope of |PE_ij| vs t_j        — drift of error with time (%/h)
population: median (+ seeded-bootstrap 95% CI) of each across subjects i

C_pred is exactly what Hypnos's reference kernels already produce. The engine adds only the alignment + the metric math (pure NumPy), and it is forward-only: it drives the recorded dose history and never searches for a dose (spec §10).

  SubjectRecord                          reference kernel (existing)
  ┌───────────────────────┐              ┌───────────────────────────┐
  │ covariates            │──────────────▶│  simulate(recorded dose)  │
  │ recorded dose history │              │  → Cp / Ce / BIS trajectory│
  │ observations (t,v,kind)│             └─────────────┬─────────────┘
  └───────────┬───────────┘                            │ interpolate to obs times
              │ observed values                         ▼
              └───────────────────────▶  performance_error  →  varvel_metrics (per subject)
                                                                      │
                                          pooled_performance (median + seeded bootstrap CI)
                                                                      ▼
                                          external_validation[] record  ·  validation_status

Because no open patient dataset is bundled (spec §3 — Hypnos commits derived metrics + a manifest, never raw records), the engine is proven two ways with no invented clinical numbers: against hand-computed answers on synthetic fixtures (the textbook edge cases — a single sample, all-zero error, monotone drift, a non-positive prediction), and end-to-end against the real Eleveld kernel by a self-consistency fixture — feed a model's own prediction back as the "observations" and the error is identically zero; inflate the observations by a uniform +20% and MDPE = MDAPE = 20% falls out exactly:

from hypnos.analysis import SubjectRecord, validate_against_cohort
ds = hypnos.load()
# observations here are a +20% offset of the model's own predictions (a known-answer fixture;
# a real run would supply Open-TCI concentrations or VitalDB BIS under their access terms)
cv = validate_against_cohort(ds, "hypnotics_iv.propofol.eleveld_2018", subjects, target="cp", seed=7)
cv.population.mdpe      # 20.0   (bias)        cv.population.ci95["mdpe"]   # seeded bootstrap CI
cv.population.mdape     # 20.0   (inaccuracy)  cv.to_record()               # schema external_validation[] entry

The engine is also a CLI command — the "separately-invoked, locally-run" external validation the spec describes (§9). Point it at your own cohort CSV (long format: subject,time_min,observed,kind + covariates + dose specs; the generic subjects_from_csv adapter maps it to SubjectRecords), or run the no-data known-answer fixture:

$ hypnos validate-cohort --model hypnotics_iv.propofol.eleveld_2018 --self-consistency --offset 20
external validation — hypnotics_iv.propofol.eleveld_2018 vs self-consistency (+20% offset known-answer fixture)
  mode pk_concentration · target cp · 3 subject(s) · seed 0
  metric          value   95% CI (seeded bootstrap)
  MDPE           20.00%  [20.00, 20.00]      ← a +20% offset recovers MDPE = 20% exactly
  MDAPE          20.00%  [20.00, 20.00]
  wobble          0.00%  [0.00, 0.00]
  divergence     -0.00%/h  [-0.00, -0.00]

$ hypnos validate-cohort --model hypnotics_iv.propofol.schnider_1998 --observations my_cohort.csv --json
# → the schema external_validation[] record (reproducible; computed_by=hypnos)

--self-consistency needs no external data and is exercised in CI; the --observations path is the locally-run command a researcher points at Open-TCI / VitalDB / their own study export. The design's load-bearing decision: the computed metrics live in a new external_validation[] block, kept strictly separate from the human-curated, publisher-reported predictive_performance — so the two can be reconciled, never conflated (spec §4.1). hypnos validate enforces the separation's invariants (a BIS validation can never be filed as a concentration validation).

VE1 — the first source adapter (VitalDB). VitalDB is an open intra-operative database whose cases carry the TCI pump's propofol delivery and the measured bispectral index. hypnos.subjects_from_vitaldb maps fetched cases to SubjectRecords for PD-BIS validation — the scientifically independent observation is measured BIS (the pump's predicted Ce is just another model's output), and the propofol delivery (Orchestra/PPF20_RATE, mL/h, PPF20 = 20 mg/mL) is reconstructed as a step-infusion schedule. The adapter is unit-tested against a synthetic VitalDB-shaped fixture; the live fetch is a local command (python scripts/fetch_vitaldb.py --n 30) that never enters CI and never commits raw records — per spec §3 it writes only derived metrics + a manifest to a gitignored data/. Three caveats travel with every number, because they are domain-review items, not verified facts: (1) a propofol-only PK→BIS stack does not model remifentanil's synergistic BIS deepening, so expect a systematic BIS over-prediction in balanced-anaesthesia cases (a real, interpretable finding that itself motivates the interaction surface above); (2) the track names and vial concentration must be confirmed per cohort; (3) committing any derived metric requires confirming VitalDB's data-use terms.

VE2/VE3 — the cross-model, envelope-stratified leaderboard, run on real VitalDB. The headline this layer was built for: hypnos.cross_model_leaderboard (and hypnos leaderboard) scores every eligible PK→BIS stack on one cohort, by the same kernels and seed, and (VE3) re-scores each on its in- and out-of-envelope sub-cohorts via partition_by_envelope — so the ranking compares models, not the cohorts-and-methods that heterogeneous published numbers conflate, and v0.1's asserted failure modes become measured. The VitalDB harness is now genuinely end-to-end: fetch → cache the derived cohort (gitignored) → leaderboard → a committable, aggregate-only report (docs/validation/vitaldb_bis_leaderboard.md). A first real run (30 cases, measured BIS, seed 7):

$ python scripts/fetch_vitaldb.py --n 30 --seed 7
VitalDB external-validation leaderboard — propofol PK→BIS (30 subjects, shared PD bis_sigmoid, seed 7)
  rank model            tier  MDPE%   MDAPE%   in-env     out-env
  1. marsh_1991         B    -32.3    32.3   32.3(30)   —
  2. schnider_1998      B    -33.4    33.7   33.1(22)   34.9(8)
  3. eleveld_2018       A    -34.4    34.4   34.4(30)   —

The reading is itself the lesson: MDPE ≈ −33% for all three — measured BIS is systematically lower (patients deeper) than a propofol-only stack predicts, exactly the remifentanil-synergy bias the adapter warns of — and the PK-model choice barely moves the needle (~2% MDAPE spread). For depth prediction in balanced anaesthesia, the missing synergy term dominates the choice of propofol PK model. That is a real, reproducible, envelope-stratified finding with a pinned manifest — the difference between a dataset that asserts its tiers and one that earns them. (Numbers are evidence pending domain review, not verified facts; a propofol-mono sub-cohort filter and the Open-TCI concentration adapter are the next refinements.)

Drug–drug interaction: the propofol–remifentanil synergy surface

The clinically dominant TIVA pairing is propofol + remifentanil, and their hypnotic interaction is supra-additive (synergistic): adding remifentanil markedly deepens hypnosis at the same propofol dose. Hypnos models this with a Greco-type response surface and a two-drug forward simulator (simulate_interaction), composing two independent PK models into one effect.

Propofol–remifentanil synergy

  • Left — same propofol dose, ± remifentanil (real output). The opioid pushes BIS from a 27.8 nadir to 14.1.
  • Right — the Greco surface E = E0 − Emax·U^γ/(1+U^γ) with U = u_prop + u_remi + α·u_prop·u_remi. The curved iso-BIS contours are the synergy: pure additivity (α = 0) would draw straight lines. At zero opioid the surface collapses exactly to the propofol-alone sigmoid, so it composes consistently with the PK kernels.

Honesty note (same stance as Eleveld). The response-surface mathematics is exact and round-trip validated; the coefficients are representative/illustrative (Tier C, unverified) pending field-by-field transcription of the Bouillon 2004 surface. The qualitative result — remifentanil spares propofol / deepens hypnosis — is robust; the exact numbers are flagged, not asserted. simulate_interaction propagates the worst tier among PK-A, PK-B, and the surface (here C), and still tiers down to D if either patient covariate set is out of envelope.

ir = hypnos.simulate_interaction(
    ds, "interactions.propofol_remifentanil.greco_bis",
    pk_a="hypnotics_iv.propofol.schnider_1998",
    pk_b="opioids.remifentanil.minto_1997",
    patient=dict(age=40, weight=70, height=170, sex="M"),
    schedule_a=[("bolus", 0, "2 mg/kg"), ("infusion", 0, "6 mg/kg/h")],
    schedule_b=[("bolus", 0, "1 mcg/kg"), ("infusion", 0, "0.25 mcg/kg/min")],
    t=np.linspace(0, 30, 181),
)
ir.effect_min, ir.tier        # 14.1, "C"

Breadth & pediatrics: explicit Tier-D extrapolation labeling

Phase C widens coverage beyond the propofol/remifentanil core — a new drug class (the α₂-agonist dexmedetomidine, Hannivoort 2015) and the pediatric propofol model Paedfusor — and makes the spec's headline pediatric idea operational: when an adult model is used in a child (or a pediatric model in an adult), Hypnos does not merely flag "out of envelope," it names the extrapolation and tiers it to D.

Pediatric propofol model divergence

For a 6-year-old, 20 kg child, three models are in-envelope: the canonical pediatric pair Kataria (Tier C) and Paedfusor (Tier B) — the two models the pediatric-TCI literature routinely compares head-to-head ("Kataria vs Paedfusor") — plus the broad-envelope general-purpose Eleveld (Tier A), built to span neonate→elderly and so correctly covering the child by design. The two adult-only models, Marsh and Schnider, are greyed out and explicitly labeled pediatric extrapolations: an adult model used in a child is not predictive, so it is floored to Tier D. The labeling is symmetric: any pediatric model (Kataria, Paedfusor) used in an adult is flagged as a pediatric-model extrapolation, and a 90-year-old outside dexmedetomidine's 18–70 y range is labeled a geriatric extrapolation. Kataria and Paedfusor are both in-envelope yet disagree on plasma (peaks 4.88 vs 4.36 µg/mL here) — making the pediatric model-selection question measurable, the headline feature applied to children.

$ hypnos compare --drug propofol --age 6 --weight 20 --height 115 --sex M
included (3):
  - hypnotics_iv.propofol.eleveld_2018         tier A  Ce peak 2.474 ug/mL   # broad envelope covers the child
  - hypnotics_iv.propofol.kataria_1994         tier C  Cp peak 4.878 ug/mL   # canonical pediatric model
  - hypnotics_iv.propofol.paedfusor_2005       tier B  Cp peak 4.363 ug/mL   # the other half of the pair
excluded for envelope (2):
  - hypnotics_iv.propofol.marsh_1991      tier D  (... PEDIATRIC EXTRAPOLATION: age 6 y is below the
        model's derivation range (>= 16 y); an adult model used in a child is not predictive -> Tier D)
  - hypnotics_iv.propofol.schnider_1998   tier D  (... PEDIATRIC EXTRAPOLATION ...)

v0.5: the organ-function envelope — making the silence speak

The demographic envelope above greys a model when age/weight/BMI fall outside its derivation range. But the highest-stakes extrapolations are physiological — hepatic failure, renal failure, low cardiac output, hypoalbuminemia. Almost no anesthetic PK model was fitted in these groups, yet they are exactly the patients in whom a dosing error is most dangerous, and until now Hypnos was silent on them — and silence reads as "fine." v0.5 §B makes the silence speak: declare organ impairment and every model with no cited standing in that state is greyed and floored to Tier-D with a named extrapolation, exactly as a too-high BMI already is.

Axis Patient covariate "Impaired" (definitional staging, not fitted PK) What the envelope says
Hepatic --child-pugh A/B/C any Child-Pugh class = chronic liver disease hepatically-cleared drugs: reduced clearance, model not fit here
Renal --crcl <mL/min> CrCl < 60 (KDIGO CKD stage ≥3) renally-cleared active metabolites may accumulate
Cardiac --ejection-fraction <%> EF < 40 (reduced) slowed front-end kinetics → higher, faster bolus peak
Albumin --albumin <g/dL> albumin < 3.5 (hypoalbuminemia) raised free fraction of highly-bound drugs → effect under-predicted (cited per-drug binding caveat, below)

The one well-established exception is encoded, not hand-waved: remifentanil is cleared by nonspecific blood/tissue esterases, so its clearance is essentially independent of hepatic and renal function — a cited organ_tolerance (Dershwitz 1996, Hoke 1997). So a cirrhotic, renal-failure patient greys every propofol model but leaves remifentanil standing, with the honest caveat that the active acid metabolite accumulates in renal failure:

$ hypnos compare --drug propofol --age 55 --weight 75 --height 175 --sex M --child-pugh C --crcl 15
excluded for envelope (all):
  - hypnotics_iv.propofol.eleveld_2018   tier D
        ORGAN ENVELOPE: HEPATIC EXTRAPOLATION: Child-Pugh C (chronic liver disease) is outside the
        model's derivation population; the model was not fit in hepatic impairment -> Tier D
        ORGAN ENVELOPE: RENAL EXTRAPOLATION: CrCl 15 mL/min (renal impairment, KDIGO stage ≥3) ... -> Tier D
  (every propofol model greyed — none has organ-failure standing; the eligible set is empty, honestly)

$ hypnos compare --drug remifentanil --age 55 --weight 75 --height 175 --sex M --child-pugh C --crcl 15
included (3):  minto_1997 · eleveld_2017 · kim_2017   # esterase clearance is organ-independent
        ORGAN NOTE: hepatic impairment — model retains standing: cleared by nonspecific esterases ...
        ORGAN NOTE: renal impairment — model retains standing ...  CAVEAT: the active acid metabolite
        (GR90291) accumulates in renal failure; the parent-drug PK this model predicts is unaffected

The honest message the view delivers for an organ-failure patient: "model X has some standing and everything else is extrapolation — here is how far the extrapolations spread, and not one of them is validated here." A normal simulation (no organ covariates) is unaffected. The never-invent rule holds: a model gets organ-failure standing only with a citation — no model is silently "scaled for cirrhosis." The cardiac and albumin axes grey every model including remifentanil, because no model was fit there and none claims otherwise. The same overlay is live in the CLI (--child-pugh/--crcl/--albumin/--ejection-fraction), the dashboard "Organ function" panel, and a CI-executed reference notebook that reproduces the envelope-shrinkage, the remifentanil esterase exception, and the binding-sensitivity separation from the live kernels.

The albumin axis says why it matters (v0.5 S1). Hypoalbuminemia is not a generic envelope miss — for a binding-sensitive drug it is a specific, safety-relevant failure mode. Anesthetics like propofol (~98% bound), fentanyl (~84%), and dexmedetomidine (~94%) are highly protein-bound; only the free fraction is active. When albumin falls, the free fraction rises, so a model fit in normal-albumin patients under-predicts effect from a given total concentration — exactly when the patient is most fragile. Hypnos curates the drug's protein_binding (cited) and surfaces this as a named caveat, never folding a fabricated free-fraction correction into the numbers (the free-fraction shift is flagged, not modeled — v0.5 §B3/§B7):

$ hypnos compare --drug propofol --age 55 --weight 75 --height 175 --sex M --albumin 2.4
  - hypnotics_iv.propofol.eleveld_2018   tier D
        ORGAN ENVELOPE: ALBUMIN EXTRAPOLATION: albumin 2.4 g/dL (hypoalbuminemia) ... -> Tier D
        BINDING-SENSITIVE: propofol is ~98% protein-bound; in hypoalbuminemia the free (active)
        fraction rises, so the total-concentration prediction UNDER-estimates effect — a documented
        failure mode, surfaced not modeled [zamacona-1997-propofol-binding]

Remifentanil (~70% bound) is deliberately not flagged — no claim without standing. The cited magnitude of a documented disease adjustment ("Child-Pugh C reduces clearance ~X%") is the still-proposed quantitative disease_state_modifier layer; it needs per-drug source curation of the adjustment number, so Hypnos surfaces the direction and the failure mode now and refuses to invent the magnitude.

v0.6: local anesthetics — where the safety message is the science

Every other Hypnos subsystem answers "what concentration results from this dose?" For local anesthetics that question has a dangerous shadow — "what is the maximum safe dose?" — the one question Hypnos must never answer. The classic mg/kg "maximum recommended doses" are themselves scientifically shaky: they ignore that site of injection dominates systemic absorption. So v0.6 is designed so the honest science and the safety posture are the same thing. LA0 shipped disposition + site absorption + binding (teaching that the milligram ceiling is the wrong mental model before drawing a single threshold that could be misread); LA1 now adds the toxicity-threshold ranges and the headline double-uncertainty view — and the threshold is curated as a range, never a line, precisely so no single number can be read off it.

The headline: the same dose of the same drug at different injection sites produces materially different peak plasma concentrations, in a robust rank order (intercostal > caudal/epidural > brachial plexus > subcutaneous). A first-order site absorption feeding one-compartment disposition is the closed-form Bateman model; hypnos.la simulates it forward (no dose is ever computed):

$ hypnos la --drug bupivacaine --dose 100 --site lumbar_epidural
bupivacaine — systemic plasma concentration after 100 mg, by injection site
  tier C  (systemic concentration only — NOT block efficacy; thresholds shown as RANGES via the double-uncertainty view (v0.6 LA1))
site               rank ka(1/min)  Cmax(ug/mL)  Tmax(min)
intercostal           1      0.20        1.222       17.8
caudal_epidural       2      0.12        1.161       25.8
lumbar_epidural       3      0.08        1.099       34.3
brachial_plexus       4      0.05        1.012       47.0
subcutaneous          5      0.02        0.800       83.5
site dominance: the SAME 100 mg gives Cmax 1.22 at intercostal vs 0.80 ug/mL at subcutaneous
(1.5x), peaking 66 min later — so the mg/kg ceiling is the wrong mental model (absorption is site-driven).

toxicity thresholds (RANGES, never lines — v0.6 LA1):
  cns_first_symptoms free_plasma  [0.25, 0.4] ug/mL  (tier C, individual variability high)
  cns_first_symptoms total_plasma [1.6, 2.6] ug/mL  (tier C, individual variability high)
  cardiovascular     total_plasma [2, 4] ug/mL  (tier C, individual variability high)

double-uncertainty view — bupivacaine 100 mg at lumbar_epidural (tier C):
  cardiotoxicity: high (racemate) · CNS-to-CVS margin narrow (v0.6 LA2)
  predicted peak: total 1.099 ug/mL · free 0.0686 ug/mL (non-linear; linear under-estimate 0.0549) · Tmax 34 min
  >> THRESHOLD uncertainty dominates: the widest published threshold band spans a 2.0x fold-range
     vs a 1.5x site-driven PK spread — no single safe-concentration line is defensible. This is
     the answer, not a gap to be closed.

The double-uncertainty view (la.double_uncertainty) overlays the predicted concentration against each curated threshold band, on the matching basis (total or free), and names which uncertainty dominates — compared on a consistent multiplicative fold-range scale. For LA the threshold band almost always wins, and saying so is the safety message: the threshold itself is too uncertain to draw a line. The free trace (la.free_concentration) sits beside the total because toxicity tracks free drug.

Hypnos v0.6 LA1 — the double-uncertainty view

The rank is the robust curated direction even where the absolute ka is Tier-C (the rank order is established; the magnitudes are old, wide, and method-dependent — Tier-C by design, cited to Tucker & Mather 1979). LA protein binding (lidocaine ~65%, bupivacaine ~95% saturable, ropivacaine ~94%, levobupivacaine ~97%) is curated too — and correctly kept off the v0.5 albumin axis: LA binding is α1-acid-glycoprotein-driven, not albumin-driven, so the hypoalbuminemia caveat does not fire for LAs (their free-fraction story is the binding-saturation failure mode). Threshold ranges are cited to Tucker & Mather 1979 (lidocaine), Knudsen 1997 and Scott 1989 (bupivacaine/ropivacaine), Bardsley 1998 (levobupivacaine), unverified Tier-C; the load-bearing facts are the margins and the uncertainty, not precise numbers.

LA2 — stereochemistry and the agent-choice cardiotoxicity margin. Racemic bupivacaine is the most cardiotoxic common amide ("fast-in/slow-out" avid cardiac Na-channel binding); its S-enantiomers (levobupivacaine, ropivacaine) were developed to widen the cardiovascular margin at similar potency. A drug-level cardiotoxicity_class (rank · stereochemistry · CNS-to-CVS margin) captures that, and hypnos la --agents ranks the agents with a numeric CNS-to-CVS fold-margin that is monotone with the qualitative class — so the comparison can never contradict itself:

$ hypnos la --agents
local-anesthetic cardiotoxicity comparison (v0.6 LA2) — most cardiotoxic first
drug                     class    stereochem  CNS->CVS margin   fold
bupivacaine               high      racemate           narrow   1.4x
levobupivacaine   intermediate  S_enantiomer         moderate   1.7x
ropivacaine                low  S_enantiomer             wide   2.0x
lidocaine                  low       achiral             wide   5.0x

This is why agent choice matters: at a similar CNS threshold the cardiovascular margin differs by stereochemistry. The double-uncertainty view surfaces the class and raises an explicit narrow-margin warning for bupivacaine (cardiovascular toxicity can precede or skip the CNS prodrome); the class rides through every export as hypnos:cardiotoxicityClass.

Hypnos v0.6 LA2 — the agent-choice cardiotoxicity margin

LA3 — the binding-saturation failure mode, modeled. Amide LAs are highly, saturably bound to α1-acid glycoprotein, and only the free fraction is toxic. As total concentration rises the binding saturates, so the free fraction climbs non-linearly — total concentration under-predicts free-drug risk exactly when risk is highest. LA1 surfaced this as a caveat; LA3 models it. la.saturable_free_concentration is an exact capacity-limited (Langmuir 1:1) kernel whose affinity is pinned by the already-curated low-concentration fraction_bound (C·Ka = fb0/(1−fb0)), so the only new curated number is the binding capacity (≈ AAG molar concentration × drug MW ≈ 4.7–4.9 µg/mL, cited). It is Tier-D and explicitly illustrative: the qualitative rise of the free fraction toward 1 is the documented, load-bearing fact; the magnitude is not a fitted prediction. The view names the under-prediction gap (bupivacaine free peak ≈ 1.8× the linear estimate at a high-absorption site), lidocaine (non-saturable) correctly carries no model, and the kernel reduces exactly to the linear free = total·(1−fb0) at low concentration.

Hypnos v0.6 LA3 — the binding-saturation failure mode

All four LA phases are now live in the CLI (hypnos la), the Streamlit dashboard (a dedicated "Local anesthetics" panel — site dominance, the double-uncertainty view, and the cardiotoxicity comparison; LA models sit outside the IV-divergence view because they use the Bateman absorption kernel, not an IV-disposition kernel), and a CI-executed reference notebook that walks the whole story from the live kernels. Same tested hypnos.la functions throughout, so no surface drifts from another.

v0.7 (C0): the covariate-equation layer — the fourth hidden uncertainty

The divergence view answers which model? (structural), the bands answer which patient? (BSV), the confidence bands answer how well is it pinned down? (estimation). All three quietly assume one thing: that the covariate transform turning a patient into the model's inputs is fixed and known. It is not. Most anesthesia models do not scale on raw weight — they scale on a derived quantity computed by a named auxiliary equation: Schnider's clearance scales on lean body mass by James (1976); Eleveld scales on fat-free mass by Al-Sallami (2015); Kim scales on FFM by Janmahasatian (2005). Swap one equation for another and you have silently fitted a different model — the typical-value curve moves with no change to a single published θ. This is the field's best-documented "same model, different pump, different concentration" divergence, and most open resources do not even record which equation a model was derived with. (Full design spec.)

v0.7 C0 turns v0.1's lone covariates.lbm_equation string into a validated, cited, validity-bounded library: each equation is a first-class object with its form, sex branches, units, its own derivation envelope, and known_failure_modes. Every covariate-scaled model now binds to the exact equation its authors used, and the validator enforces it.

Hypnos covariate-equation layer — the James LBM inversion and its consequence

Both panels are real output from hypnos.covariates.evaluate on the live library:

  • Left — the James inversion, as a tested property. James's LBM equation contains a −128·(weight/height)² term. At fixed height, computed LBM rises, peaks (~110 kg / BMI 38), and then decreases with weight — non-physical. The FFM equations (Janmahasatian, Al-Sallami) stay monotone. James's own validity_envelope (BMI ≤ 37) is shaded; evaluate flags any patient above it inverted, tiers the covariate layer to D, and emits the cited failure-mode warning. The inversion is surfaced, never silently "fixed" — auto-substituting Janmahasatian to suppress it would erase a documented failure mode the project exists to expose.
  • Right — the consequence at its source. Schnider's metabolic clearance Cl1 scales on LBM with a negative coefficient, so when James inverts, Cl1 inflates in obesity — exactly the documented Schnider-in-the-obese failure mode the divergence view already greys. v0.1 greyed Schnider here; v0.7 explains why, at the level of the actual offending object — a covariate equation no one was looking at.
$ hypnos covariates --model hypnotics_iv.propofol.schnider_1998 --age 50 --weight 130 --height 170 --sex M
model: hypnotics_iv.propofol.schnider_1998   covariate_sensitivity_status: declared
  lbm    = james_1976  -> Cl1   value   68.15 kg  tier D  ⚠️ INVERTED/out-of-envelope
      james_1976 (lbm) outside its validity envelope (BMI 45.0 > 37): ... LBM PEAKS and then
      DECREASES with rising weight (a non-physical inversion) -> covariate layer tier D
  covariate-layer tier at this patient: D

Every covariate-scaled model carries a covariate_model block (Schnider/Minto → James, Kim → Janmahasatian, Eleveld propofol+remifentanil → Al-Sallami), each naming the equation, what θ it scales, and whether it is the authors' verbatim choice. hypnos validate checks every binding resolves to a library record and every used_for symbol is a real parameter; the verification checklist gains a covariate_equation group with the five transcription traps (named equation, units cm-vs-m, sex coding, equation envelope, fixed-vs-fitted exponents) as explicit human line items.

C1: divergence within a model — the equation-substitution view

The headline divergence view overlays different models for one patient. C1 adds the orthogonal view: for one model, overlay its predicted curve under each admissible body-size equation — the substitution some commercial TCI pumps make silently when they implement "the Schnider model" with Janmahasatian FFM instead of James LBM. covariate_divergence re-runs the kernel with the descriptor computed by a different equation (a backward-compatible override; the verbatim curve reproduces the model's normal prediction exactly, a tested invariant), greying any equation outside its own validity envelope.

Hypnos covariate-equation divergence — model-selection risk inside one model

  • Left — the obese patient. Schnider's effect-site curve under its own James LBM (greyed — it has inverted at BMI 45) vs the Janmahasatian FFM substitution: a 9% divergence in a single model, from a covariate equation no one was looking at.
  • Right — where the divergence opens. Sweeping BMI, the two predictions (same θ) track closely until James leaves its validity envelope at BMI 37 (shaded), then peel apart — the model-selection risk emerging exactly at the equation's failure boundary. The substitution is always a labeled alternative, never presented as the model's own output; the inversion is surfaced, not silently "fixed" (auto-substituting would erase the documented failure mode).
$ hypnos covariate-divergence --model propofol.schnider_1998 --age 50 --weight 130 --height 170 --sex M
hypnotics_iv.propofol.schnider_1998 — covariate-equation divergence (BMI 45.0)
  equation                 LBM (kg)        Ce peak   status
  james_1976                   68.1       (greyed)   OUTSIDE equation envelope — INVERSION (greyed) *own*
  al_sallami_2015              73.5   13.150 ug/mL   in-envelope (substitution; verbatim=false)
  janmahasatian_2005           73.5   13.150 ug/mL   in-envelope (substitution; verbatim=false)
  peak spread across equations: 9%  (driver: janmahasatian_2005 vs james_1976)
  note: the model's DERIVED equation is james_1976; at BMI 45.0 it has INVERTED.
        this divergence IS the documented Schnider-in-obesity failure mode (v0.1), shown at its source.

C2: the covariate-value band + the fifth variance component

C1 answered which equation? (a structural choice). C2 answers the other half: how well do we even know the covariate? A weight is often estimated, a height rounded — and v0.1–v0.3 treat the covariate vector as exact. Supply a covariate as a distribution (weight={"mean": 130, "sd": 13}) and simulate(..., bands=["covariate"], seed=7) propagates it as a seeded, reproducible covariate band, framed always as "how much does not-knowing-the-weight move the prediction?" — never "what weight should I enter?". A distribution-valued covariate is collapsed to its mean for every deterministic path, so scalar-covariate calls are byte-identical (backward-compatible). The never-invent rule is load-bearing: no distribution → no value band (Hypnos curates no patient's weight).

This completes the reducible/irreducible decomposition with its fifth component. compare(..., bands=["prediction","covariate"]) splits the total predictive variance into structural / bsv / residual / covariate, and the covariate share into its two distinct halves (covariate_split): equation-choice (which body-size equation — reducible by agreeing on the model) and value-uncertainty (the input distribution — reducible by measuring better). Both are reducible, by different actions, and neither is the irreducible between-subject scatter.

Hypnos covariate-value band + the fifth variance component

  • Left — for an obese patient whose weight is estimated (130 ± 13 kg), the Eleveld effect-site curve carries two seeded ribbons: the v0.2 between-subject band and the v0.7 covariate-value band, directly comparable.
  • Right — the time-resolved variance decomposition now with the covariate component beside BSV and residual. The honest reading for this model: Eleveld's between-subject variability (~87%) dwarfs the weight-measurement uncertainty (~2%) — for this patient the patient is the uncertainty, not the weight. A genuinely informative null result a single curve can never give, and exactly the kind of finding the decomposition exists to surface: here the lever is neither "curate more models" nor "weigh more carefully."
$ hypnos compare --drug propofol --age 50 --weight 130 --height 170 --sex M \
                 --bands --covariate-band --weight-sd 13 --seed 7
  variance share @ t*=3.75 min: structural 0.00 | BSV 0.87 | residual 0.12 | covariate 0.02
  covariate split: equation_choice 0.00 | value_uncertainty 0.02  (band-tier B)
  reducibility: reducible 0.02 | irreducible 0.98  (reducible = more models + agree on the covariate equation / measure better)

The dramatic equation-choice divergence lives where a model's own equation has inverted — best seen at its source in the C1 view; the eligible-set decomposition here reports it honestly as small, because the only between-subject-curated propofol model (Eleveld) rests on a well-behaved FFM equation. C0+C1+C2 are shipped; exports that compute LBM the model's way (C3) remain.

v0.8: the developmental layer — children are not small adults

The envelope can grey a model when a patient is younger than its derivation range. But a bare "out of range" flag is the beginning of the pediatric story, not the end. Two mechanistic facts make neonatal/infant PK qualitatively different, and greying-out captures neither: size is non-linear in weight (clearance scales with weight^¾, volume with weight¹, so a 3 kg neonate does not clear at 3/70 of the adult rate), and clearance organs are immature at birth and mature along the post-menstrual-age (PMA) sigmoid MF(PMA) = PMA^Hill / (TM50^Hill + PMA^Hill). The two act in opposite directions, and pediatric dosing is the single most extrapolation-prone, highest-consequence corner of anesthetic pharmacology — exactly where the literature is quietest. (Full design spec.)

v0.8 makes the developmental silence speak with the same evidence-vs-annotation discipline v0.5 used for organ failure. A model fitted in children — Kataria (3–11 y), Paedfusor (>1 y), or Eleveld's broad-application model (neonate→elderly) — is evidence: it keeps its tier. An adult model carried into a neonate is an explicit, bounded, Tier-D extrapolation by allometric size and (where curated) PMA maturation — never a silent linear-per-kg rescaling, the dangerous mental model this subsystem exists to render visibly wrong.

$ hypnos developmental --drug propofol --pma-weeks 40 --postnatal-days 3 --weight 3.4 --age 0.06 --sex M
fitted-pediatric standing (evidence; greyed where out of band):
  - propofol.eleveld_2018   GREYED -> Tier D   (weight 3.4 kg below its 5 kg floor)
  - propofol.kataria_1994   GREYED -> Tier D   (derivation age 3-11 y; neonate is BELOW range)
  - propofol.paedfusor_2005 GREYED -> Tier D   (derivation age >1 y; neonate is BELOW range)
  (no model has fitted standing here -> everything below is EXTRAPOLATION)

extrapolation overlay (Tier-D, opt-in, never a dose):
  - marsh_1991     allometry_plus_maturation   MF(PMA)=0.355   tier D
  - schnider_1998  allometry_only              (maturation un-modeled)   tier D
        CAVEAT: maturation un-modeled -> neonatal clearance OVER-stated, exposure UNDER-predicted
  - linear-per-kg  (naive shortcut)            -> VISIBLY WRONG reference line

The two kernels (hypnos.developmental.allometric_scale, maturation_factor) are pure and round-trip-validated; simulate(..., developmental=True) carries an adult model into a child by its curated block, forcing Tier-D and warning, and refuses (never invents) when no block is curated. The maturation driver is PMA, never chronological age — the cardinal pediatric trap, enforced by the validator and emitted into the SBML hypnos:maturationFunction RDF with a comment forbidding the substitution. The honest output is the spread and the labels; Hypnos computes no pediatric dose (spec §9). Curated blocks are Tier-D and unverified (illustrative TM50/Hill pending source confirmation) — a Tier-D extrapolation band shown honestly is the point.

v0.9: the pharmacogenomic layer — a susceptibility is not a dose

Anesthetic pharmacogenetics splits cleanly into two kinds the field — and any naive "pharmacogenomic dosing" tool — routinely blur. A handful of genotypes change kinetics (atypical butyrylcholinesterase prolongs succinylcholine/mivacurium; CYP2D6 activates codeine/tramadol). But the single most important fact, malignant-hyperthermia susceptibility (RYR1/CACNA1S), changes risk, not kinetics — it is an absolute trigger to avoid, not a dose to adjust. Modeling it as a clearance scale-factor would be a category error with lethal implications. (Full design spec.)

v0.9 adds genotype as a declared envelope axis with two rigorously-separated record kinds: a kinetic modifier scales a forward prediction (opt-in, Tier-D, substrate-scoped, cited), and a safety flag forbids a trigger or warns of a prolonged effect and carries no number (affects_kinetics is False by construction). The schema $defs are disjoint, so MH can never be smuggled in as a clearance change. hypnos pgx (and pgx_overlay) leads with avoidance, because that is the true register of the field:

$ hypnos pgx --bche homozygous_atypical --mh-susceptible
SAFETY / AVOIDANCE FLAGS (not dose adjustments):
  AVOID  RYR1 (sevoflurane/isoflurane/desflurane/succinylcholine) -> trigger-free technique  [Tier A]
  AWARE  BCHE (succinylcholine) -> EXPECT prolonged block; consider an alternative relaxant
KINETIC MODIFIERS (opt-in, Tier D, forward-only):
  succinylcholine + BCHE(homozygous_atypical) -> x0.2 on Cl; scope [succinylcholine, mivacurium, ester_la]
NOT APPLIED (substrate guardrail — a genetic effect never leaks to a non-substrate):
  remifentanil (nonspecific esterases) · lidocaine/bupivacaine/ropivacaine (amide, hepatic) · rocuronium
NO ACTIONABLE PGx EVIDENCE CURATED: dexmedetomidine, fentanyl, nitrous oxide, propofol
  (the honest majority — most anesthetic PGx is avoidance/awareness, not titration)

Two non-negotiable rules carry through. Never-invent: no modifier/flag without a cited record. Never-infer (new, an ethics line): a modifier or flag fires only on a genotype the caller explicitly declares — never from ancestry or frequency; absent a declaration there is no genetic effect, shown as "genotype not declared," never "wild-type assumed." The substrate guardrail (BCHE excludes remifentanil/amide-LAs/rocuronium) is enforced in code and by the validator. And the inverse-control boundary is absolute: a genotype shifts a forward prediction or raises a contraindication; it never yields a dose.

Inhalational agents: a different parameter convention (MAC)

Phase D brings in the non-IV families, which do not fit compartmental PK. Volatile anaesthetics are characterized by MAC (minimum alveolar concentration), its age correction, and partition coefficients — a distinct, first-class physicochemical model type with its own kernel. Hypnos ships sevoflurane, desflurane, isoflurane, and nitrous oxide, and evaluates age-corrected MAC, the MAC fraction (a depth surrogate), and the additive combined MAC fraction when nitrous oxide is co-administered.

Volatile MAC vs age

The age correction is the verified, load-bearing relation (Nickalls & Mapleson 2003): MAC(age) = MAC40 · 10^(−0.00269·(age−40)), ~6% per decade and universal across agents — the right panel shows all three potent agents collapsing onto a single normalized curve. Envelope enforcement applies here too (the relation is valid for age > 1 y).

$ hypnos mac --agent sevoflurane --age 75 --end-tidal 1.2 --n2o 50
MAC (age-corrected): 1.45 vol%   (MAC40 1.8)        # 75 y reduces MAC ~19% below the age-40 anchor
end-tidal 1.2 vol% -> MAC fraction 0.83
+ N2O 50 vol% -> combined MAC fraction 1.43          # MAC fractions are additive
hypnos.mac(ds, "volatiles.sevoflurane.mac", age=75, end_tidal_pct=1.2, n2o_end_tidal_pct=50).combined_mac_fraction

Uptake — the solubility-driven wash-in. The other half of volatile pharmacology is uptake: how fast the alveolar fraction FA rises toward the inspired FI. It is governed by the blood:gas partition coefficient λ already in each record — and Hypnos computes it with a single-compartment alveolar mass balance (hypnos washin):

FA/FI(t) = plateau · (1 − e^(−t/τ)),   plateau = V̇_A/(V̇_A + λ·Q̇),   τ = FRC/(V̇_A + λ·Q̇)

Inhalational wash-in (FA/FI)

The left panel is the FA/FI wash-in; the right makes the mechanism explicit — early plateau falls monotonically as blood:gas λ rises. Both are computed from the curated coefficients alone.

$ hypnos washin
agent             λ(blood:gas)  plateau  τ(min)   FA/FI@3min
desflurane                0.42    0.656    0.41        0.655
nitrous_oxide             0.47    0.630    0.39        0.630
sevoflurane               0.65    0.552    0.34        0.552
isoflurane                 1.4    0.364    0.23        0.364

The discriminating quantity is the early FA/FI plateau (the wash-in "knee" reached before tissue uptake dominates): a less soluble agent has a higher plateau and rises toward it faster, reproducing the canonical ordering — desflurane and nitrous oxide wash in fast, isoflurane slowly — straight from the curated coefficients. Honesty boundary, same stance as the Greco surface: the math is exact, but it rests on stated, standard 70-kg-adult ventilation constants (V̇_A ≈ 4 L/min, FRC ≈ 2.5 L, Q̇ ≈ 5 L/min, all overridable). It is a comparative, education-grade characterization, not a per-patient FA/FI predictor; full multi-compartment tissue uptake (Mapleson) is out of v0.1 scope.

Offset — the wash-out mirror (emergence). Onset has an offset. Run the same alveolar mass balance with the inspired fraction set to zero (and the symmetric early-phase idealization that tissues are still saturated, so blood redelivers agent), and it integrates to the exact mirror of wash-in — falling from 1 toward an elimination floor rather than rising toward a plateau (hypnos washout):

FA/FA₀(t) = floor + (1 − floor)·e^(−t/τ),   floor = λ·Q̇/(V̇_A + λ·Q̇) = 1 − plateau,   same τ

Inhalational wash-out (FA/FA₀)

$ hypnos washout
agent             λ(blood:gas)   floor  τ(min)   FA/FA0@3min
desflurane                0.42   0.344    0.41        0.345
nitrous_oxide             0.47   0.370    0.39        0.370
sevoflurane               0.65   0.448    0.34        0.448
isoflurane                 1.4   0.636    0.23        0.636

The discriminator flips sign but tells the same story: a less soluble agent has a lower floor, so it washes out more completely and faster — desflurane settles toward 0.34 while isoflurane holds at 0.64. This is the physicochemical reason desflurane is chosen for long cases (faster emergence), now computable from the curated λ alone. Same honesty boundary as wash-in, and the same scope limit: this captures only the early, lung-dominated phase — the long tissue-release tail (full Mapleson) is out of v0.1. With floor = 1 − plateau and a shared τ, wash-in and wash-out are one model run in two directions, giving the volatiles the onset→offset symmetry the IV families have via tpeak/decrement.

Neuromuscular blockers are seeded: a train-of-four (T1 twitch) sigmoid PD record (the NMB convention — a steep Hill curve on a twitch-height scale, Ce50 ≈ 0.82 µg/mL at the adductor pollicis) composes onto a rocuronium PK model that is curated but kernel-pending (the Wierda 1991 compartmental parameters aren't openly reconcilable, so simulate() refuses it). Sugammadex reversal (1:1 encapsulation binding kinetics) is deferred — it needs its own model type, like the deferred local-anesthetics subsystem.

Install & quickstart

git clone https://github.com/clay-good/hypnos
cd hypnos
pip install -e ".[dev]"          # add ",dashboard" for the Streamlit UI

hypnos version
hypnos validate                  # JSON-Schema + integrity checks on the dataset
hypnos info                      # counts by subsystem / tier / review status
hypnos compare --drug propofol --age 72 --weight 60 --height 162 --sex F
import numpy as np
import hypnos

ds = hypnos.load()
m = ds["hypnotics_iv.propofol.schnider_1998"]
m.tier                                      # "B"
m.applicability_envelope.weight_kg          # Range(min=44, max=123)
m.review_status                             # "unverified"

# Forward simulation (NO inverse control, by design)
patient = dict(age=72, weight=60, height=162, sex="F")
schedule = [("bolus", 0.0, "2 mg/kg"), ("infusion", 0.0, "6 mg/kg/h")]
t = np.linspace(0, 60, 361)
res = hypnos.simulate(ds, m.id, patient=patient, schedule=schedule, t=t,
                      pd_model="pd_effect.propofol.bis_sigmoid")
res.cp, res.ce, res.effect      # plasma, effect-site conc, BIS trajectory
res.tier, res.warnings          # propagated tier + envelope/failure-mode warnings

# Model-divergence comparison — the headline feature
cmp = hypnos.compare(ds, drug="propofol", patient=patient, schedule=schedule, t=t)
cmp.divergence["ce"]            # {'max_abs': 5.62, 'max_rel': 1.69, 'driver': {'high': '...schnider_1998', 'low': '...marsh_1991', 'gap': 5.62}}
cmp.excluded                    # models greyed out for envelope violation, with reasons

# v0.2 — seeded prediction bands + uncertainty-aware divergence
res = hypnos.simulate(ds, "hypnotics_iv.propofol.eleveld_2018", patient=patient,
                      schedule=schedule, t=t, pd_model="pd_effect.propofol.eleveld_bis",
                      bands=True, percentile=(5, 95), samples=2000, seed=7)
res.ce_quantiles                # {5: array, 50: array, 95: array}; None if no published BSV
res.effect_quantiles            # PD effect (BIS) band — PK BSV through the Hill link (lower bound)
res.band_tier                   # "B" — the band's own tier, ≤ the median-line tier

cmp = hypnos.compare(ds, drug="propofol", patient=patient, schedule=schedule, t=t,
                     bands=True, seed=7)
cmp.divergence["ce"]["variance_share"]   # {'structural': .., 'bsv': .., 'residual': ..}
cmp.divergence["ce"]["reducibility"]     # v0.3 §7: {'reducible': .., 'irreducible': .., 'estimation_curated': bool}
cmp.divergence["ce"].get("separation")   # disjoint-band test (≥2 band-eligible models)
cmp.excluded_from_bands                  # models with no published BSV, named (never fabricated)

How it works

The dataset is the single source of truth. Everything else — simulation, exports, the dashboard — is a deterministic projection of the JSON. No second store of numbers to drift.

flowchart TD
    DS["<b>dataset/</b> — source of truth<br/>JSON model records + JSON Schema + JSON-LD context<br/>drugs · models · covariate eqs · envelopes · tiers · citations"]
    DS --> PKG["<b>hypnos</b> Python package<br/>load · filter · validate<br/>simulate · compare (PK/PD + divergence + bands + organ envelope)<br/>sample_individual (seeded BSV draws) · sample_covariate_vector (v0.7 C2)<br/>simulate_interaction (synergy surface)<br/>covariates (LBM/FFM equation library · James inversion · v0.7)<br/>covariate_divergence (equation substitution) + covariate-value band (C1/C2)<br/>analysis (tpeak · decrement · Varvel MDPE/MDAPE)<br/>inhalational (MAC · wash-in/out)<br/>la (LA absorption · double-uncertainty · cardiotoxicity · free-fraction)<br/>verification (checklists, never promotes)"]
    PKG --> CLI["<b>hypnos</b> CLI<br/>validate · info · models · status · verify<br/>simulate · compare · interact · validate-cohort (Varvel)<br/>tpeak · decrement · mac · washin · washout · la<br/>covariates · covariate-divergence<br/>performance · export"]
    PKG --> DASH["Streamlit dashboard<br/>drug-aware divergence + accuracy + driver<br/>seeded prediction-band ribbons + separation/variance readout<br/>covariate-value band + the 5th variance component (v0.7 C2)<br/>PD effect (BIS) band · onset table · synergy<br/>volatiles (MAC + wash-in/out) · local anesthetics (v0.6)"]
    PKG --> EXP["<b>hypnos.export</b><br/>format builders"]
    EXP --> NM["NONMEM control stream"]
    EXP --> PHARMML["PharmML projection"]
    EXP --> SBML["SBML L3v2 → COPASI/Tellurium"]
    EXP --> TCIJSON["TCI-sim JSON"]
    EXP --> RX["nlmixr2/rxode2 (R)"]
    EXP --> PUMAS["Pumas (Julia)"]
    EXP --> BIB["BibTeX citations"]
    SBML --> OMEX["COMBINE .omex<br/>(deterministic archive)"]
    PHARMML --> OMEX
    TCIJSON --> OMEX
    BIB --> OMEX
Loading
flowchart LR
    P["dataset/models/*.json"] --> REG["registry.py<br/>covariate kernels +<br/>tier & envelope propagation"]
    REG --> REF["reference.py<br/>pure-NumPy/SciPy PK/PD kernels"]
    REG --> EX["exporters<br/>nonmem · pharmml · sbml · tci_json"]
    ANN["annotate.py<br/>MIRIAM · clinicalUse=PROHIBITED · hypnos: RDF"] --> EX
    REF -. "round-trip validates<br/>(1e-6 algebraic / 1e-4 ODE)" .-> EX
Loading

The model record — the unit of curation

One JSON file = one model of one drug for one purpose (e.g. propofol PK — Schnider 1998). Validated against dataset/schema/model.schema.json. Two fields carry the anesthesia-specific load-bearing ideas:

  • applicability_envelope — the covariate ranges the model was actually derived in. First-class, machine-readable, enforced by the simulator.
  • known_failure_modes — documented, cited ways a model misbehaves, each with a machine-evaluable predicate (e.g. "bmi > 42") and an action (tier_down_to_D / warn / exclude).
  • variability / residual_error / omega_block (v0.2, optional) — the random-effects layer: per-parameter bsv.omega2 (η-scale variance), the Σ residual model, off-diagonal Ω, and a variability_status rollup. Each carries its own tier and extraction status; absence renders as an explicit gap (no band), never a fabricated one.
  • covariate_model (v0.7, optional) — the binding to the named body-size/composition equations the model is derived with: each derived_inputs[] names a quantity (lbm/ffm), an equation resolving into the shared dataset/covariate_equations/ library, the θ it scales (used_for), and whether it is the authors' verbatim choice. Absence renders as an explicit "covariate equation unspecified" gap, never an assumption that raw weight was used.

Confidence tiers and how they propagate

Tier Meaning
A Externally validated across ≥2 independent populations; broad covariate range; acceptable predictive performance (|MDPE| ≲ 10–20%, MDAPE ≲ 20–30%).
B One robust population model with ≥1 external validation; performance reported and reasonable.
C Single small/narrow derivation cohort; little/no external validation.
D Used outside its derivation envelope, or an extrapolation, or a hypothesized link with no quantitative validation. Not predictive.

Two rules, enforced in code and by hypnos validate:

  1. Worst input wins. A record's tier equals the worst tier among its parameters. A composed simulation (PK + ke0 + PD) inherits the worst tier among its components. Example: Schnider PK (B) + BIS sigmoid (C) ⇒ the BIS trajectory is reported as Tier C.
  2. Envelope violations force a Tier-D floor. Any request outside the envelope, or that trips a failure-mode predicate, is auto-tiered to D with an attached warning. You cannot launder an extrapolation into an A. Age extrapolations are named: a sub-range patient in an adult model is a pediatric extrapolation, an over-range patient in a pediatric model is flagged as such, and a ≥65 y patient above an adult model's range is a geriatric extrapolation.

The tier has a numeric counterpart. Pharmacometrics measures predictive accuracy with Varvel's metrics — MDPE (median performance error = bias, signed) and MDAPE (median absolute performance error = inaccuracy), plus wobble and divergence — so a tier is not purely editorial (spec §5). Hypnos carries these as cited predictive_performance rows and surfaces them with hypnos performance (or hypnos.performance_table(ds)); every row resolves to a real citation with a DOI (hypnos validate enforces it — a performance number is never asserted bare). Coverage spans both headline drugs and the α₂-agonist class:

  • Propofol trio — an independent head-to-head external validation (Hüppe 2020: Eleveld MDAPE 22%, Marsh 25%, Schnider 26% over 50 surgical adults).
  • Dexmedetomidine (Hannivoort) — best of nine published models in a spinal-anesthesia external validation (Obara 2018: MDPE 5.6%, MDAPE 18.1%, wobble 6.2%).
  • Minto remifentanil carries both an in-envelope number and the price of leaving the envelope: MDPE −17.3% / MDAPE 24.6% in cardiac surgery (Scherrer 2022), against MDAPE 53.4% in morbid obesity (La Colla 2010) — the latter a number that quantifies a documented failure mode (the James-LBM term in the obese), the authors' "not clinically acceptable" made machine-readable.

Reference kernels & validation

Every model with kernel.implemented = true binds to a pure-NumPy/SciPy reference kernel (reference.py):

  • an n-compartment mammillary linear-ODE PK solver, integrated exactly via the augmented matrix exponential (machine precision, robust even when ke0 = 0);
  • the effect-compartment first-order ke0 link;
  • the sigmoid E_max / Hill PD transform (propofol→BIS, rocuronium→train-of-four), single-slope and the Eleveld two-slope variant (asymmetric Hill about Ce50);
  • the two-drug Greco response surface;
  • the volatile MAC age-correction (Mapleson/Nickalls) and the single-compartment alveolar wash-in / wash-out (FA/FI uptake and FA/FA₀ emergence, mirror-image: floor = 1 − plateau, shared τ) — non-compartmental, physicochemical kernels.

Validation, run in CI (test suite):

  • Analytic vs. independent numeric — the matrix-exponential solver is checked against a separate scipy.solve_ivp integration (≤ 1e-4 relative) and against the closed-form one-compartment bolus solution (≤ 1e-7).
  • Export round-trip — each SBML / TCI-JSON / rxode2 / Pumas export is parsed back, re-simulated, and compared to the kernel (≤ 1e-6 algebraic). An export bug cannot ship silently.
  • NONMEM $THETA fidelity — emitted thetas are asserted equal to the instantiated parameters; the v0.2 $OMEGA/$SIGMA are emitted from the curated omega2/residual model with EXP(ETA(.)) wired into $PK, and a no-BSV model keeps 0 FIX with the missing component named. The off-diagonal $OMEGA BLOCK path is exercised against a synthetic complete block (front-anchored covariance, non-contiguous fall-back), and the rxode2/Pumas population blocks are asserted to derive the same typical micro-constants the median model emits (η=0 collapses the band to the line).
  • .omex determinism — the COMBINE archive is byte-identical across runs (fixed timestamps); CI rebuilds it and validates the manifest, so an archive bug cannot ship silently.
  • effect-site divergence is computed only over models that carry a ke0 link — a PK-only model (e.g. Kim remifentanil, Paedfusor) has ce = 0 and is excluded from the effect-site spread so it cannot manufacture a spurious divergence; plasma divergence still spans every model.

Export formats

Exports are generated artifacts, never hand-edited (CI regenerates them), each instantiated at a stated reference patient and carrying the propagated tier, the verbatim covariate equations, MIRIAM bqmodel:isDerivedFrom DOI/PMID links, and the universal clinicalUse = PROHIBITED flag.

Format Role Status
NONMEM control stream ($PK/$THETA/$OMEGA/$SIGMA) Lingua franca of population PK ✅ ADVAN11/TRANS4; v0.2 emits the real $OMEGA diagonal + $SIGMA where BSV is curated (and $OMEGA BLOCK for a complete off-diagonal block; else 0 FIX)
SBML L3v2 Compartmental ODE → COPASI/Tellurium; continuity with Nidus ✅ rate-rule form, round-tripped; v0.2 carries the Ω/Σ as hypnos: RDF annotations (SBML core is deterministic, so a solver still sees the typical patient — stated in an inline comment)
TCI-sim JSON Clean ingestable JSON for the open-TCI/simulator community ✅ round-tripped; v0.2 carries the variability block losslessly
PharmML (projection) The "SBML of PK/PD"; durable interop anchor ✅ structural + params + provenance; v0.2 first-class VariabilityModel (η→RandomEffect, ε→ResidualError)
nlmixr2 / rxode2 (R) Open-source pharmacometric estimation & simulation ✅ round-tripped; v0.2 runnable <id>_pop companion (lotri Ω + cp ~ lnorm/prop/add Σ)
Pumas (Julia) Modern open pharmacometric simulation ✅ round-tripped; v0.2 <id>_pop @param/@random/@derived NLME block
COMBINE .omex Provenance-bundled archive (SBML+PharmML+TCI-JSON+RDF+BibTeX) ✅ deterministic, manifest-validated
BibTeX Citation export (cite Hypnos and the source) ✅ per-model + dataset
CSV Flat parameter export (one row per parameter, DOI/PMID joined) ✅ per-model + dataset
; HYPNOS EXPORT — NOT FOR CLINICAL USE
;   clinicalUse = PROHIBITED — research/education/simulation only
;   model: hypnotics_iv.propofol.schnider_1998  (tier B, review: unverified)
; Population covariate equations (verbatim from source):
;   Cl1: Cl1 = 1.89 + 0.0456*(WGT-77) - 0.0681*(LBM-59) + 0.0264*(HGT-177)
$SUBROUTINES ADVAN11 TRANS4
$THETA
  1.78948   ; 1 CL  (L/min)
  4.27      ; 2 V1  (L)
  ...
; bqmodel:isDerivedFrom = https://doi.org/10.1097/00000542-199805000-00006

The v0.2 random-effects layer projects to every population format (here Eleveld propofol, the one model carrying a curated Ω/Σ). A single shared projection (export/_variability.py) renders the η-scale Ω diagonal and the Σ residual model in each ecosystem's native idiom — so the population model, not just its typical patient, round-trips:

# nlmixr2 / rxode2 — a runnable population companion (eta=0 collapses to the median line)
hypnotics_iv_propofol_eleveld_2018_pop <- rxode2({
  Cl1 <- 1.922638103 * exp(eta.Cl1)      # log-normal BSV on each disposition parameter
  V1  <- 6.47078481  * exp(eta.V1)
  ...
  k10 <- Cl1 / V1 ; k12 <- Cl2 / V1 ; ...   # micro-constants derived → round-trip exact
  cp = A1 / V1
  cp ~ lnorm(0.191)                       # Σ: log-additive residual
})
hypnotics_iv_propofol_eleveld_2018_omega <- lotri({
  eta.Cl1 ~ 0.265   # CV~55%     eta.V1 ~ 0.61   # CV~92%   ...   eta.ke0 ~ 0.702  # CV~101%
})
<!-- PharmML — random effects are first-class -->
<VariabilityModel bandTier="B" variabilityStatus="diagonal">
  <VariabilityLevel symbol="indiv" type="betweenSubject"/>
  <RandomEffect symbol="eta_Cl1" parameter="Cl1" level="indiv" distribution="Normal"
                transformation="log" variance="0.265" cvPercent="55.08"/>
  ...
  <ResidualError model="log" description="log-additive (≈ proportional on natural scale)" logSd="0.191"/>
</VariabilityModel>

NONMEM emits the matching $OMEGA diagonal (and a real $OMEGA BLOCK when a complete off-diagonal block is curated — covariance r·√(ωᵢ²ωⱼ²) over a contiguous η run, honest diagonal-plus-caveat otherwise); Pumas emits a @param/@random/@derived NLME block; TCI-JSON carries the block verbatim; and SBML — whose core is deterministic — carries the Ω/Σ as hypnos: RDF annotations with an inline comment that a solver sees only the typical patient. A model with no published BSV (Marsh, Schnider) emits none of this and names the missing component — the never-synthesize rule, all the way through the export layer.

The COMBINE .omex archive — the distribution unit

One .omex (a ZIP with a COMBINE omex-manifest) bundles a model's SBML (master) + PharmML + TCI-JSON + a provenance metadata.rdf + a citations.bib, so the model travels with its interop projections, its confidence tier, its DOI/PMID provenance, and the clinicalUse flag in one self-describing file.

flowchart LR
    M["model record<br/>(dataset/models/*.json)"] --> SBML["model.sbml.xml<br/><i>master</i>"]
    M --> PH["model.pharmml.xml"]
    M --> TCI["model.tci.json"]
    M --> RDF["metadata.rdf<br/>tier · clinicalUse · DOI/PMID"]
    M --> BIB["citations.bib"]
    SBML --> MAN["manifest.xml<br/>(omex-manifest)"]
    PH --> MAN
    TCI --> MAN
    RDF --> MAN
    BIB --> MAN
    MAN --> OMEX["<b>&lt;model&gt;.omex</b><br/>deterministic ZIP"]
Loading

Archives are written with fixed entry timestamps, so a given dataset version always yields byte-identical bytes — a reproducibility guarantee CI enforces.

hypnos export --format omex   --output exports/hypnos.omex   # one archive, all PK models
hypnos export --format omex   --output exports/omex/          # one .omex per model
hypnos export --format bibtex --output exports/               # citations.bib
python scripts/regenerate.py                                  # regenerate every export + figures

Current coverage (v0.9.0 — v0.1 A–E · v0.2 variability · v0.3 E0–E3 estimation+confidence bands · v0.4 VE0–VE3 leaderboard+VPC · v0.5 S0/S1/B0/B1 organ+binding+reversal+disease · v0.6 LA0–LA3 local anesthetics · v0.7 C0–C3 covariate equations+divergence+bands+exports · v0.8 developmental · v0.9 pharmacogenomics)

Honest status. A is the propofol spine; B adds the dominant opioid and the interaction surface; C widens to a new drug class and pediatrics; D brings in the non-IV families; E hardens for release (COMBINE .omex, BibTeX, reproducibility, verified MDPE/MDAPE) (v0.1 roadmap). v0.2 adds the population-variability layer — Ω/Σ random effects, seeded prediction bands, and uncertainty-aware divergence (Eleveld propofol curated; bands + separation index + variance decomposition live; every export — NONMEM $OMEGA/$OMEGA BLOCK/$SIGMA, PharmML, nlmixr2/rxode2, Pumas, TCI-JSON, and SBML (as hypnos: RDF) — carries the random-effects layer). The only V3 item left is the never-invent BSV data backfill for the other models, blocked on source-table confirmation. v0.3 (E0) adds the estimation-uncertainty layer — per-θ SE/RSE/covariance vocabulary kept separate from BSV (closing the RSE-vs-CV conflation by construction), its validate traps, and the reducible/irreducible variance split in compare; the per-model RSE values await human-PDF transcription. v0.4 adds the external-validation metric engine (VE0) — Varvel MDPE/MDAPE/wobble/divergence computed forward from the reference kernels, with the computed metrics kept strictly separate from the curated publisher-reported ones; the open-data adapters that feed it (VE1–VE3) are next and need credentialed data CI never touches. v0.5 makes the organ-function envelope speak — S0: hepatic/renal/cardiac/albumin impairment greys every model with no cited standing, with remifentanil's esterase exception encoded (Dershwitz/Hoke); S1: the protein-binding / free-fraction failure mode surfaces a cited BINDING-SENSITIVE caveat for propofol/fentanyl/dexmedetomidine in hypoalbuminemia (the shift named, not modeled). v0.6 (LA0–LA3, complete) delivers the local-anesthetic subsystem — site-driven systemic absorption (the mg/kg ceiling is the wrong model), disposition + binding for lidocaine/bupivacaine/levobupivacaine/ropivacaine, the toxicity-threshold ranges (never lines) + the double-uncertainty view whose honest punchline is that the threshold uncertainty dwarfs the PK spread, the stereochemistry/cardiotoxicity agent-choice axis (cardiotoxicity_class + hypnos la --agents, the CNS-to-CVS fold-margin monotone with the ranking), and the non-linear binding-saturation free-fraction model (a Tier-D capacity-limited Langmuir kernel whose affinity is pinned by the curated fraction_bound, making total concentration's under-prediction of free-drug risk visible) — all carried through every export with the hypnos:safetyCritical flag. v0.7 (C0+C1) opens the covariate-equation layerC0: a shared, validity-bounded, cited library of the LBM/FFM equations each model is derived with (James 1976, Janmahasatian 2005, Al-Sallami 2015), covariate_model bindings on all five covariate-scaled models, covariates.evaluate with the James inversion as a tested property (peak-then-decline → Tier-D + cited warning), validator + verification-group enforcement, and the hypnos covariates CLI; C1: the equation-divergence view (covariate_divergence + hypnos covariate-divergence) overlaying a single model under each admissible body-size equation, greying any outside its own envelope — the documented Schnider-in-obesity failure mode rendered at its covariate source, via a backward-compatible kernel override (the verbatim curve reproduces the model's normal prediction exactly); C2: the covariate-value band (simulate(..., bands=["covariate"]) from a weight={"mean","sd"} distribution, seeded, never-invent) + the fifth variance component in compare (covariate share split into equation_choice vs value_uncertainty, with a refined reducible/irreducible rollup) — reaching the CLI (--covariate-band --weight-sd) and the dashboard. Covariate-aware exports (C3) are next. v0.8 (complete) makes the developmental envelope speak mechanistically — the allometric size + Anderson–Holford PMA-maturation kernels, the opt-in Tier-D developmental=True simulation and the developmental_overlay/hypnos developmental headline (fitted-pediatric evidence vs allometry(+maturation) extrapolation vs the visibly-wrong linear-per-kg shortcut), the five-trap validator checks (PMA-clock first), and the hypnos:developmentalModel SBML RDF; Marsh (allometry_plus_maturation) and Schnider (allometry_only) carry curated Tier-D blocks, Eleveld is labeled fitted-pediatric evidence — all unverified pending source confirmation. v0.9 (complete) adds the pharmacogenomic envelope — the never-blurred kinetic-modifier vs safety-flag separation (a susceptibility is never a number — affects_kinetics False by construction), the declared-only genotype axis with the never-infer ethics rule, the avoidance-first pgx_overlay/hypnos pgx, the substrate guardrails (BCHE excludes remifentanil/amide-LAs/rocuronium, enforced in code + validator), and the hypnos: RDF carriers; the RYR1→MH avoidance flag rides the volatiles + succinylcholine, the atypical-BCHE prolonged-block flag + opt-in Tier-D modifier ride a new succinylcholine record. 27 models · 17 drugs · 9 subsystems · 20 executable kernels · 3 covariate equations · 9 export formats · 34 citations · 1 model with a curated random-effects layer · 5 models with a named covariate-equation binding · 3 models with cited organ-failure standing · 4 local anesthetics with toxicity-threshold ranges + cardiotoxicity classes · 3 with a non-linear free-fraction model · 3 binding-sensitive drugs flagged · 2 adult models with a developmental extrapolation block · 4 models carrying a pharmacogenomic flag/modifier.

Model Record Kernel Tier Notes
Propofol PK — Marsh 1991 ✅ executable B Weight-only; warns in elderly.
Propofol PK — Schnider 1998 ✅ executable B Age/weight/height/James-LBM; high-BMI failure mode encoded.
Propofol PK — Eleveld 2018 ✅ executable A General-purpose, broad envelope (neonate→obese elderly). Kernel transcribed from the published equations (cross-checked vs the tci R package), validated to reproduce the reference individual exactly; review_status stays unverified pending human PDF confirmation. v0.2: carries the curated Ω-diagonal + log-additive Σ (band-tier B) — the only model with a random-effects layer today.
Propofol PK — Paedfusor 2005 ✅ executable B Pediatric (1–12 y); the Tier-D extrapolation showcase, in both directions.
Propofol PK — Kataria 1994 ✅ executable C Pediatric (3–11 y, n=53); the canonical "Kataria vs Paedfusor" pair. Weight-proportional with an age term on V2; PK-only. Transcribed from the standard published set (Shafer/STANPUMP lineage); unverified.
Propofol PD — BIS sigmoid ✅ executable C Single-slope effect-site → BIS; composes onto any PK model and floors the tier to C.
Propofol PD — Eleveld two-slope BIS ✅ executable B Validated PD companion to the Eleveld PK kernel; asymmetric Hill (γ=1.47 below Ce50, 1.89 above), age-corrected Ce50. A fully-Eleveld PK-PD BIS trajectory.
Remifentanil PK — Minto 1997 ✅ executable B Age + James-LBM; shares the high-BMI LBM failure mode; reported in ng/mL (the opioid convention).
Remifentanil PK — Eleveld 2017 ✅ executable A Allometric (FFM) general-purpose; broad envelope (neonate→obese elderly), faster ke0 than Minto. Validated to reference; unverified. Found+fixed a V3 reference typo in the tci source.
Remifentanil PK — Kim 2017 ✅ executable A Derived in obesity (Janmahasatian FFM, BMI to ~70); widest obesity envelope of the trio. PK-only (no published ke0). Validated to reference; unverified.
Dexmedetomidine PK — Hannivoort 2015 ✅ executable B New drug class (α₂-agonist); allometric (vol ^1, CL ^0.75); adult-only, narrow BMI.
Fentanyl PK — Shafer 1990 ⏳ pending C Curated record with verified citation; kernel deferred — secondary sources disagree on the exact micro-rate constants, so simulate() refuses it.
Interaction — propofol×remifentanil (Greco/BIS) ✅ executable C Two-drug response surface; math exact and round-tripped, coefficients illustrative/unverified pending Bouillon-2004 transcription.
Volatiles — sevoflurane · desflurane · isoflurane · N₂O (MAC) ✅ executable B New physicochemical type: MAC40 + Mapleson age-correction + partition coefficients; MAC fraction + nitrous-oxide additivity.
Rocuronium PK — Wierda 1991 ⏳ pending C Seeds nmb_agents; kernel deferred (compartmental params not openly reconcilable).
Rocuronium PD — train-of-four sigmoid ✅ executable C NMB convention: steep Hill on twitch-height; Ce50 ≈ 0.82 µg/mL (adductor pollicis). Composes onto a rocuronium PK kernel once verified.

Implemented but still unverified — the distinction that matters. Eleveld's covariate equations (post-menstrual-age maturation, allometric scaling, BMI/age sigmoids) are exactly where transcription errors hide (spec §9). The kernel was transcribed from the published equations, cross-checked against the open tci R implementation, and validated to reproduce the reference individual (V1=6.28, CL=1.79, ke0=0.146) to the decimal. It still reads unverified, because reproducing one reference point is not a human confirming every covariate equation against the PDF. Promoting it to verified is the next highest-leverage contribution — run hypnos verify hypnotics_iv.propofol.eleveld_2018 for the checklist. Models whose parameters cannot even be reconciled to a primary source (rocuronium PK, Shafer fentanyl) stay kernel-pending and simulate() refuses them.

Verification: the single highest-leverage contribution

Promotion to verified requires a human to open the source PDF and confirm, field by field, every structural parameter and every covariate equation (the covariate equations are where published-vs-implemented divergence hides). LLMs may assist but never promote — nothing automated writes review_status = verified.

Between "illustrative placeholder" and "human-verified" sits a third, honest state: pending_human_review. An automated process fetched a real, cited source and numerically compared the curated values — and they matched — but no human has confirmed against the primary PDF. The governance line holds by construction: an automated check can reach pending_human_review, never verified (the extraction.source_review block always carries human_verified: false, and hypnos validate enforces it). The block records exactly what was compared, against which fetched URLs, and the outcome (match / partial_match / discrepancy), so a human can audit the machine's work and then promote — or contest — it.

An automated pass triaged all 24 models against the tci/opentiva/OpenTCI/PAS reproductions, primary-paper PubMed abstracts, FDA/EMA labels, and CrossRef. It did not silently fix what it found: a parameter-mislabel bug in the upstream tci package (Hypnos's value is correct), an objectively wrong DOI on the rocuronium citation (fixed, CrossRef-verified), and four citation/attribution problems now marked contested — the propofol→BIS sigmoid (mis-cited to Schnider 1999), ropivacaine (cited to Tucker 1979, predating the drug — traces to Lee 1989), levobupivacaine (Bardsley 1998 couldn't compute its PK), and the propofol–remifentanil BIS surface (Bouillon's BIS surface is additive, not the curated synergy). Each carries the correct source and a recommended human action.

$ hypnos status
verification coverage: 0/24 verified (0%)   pending_human_review 18   unverified 2   contested 4
  (pending_human_review = curated values cross-checked against a real source by an automated
   process; NOT human-verified — a human confirms against the primary PDF to promote)

start here (cheapest human wins first: pending_human_review, then unverified by kernel + tier):
  - hypnotics_iv.propofol.eleveld_2018               tier A  kernel   cite eleveld-2018-propofol
  - opioids.remifentanil.eleveld_2017                tier A  kernel   cite eleveld-2017-remifentanil
  ...

$ hypnos verify hypnotics_iv.propofol.schnider_1998 --markdown   # copy-pasteable PR checklist

hypnos verify <id> emits the field-by-field checklist — each parameter, each covariate equation (e.g. the James LBM term), the envelope, the derivation population, and the DOI to confirm — as plain text or markdown for a PR. Each structural parameter carries its curated source locator (@ Schnider 1998, Table 2) so the verifier goes straight to the right table instead of hunting the whole PDF; where a locator is not yet curated the checklist flags the gap to fill — the dataset staying honest about its own provenance, not just its parameter values. The prioritization is deliberate: models with an implemented kernel and the best tier come first, because verifying those unlocks trustworthy simulation. See the essay Why model-selection risk is the load-bearing idea for the philosophy, and CONTRIBUTING.md for the checklist rules.

Design decisions

Decision Rationale
Pure Python (NumPy/SciPy); R/Julia only as export targets Nothing is compute-bound; the R/Julia models are generated artifacts, not a runtime dependency.
Dataset is the centerpiece; everything else is a projection The durable contribution is the curated, tiered, envelope-annotated models — not any one viewer or solver.
Envelope + failure modes are first-class and machine-enforced Model-selection risk is the core pain; making the envelope enforceable is the load-bearing idea.
Tier & envelope warnings propagate; worst input wins A composed simulation is only as trustworthy as its weakest component or furthest extrapolation.
Humans verify; LLMs do not promote unverified → verified requires reading the source PDF and confirming the parameters and the covariate equations. Even Eleveld — implemented and validated to its reference patient — stays unverified; that flag means a human has checked the PDF, nothing less. Models whose parameters cannot be reconciled to a primary source (rocuronium PK, Shafer fentanyl) stay kernel-pending and simulate() refuses them.
Age extrapolations are named, not just flagged "Out of envelope" is generic; "pediatric extrapolation of an adult model" is the actual clinical risk the spec calls out. The label is first-class and tested.
One internal concentration unit (µg/mL), conventional units for display The kernels work in µg/mL (= mg/L) throughout; each drug declares its conventional unit so output reads naturally (ng/mL for opioids and dexmedetomidine). The CLI likewise uses drug-appropriate default doses — a 2 mg/kg propofol regimen applied to remifentanil would be a ~1000× overdose.
No TCI engine, no dosing output, ever The line between "research simulator" and "unregulated medical device" is exactly the inverse-control step. Hypnos stays on the safe side by construction.
Forward characterizations only — onset and offset, not constant-conc CSHT Onset (tpeak: bolus → peak Ce, where Ce = Cp) and offset (decrement: plasma decline after a fixed-rate infusion) are pure forward problems and in scope. Classic context-sensitive half-time needs a target-controlled infusion holding plasma constant (inverse control), so it is deliberately excluded. The decrement metric still shows the same context-sensitivity: it lengthens with infusion duration for propofol (1.0→4.2 min over 10→600 min) yet stays near-flat for remifentanil (~2.2 min) — its celebrated property. The safety boundary even shapes which metrics exist.
Exact matrix-exponential solver, memoized Closed-form per segment for a linear time-invariant system; more accurate than stepwise integration, and the augmented form handles ke0 = 0 without a singular inverse. The propagator is memoized by (dt, rate), so a uniform grid costs ~1 matrix exponential instead of one per step (≈70× faster at n=361; identical results).

Repository layout

hypnos/
├── README.md · LICENSE (MIT, code) · LICENSE-DATASET (CC-BY-4.0, data)
├── CITATION.cff · CONTRIBUTING.md · pyproject.toml
├── dataset/                     # the single source of truth
│   ├── schema/                  # JSON Schema + JSON-LD context
│   ├── drugs/ · models/ · citations/
│   └── VERSION
├── python/hypnos/
│   ├── load.py · filter.py · validate.py · models.py
│   ├── reference.py             # pure-NumPy/SciPy PK/PD kernels
│   ├── simulate.py              # forward simulation + compare + interaction (no inverse control)
│   ├── inhalational.py          # volatile MAC API (age correction, fraction, N2O additivity) + wash-in/out (FA/FI · FA/FA₀)
│   ├── la.py                    # LA site-absorption PK (Bateman, LA0) + double-uncertainty view (LA1) + cardiotoxicity comparison (LA2) + saturable free-fraction kernel (LA3)
│   ├── covariates.py            # the v0.7 covariate-equation library (LBM/FFM; the James inversion as a tested property)
│   ├── developmental.py         # the v0.8 allometric-size + Anderson–Holford PMA-maturation kernels (forward-only, never a dose)
│   ├── pharmacogenomics.py      # the v0.9 declared-genotype overlay: kinetic modifiers vs safetyCritical avoidance/awareness flags (never-infer)
│   ├── analysis.py              # derived characterizations: onset/offset + Varvel external-validation metrics (forward-only)
│   ├── verification.py          # verification checklists + coverage (guides humans; never promotes)
│   ├── cli.py
│   └── export/                  # registry · annotate · _variability(Ω/Σ projection) · nonmem · pharmml · sbml · tci_json · rxode2 · pumas · bibtex · csv_flat · combine(.omex)
├── scripts/regenerate.py        # deterministically regenerate all exports + figures (+ build_*_notebook.py · fetch_vitaldb.py)
├── notebooks/                   # reference notebooks executed in CI (nbmake): divergence · v0.2 variability · v0.5 organ envelope · v0.6 local anesthetics
├── CHANGELOG.md · .zenodo.json   # release metadata (Zenodo DOI on first tagged release)
├── python/tests/                # analytic-vs-numeric, round-trip, envelope, tier, CLI, verification
├── dashboard/app.py             # Streamlit: divergence + bands + PD effect band (v0.2) + onset + synergy + volatiles + local anesthetics (v0.6)
├── docs/about/essay.md          # why model-selection risk is the load-bearing idea
├── docs/specs/v0.1/spec.md      # the design spec (typical-value layer)
├── docs/specs/v0.2/variability.md  # the population-variability layer (Ω/Σ, bands)
├── docs/specs/v0.3..v0.6/        # design specs: estimation uncertainty · external validation (VE0 shipped) · breadth · LAST
└── .github/workflows/ci.yml

Cheat sheet

hypnos version
hypnos validate                                   # schema + integrity (citations, tiers, kernels, envelopes)
hypnos info                                       # counts by subsystem / tier / review status
hypnos models [--drug propofol]                   # list models (id / purpose / tier / kernel / review)
hypnos performance [--drug propofol]              # published MDPE/MDAPE/wobble/divergence, cited
hypnos status                                     # verification coverage + what to verify next
hypnos verify <model_id> [--markdown]             # field-by-field verification checklist
hypnos simulate <model_id> --age .. --weight .. --height .. --sex .. [--pd <pd_id>]
hypnos simulate <model_id> --pd <pd_id> --bands --seed 7   # v0.2: Cp/Ce band + PD effect (BIS) band
hypnos compare  --drug propofol --age .. --weight .. --height .. --sex ..
hypnos compare  --drug propofol --age .. ... --bands --percentile 5,95 --samples 2000 --seed 7  # v0.2 prediction bands + uncertainty-aware divergence
hypnos compare  --drug propofol --age 55 ... --child-pugh C --crcl 15  # v0.5 organ-function envelope: greys models with no standing
hypnos interact --age .. --weight .. --height .. --sex ..             # propofol+remifentanil synergy
hypnos mac --agent sevoflurane --age 75 [--end-tidal 1.2] [--n2o 50]  # age-corrected MAC + fraction
hypnos washin  [--agent sevoflurane]                                 # inhalational wash-in (FA/FI uptake), solubility-driven
hypnos washout [--agent sevoflurane]                                 # inhalational wash-out (FA/FA₀ emergence), the offset mirror
hypnos la --drug bupivacaine --dose 100                              # v0.6 LA0: LA systemic concentration by injection site (site dominance)
hypnos la --drug bupivacaine --dose 100 --site lumbar_epidural       # v0.6 LA1: + the double-uncertainty view (concentration vs threshold RANGES, free trace)
hypnos la --agents                                                   # v0.6 LA2: cross-agent cardiotoxicity comparison (CNS-to-CVS margin by stereochemistry)
hypnos covariates                                                    # v0.7 C0: list the LBM/FFM equation library (validity envelopes, tiers, citations)
hypnos covariates --equation james_1976 --weight 130 --height 170 --sex M  # v0.7 C0: evaluate one equation (surfaces the James inversion -> Tier D)
hypnos covariates --model hypnotics_iv.propofol.schnider_1998 --weight 130  # v0.7 C0: a model's covariate_model bindings for the patient
hypnos covariate-divergence --model propofol.schnider_1998 --weight 130 --height 170  # v0.7 C1: divergence WITHIN one model over its covariate equations
hypnos simulate <model_id> --covariate-band --weight-sd 12 --seed 7  # v0.7 C2: covariate-VALUE band (weight estimated ± 12 kg)
hypnos compare --drug propofol --bands --covariate-band --weight-sd 13 --seed 7  # v0.7 C2: + the 5th variance component (equation vs value)
hypnos tpeak <model_id> --age .. --weight .. --height .. --sex ..    # time to peak effect (onset)
hypnos decrement <model_id> --duration 240 [--infusion '6 mg/kg/h']  # plasma decrement time (offset)
hypnos validate-cohort --model <id> --self-consistency --offset 20   # v0.4: Varvel MDPE/MDAPE/wobble (known-answer fixture, no data)
hypnos validate-cohort --model <id> --observations cohort.csv [--json]  # v0.4: external validation vs a cohort CSV (locally-run)
hypnos export   --format {nonmem,pharmml,sbml,tci_json,rxode2,pumas,bibtex,csv,omex} --output exports/ [--model <id>]
python scripts/regenerate.py                                          # regenerate all exports + figures
streamlit run dashboard/app.py
ds = hypnos.load()
hypnos.select(ds, drug="propofol", purpose="pk", kernel_only=True)   # filter
hypnos.summary(ds)                                                    # dataset stats
hypnos.performance_table(ds, drug="propofol")                        # published MDPE/MDAPE rows (cited)
ds[model_id].predictive_mdape                                        # in-envelope MDAPE shown in the divergence view
hypnos.simulate(ds, model_id, patient=..., schedule=..., t=...)       # forward sim
hypnos.compare(ds, drug=..., patient=..., schedule=..., t=...)        # divergence view
hypnos.simulate(ds, model_id, ..., bands=True, seed=7)               # v0.2: seeded prediction band -> .ce_quantiles/.band_tier
hypnos.compare(ds, drug=..., ..., bands=True, seed=7)                # v0.2: .divergence[..]["separation"]/["variance_share"]
hypnos.simulate_interaction(ds, surface_id, pk_a=.., pk_b=.., ...)    # two-drug response surface
hypnos.mac(ds, agent_id, age=.., end_tidal_pct=.., n2o_end_tidal_pct=..)  # volatile MAC + fraction
hypnos.washin_comparison(ds)                                         # inhalational wash-in (FA/FI) across agents
hypnos.washout_comparison(ds)                                        # inhalational wash-out (FA/FA₀ emergence) across agents
hypnos.evaluate_covariate("james_1976", dict(weight=130, height=170, sex="M"))  # v0.7: LBM/FFM eval -> .value/.tier/.inverted/.warnings
hypnos.covariate_layer_tier(ds[model_id], patient, ds)              # v0.7: worst tier the model's covariate equations contribute
ds[model_id].covariate_model.derived_inputs[0].equation            # v0.7: the named equation this model is derived with
hypnos.covariate_divergence(ds, model_id, patient=..)              # v0.7 C1: -> .by_equation (per-equation curve + envelope status), .divergence
hypnos.simulate(ds, model_id, patient={"weight":{"mean":130,"sd":13},..}, bands=["covariate"], seed=7)  # v0.7 C2: -> .ce_covariate_band
hypnos.compare(ds, ..., bands=["prediction","covariate"], seed=7)  # v0.7 C2: .divergence[k]["covariate_split"] {equation_choice, value_uncertainty}
hypnos.sample_covariate_vector(patient, rng)                       # v0.7 C2: one seeded covariate draw (never invents a distribution)
hypnos.time_to_peak_effect(ds, model_id, patient=..)                 # onset: tpeak after a bolus
hypnos.decrement_time(ds, model_id, patient=.., infusion=.., duration=..)  # offset: plasma decrement
hypnos.performance_error(c_obs, c_pred)                              # v0.4: Varvel PE% (pred is the denominator)
hypnos.varvel_metrics(pe, times)                                    # v0.4: MDPE / MDAPE / wobble / divergence
hypnos.validate_against_cohort(ds, model_id, subjects, target="cp", seed=7)  # v0.4: forward-validate on a cohort
hypnos.subjects_from_csv(rows)                                      # v0.4: long-format cohort CSV -> SubjectRecord[] (generic adapter)
hypnos.subjects_from_vitaldb(cases)                                 # v0.4 VE1: VitalDB cases -> SubjectRecord[] (PD-BIS); fetch via scripts/fetch_vitaldb.py
res.cp_peak_display, res.concentration_unit                          # conventional units (ng/mL for opioids)
hypnos.verification_summary(ds)                                       # coverage + next-to-verify
hypnos.model_verification(ds, model_id)                              # field-by-field checklist object
hypnos.validate_dataset(ds)                                           # -> list of problems ([] == valid)

Licensing & citation

  • Code: MIT (LICENSE).
  • Dataset: CC-BY-4.0 (LICENSE-DATASET) — each model record is data; attribution required.
  • Citation: CITATION.cff; Zenodo concept DOI on first release. Every model also exposes its primary-source DOI via model.primary_citation — when you use one model, cite Hypnos and the original source.

Roadmap

Phase Content Status
A — Propofol spine Marsh/Schnider/Eleveld PK + ke0 + propofol→BIS; reference kernels; NONMEM/PharmML/SBML/TCI-JSON; round-trip validation; divergence view ✅ complete (Marsh + Schnider + Eleveld all executable; 3-way divergence view live)
B — Opioids + interaction remifentanil (Minto, Eleveld, Kim); propofol–remifentanil response surface; nlmixr2/rxode2 + Pumas export ✅ complete (Minto + Eleveld + Kim all executable; propofol×remifentanil Greco surface; R + Julia export round-tripped)
C — Breadth dexmedetomidine, ketamine, midazolam, fentanyl family; pediatric models with explicit Tier-D labeling ✅ core shipped (dexmedetomidine + the pediatric pair Kataria & Paedfusor executable with explicit pediatric/geriatric extrapolation labeling and a live pediatric model-divergence; fentanyl curated, kernel pending; ketamine/midazolam roadmap)
D — Inhalational + NMB volatile MAC/partition/uptake; neuromuscular blockers + train-of-four; sugammadex reversal ✅ core shipped (4 volatiles with MAC age-correction + additivity + solubility-driven wash-in (FA/FI uptake) and wash-out (FA/FA₀ emergence) all executable; rocuronium seeded with TOF PD; rocuronium PK kernel + sugammadex binding kinetics pending)
E — Hardening external-validation MDPE/MDAPE backfill; COMBINE .omex; Zenodo DOI ✅ core shipped (deterministic .omex + BibTeX exporters; scripts/regenerate.py; .zenodo.json + CHANGELOG.md; external-validation MDPE/MDAPE backfilled across both headline drugs + dexmedetomidine, citation-integrity-checked and surfaced via hypnos performance; minted DOI on first tagged release)
v0.2 — Population-variability layer curate Ω/Σ random effects; seeded prediction bands; uncertainty-aware divergence (separation index + variance decomposition); export the NLME object (spec) 🟢 V0–V3 export code shipped (Eleveld propofol Ω-diagonal + Σ curated unverified with the §4-trap validate checks; simulate/compare --bands draw seeded, reproducible bands with the never-synthesize rule; every export carries the random-effects layer — NONMEM $OMEGA/$OMEGA BLOCK/$SIGMA, PharmML VariabilityModel, nlmixr2/rxode2 + Pumas NLME companions, TCI-JSON, and SBML (Ω/Σ as hypnos: RDF); the dashboard renders the bands as shaded ribbons with the separation-index + variance-decomposition readout; the PD effect (BIS) band propagates PK BSV through the Hill link as an honest lower bound). BSV backfill largely done (sourced, pending_human_review): Minto (from its own NONMEM $OMEGA in the Open-TCI archive), Kim, and Schnider (partial) Ω diagonals curated alongside Eleveld — so the separation index now computes across multiple band-eligible models. Remaining: Hannivoort dexmedetomidine Ω (paywalled) and the PD-parameter (Ce50, γ) Ω
v0.3 — Estimation-uncertainty layer 🟢 E0–E3 shipped — the per-θ estimation_uncertainty/estimate_covariance schema + validate traps + the verify group (E0); seeded confidence bands simulate(bands=["confidence"]) (E1, far narrower than the prediction band); the estimation component folded into compare's variance decomposition (E2); the per-θ estimation block in the NONMEM export (E3). Eleveld 2018 propofol carries SOURCED per-θ estimation uncertainty (pending_human_review; an agent read the open-access Table 2's 99% profile-likelihood CIs directly). Remaining (never-invent data): RSE backfill for the other models
v0.4 — External-validation layer 🟢 VE0–VE3 shipped — the Varvel engine + validate_against_cohort; the VitalDB (BIS) and Open-TCI (cp) adapters; the cross-model, envelope-stratified leaderboard (cross_model_leaderboard/hypnos leaderboard), run on 30 real VitalDB cases (all propofol PK→BIS stacks at MDPE ≈ −33%, the synergy bias); the advisory tier-falsification flag; and the VPC (visual_predictive_check). Remaining: committed live metrics (terms-gated) + cross-model RSE backfill
v0.5 — Breadth (reversal + special populations) 🟢 COMPLETE (S0/S1/B0/B1) — the organ-function envelope + the binding failure mode (S0/S1); the reversal subsystem — sugammadex encapsulation (rocuronium→TOF recovers ~1%→98%), naloxone competitive antagonism with the renarcotization relapse, neostigmine indirect inhibition with the deep-block ceiling — via simulate_reversal + hypnos reverse; and the opt-in, Tier-D disease_state_modifier (dexmedetomidine clearance reduced in hepatic impairment, De Wolf 2001). The rocuronium PK kernel is now implemented (sourced, pending_human_review)
v0.6 — Local anesthetics (LAST) site-driven absorption; toxicity-threshold ranges; stereochemistry/cardiotoxicity; binding saturation (spec) 🟢 COMPLETE (LA0–LA3) — the local_anesthetics subsystem: disposition + site-specific absorption + binding (LA0); the toxicity_threshold ranges + the double-uncertainty view + range-not-line/saturable→caveat enforcement + hypnos:safetyCritical RDF (Knudsen 1997 / Scott 1989; LA1); the drug-level cardiotoxicity_class + agent-choice axis (hypnos la --agents) + levobupivacaine (Bardsley 1998) + the CNS-to-CVS fold-margin monotone with the ranking (LA2); the non-linear capacity-limited free-fraction kernel (la.saturable_free_concentration, affinity pinned by the curated fraction_bound, Tier-D illustrative) making total's under-prediction of free-drug risk visible (LA3). Lidocaine/bupivacaine/levobupivacaine/ropivacaine. The reversal subsystem (v0.5 Part A) is the next breadth target — blocked until a verified rocuronium PK kernel lands
v0.7 — Covariate-model uncertainty 🟢 COMPLETE (C0–C3) — the cited covariate-equation library + covariate_model bindings + covariates.evaluate with the James inversion as a tested property (C0); the equation-divergence view (C1); the covariate-value band + the fifth variance component (C2); and covariate-aware exports (C3) — NONMEM $PK computes the named LBM/FFM equation the model's way (round-trip-equal to the library), SBML/PharmML carry it as hypnos:covariateEquation RDF, TCI-JSON passes the block through verbatim
v0.8 — Developmental pharmacology allometric size + PMA clearance-maturation as a cited, opt-in, Tier-D extrapolation of adult models; fitted-pediatric evidence kept distinct; the linear-per-kg shortcut shown visibly wrong (spec) 🟢 COMPLETE (D0–D3) — the hypnos.developmental kernels (allometric_scale ¾/1, maturation_factor Anderson–Holford, PMA-only), the developmental_model schema block + pma_weeks/postnatal_age_days/gestational_age_weeks envelope dims, the opt-in simulate(..., developmental=True) (forces Tier-D, refuses-not-invents), the developmental_overlay/hypnos developmental headline (fitted vs extrapolated vs visibly-wrong shortcut), the five-trap validator + verification group (PMA-clock first), and the hypnos:developmentalModel/maturationFunction SBML RDF. Marsh (allometry_plus_maturation) + Schnider (allometry_only) curated Tier-D unverified; Eleveld labeled fitted-pediatric evidence. Next (data, not code): drug-specific maturation TM50/Hill verification; pathway-specific maturation
v0.9 — Pharmacogenomics genotype as a declared envelope axis: cited, opt-in, Tier-D kinetic modifiers vs safetyCritical avoidance/awareness flags — a susceptibility is never a number (spec) 🟢 COMPLETE (G0–G3) — the disjoint pharmacogenomic_modifier/pharmacogenomic_safety_flag schema (affects_kinetics False by construction), the declared-only genotype dims + never-infer rule, the avoidance-first pgx_overlay/hypnos pgx, the substrate guardrails (BCHE excludes remifentanil/amide-LAs/rocuronium, code + validator), the opt-in simulate(..., pharmacogenomics=True) modifier + the evaluate_safety flag surfacing (a warning, never a number), and the hypnos:pharmacogenomicModifier/SafetyFlag RDF. RYR1→MH avoidance on the volatiles + succinylcholine; atypical-BCHE prolonged-block flag + Tier-D modifier on a new succinylcholine record (kernel-pending). Next (data, not code): CYP2D6/CYP3A opioid modifiers; the succinylcholine PK kernel that unlocks the prolonged-recovery band

Hypnos is the honest, reusable ground-truth layer beneath anesthetic simulation, built on one principle: a simulation is only as trustworthy as its weakest, least-validated input — so make that fact a first-class, machine-readable field.

About

A curated, citation-backed dataset of anesthetic and perioperative PK/PD model parameters featuring machine-enforced applicability envelopes, forward simulation, model-divergence visualization, and automated exports to standard pharmacometric formats like NONMEM, SBML, and Pumas.

Topics

Resources

License

MIT, Unknown licenses found

Licenses found

MIT
LICENSE
Unknown
LICENSE-DATASET

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors