S-ketamine oral thin film (Simons 2022)
Source:vignettes/articles/Simons_2022_s_ketamine.Rmd
Simons_2022_s_ketamine.RmdModel and source
- Citation: Simons P, Olofsen E, van Velzen M, van Lemmen M, Mooren R, van Dasselaar T, Mohr P, Hammes F, van der Schrier R, Niesters M, Dahan A. S-Ketamine Oral Thin Film-Part 1: Population Pharmacokinetics of S-Ketamine, S-Norketamine and S-Hydroxynorketamine. Front Pain Res. 2022;3:946486. doi:10.3389/fpain.2022.946486
- Description: Joint population PK model of S-ketamine
(three-compartment), S-norketamine (two-compartment) and
S-hydroxynorketamine (two-compartment) after a sublingual or buccal
S-ketamine oral thin film (50 or 100 mg) followed by a 20 mg intravenous
S-ketamine infusion in healthy adult volunteers (Simons 2022). The film
dose is absorbed by two parallel routes: an oral-mucosal route
(bioavailability F1 = 26.3%, zero-order input over D1 = 13.1 min into a
depot drained first order by KA1 into the S-ketamine central
compartment) and a swallowed route (F2 = 116% of the film dose,
zero-order input over D2 = 29.9 min into a gut depot drained by KA2,
then a gut-to-liver delay compartment with mean transit time MTTG) that
delivers S-ketamine directly into hepatic metabolism without reaching
the systemic S-ketamine pool. Both the swallowed S-ketamine and systemic
S-ketamine cleared by CLK1 pass through a two-compartment metabolism
delay chain (mean transit time 20.1 min) of which a fixed 80% forms
S-norketamine; S-norketamine cleared by CLN1 passes through a second
two-compartment chain (1.12 min) of which a fixed 70% forms
S-hydroxynorketamine. The S-norketamine central volume equals the
S-ketamine central volume. Clearances and volumes are referenced to 70
kg. Every OTF dose needs TWO dose records of the full film dose with
rate = -2, one into
depotand one intodepot2; concentrations are in nmol/mL. - Article: https://doi.org/10.3389/fpain.2022.946486 (open access; the supplementary Data Sheet 1 holds the individual data fits only)
The companion paper (Simons 2022 Part 2, https://doi.org/10.3389/fpain.2022.946487) analyses the pharmacodynamics using the empirical Bayes estimates of this PK model and does not restate it.
Population
Twenty healthy volunteers (10 men, 10 women; age 24 +/- 3 years, range 19-32; weight 73 +/- 12 kg, range 53-93; BMI 23 +/- 2 kg/m^2; Simons 2022 Table 1) took part in an open-label randomised crossover study at Leiden University Medical Center. On one visit they received one S-ketamine oral thin film (50 mg S-ketamine free base) and on the other two films (100 mg), at least 7 days apart. The films were placed sublingually (n = 15) or buccally (n = 5) and the subjects were not allowed to swallow for 10 min. Six hours after film placement every subject received 20 mg S-ketamine intravenously over 20 min, which anchors the absolute bioavailability. Arterial S-ketamine, S-norketamine and S-hydroxynorketamine were measured for 8 h per visit. 19 subjects completed both visits. Sublingual and buccal placement were pooled because no pharmacokinetic difference was seen.
The same information is available programmatically via
readModelDb("Simons_2022_s_ketamine")()$population.
Model structure
Simons 2022 Figure 2 shows the final model. The film dose is absorbed by two parallel routes:
- Oral mucosa: a fraction F1 enters a depot by zero-order input over D1 and is absorbed first order (KA1) into the S-ketamine central compartment.
- Swallowed: a fraction F2 enters a gut depot by zero-order input over D2. It passes first order (KA2) into a gut-to-liver delay compartment (mean transit time MTTG) and then directly into hepatic S-ketamine metabolism. It never reaches the systemic S-ketamine pool.
S-ketamine has three compartments. Clearance CLK1, together with the swallowed route, feeds a two-compartment metabolism delay chain (MTT K->NK). A fixed 80% of the chain’s output forms S-norketamine and the rest is lost. S-norketamine has two compartments and shares the S-ketamine central volume (Figure 2, “VN1 = VK1”). Its clearance CLN1 feeds a second two-compartment chain (MTT NK->HNK), of which a fixed 70% forms S-hydroxynorketamine. S-hydroxynorketamine has two compartments and a terminal clearance CLH1.
Dosing. Each film dose needs two dose records of the
full film dose, both with rate = -2 so
that the modelled durations D1 and D2 apply. One goes into
depot (bioavailability F1) and one into depot2
(bioavailability F2). An intravenous dose goes into
central. Concentrations are in nmol/mL. Multiply by 237.73,
223.70 or 239.70 g/mol for ng/mL of S-ketamine, S-norketamine or
S-hydroxynorketamine.
Source trace
Every ini() value carries an in-file comment pointing to
its source in
inst/modeldb/specificDrugs/Simons_2022_s_ketamine.R. All
values are from Simons 2022 Table 4 unless stated otherwise.
| Equation / parameter | Value | Source location |
|---|---|---|
lfdepot (F1) |
26.3% | Table 4 |
ld1 (D1) |
13.1 min | Table 4 |
lka (KA1) |
0.04 1/min | Table 4 |
lfdepot2 (F2) |
116% | Table 4 |
ld2 (D2) |
29.9 min | Table 4 |
lka2 (KA2) |
0.049 1/min | Table 4 |
lmtt (MTTG) |
10.7 min | Table 4 |
lvc, lvp, lvp2 (VK1, VK2,
VK3) |
11.6, 39.0, 174 L | Table 4 |
lcl, lq, lq2 (CLK1, CLK2,
CLK3) |
1.48, 2.43, 1.21 L/min | Table 4 |
lmtt_snk (MTT K->NK) |
20.1 min | Table 4 |
fm_snk |
0.8 (fixed) | Methods “Population Pharmacokinetic Analysis”; Figure 2 “20% loss” |
vc_snk = vc (VN1 = VK1) |
11.6 L | Table 4; Figure 2 |
lvp_snk, lcl_snk, lq_snk
(VN2, CLN1, CLN2) |
221 L, 1.00, 2.63 L/min | Table 4 |
lmtt_shnk (MTT NK->HNK) |
1.12 min | Table 4 |
fm_shnk |
0.7 (fixed) | Methods “Population Pharmacokinetic Analysis”; Figure 2 “30% loss” |
lvc_shnk, lvp_shnk (VH1, VH2) |
4.4, 87.5 L | Table 4 |
lcl_shnk, lq_shnk (CLH1, CLH2) |
0.933, 1.70 L/min | Table 4 |
e_wt_cl_q, e_wt_vc_vp
|
0.75, 1 (fixed) | Not printed; Table 4 reports values “@ 70 kg” (see Assumptions) |
| IIV (omega^2) on VK1/VN1, CLK1, CLK3, CLN1, MTT K->NK, VH1, CLH1, CLH2 | 0.057, 0.029, 0.026, 0.050, 0.021, 1.22, 0.103, 0.287 | Table 4 |
| IOV (nu^2) on F1, D1, KA1, F2, D2, KA2, MTTG | 0.060, 0.154, 0.062, 0.057, 0.611, 0.376, 0.937 | Table 4 |
| IOV (nu^2) on MTT K->NK | 0.751 | Table 4, printed on the S-norketamine additive-error row (see Assumptions) |
| IOV (nu^2) on VH2, CLH1 | 0.152, 0.008 | Table 4 |
propSd (S-ketamine) |
0.1095 | Table 4 sigma 0.012 read as a variance (see Assumptions) |
propSd_snk, addSd_snk
|
0.102, 0.058 nmol/mL | Table 4 |
propSd_shnk, addSd_shnk
|
0.079, 0.020 nmol/mL | Table 4 |
Exponential random effects,
theta_i = theta * exp(eta_i + eta_iov)
|
n/a | Methods “Population Pharmacokinetic Analysis” |
Two absorption routes, metabolism chains,
VN1 = VK1
|
n/a | Figure 2; Results “Population Pharmacokinetic Analysis” |
Two-compartment chains with rate 2 / MTT
|
n/a | Results text; see “Structural readings” below |
Virtual cohort
Observed data are not public. The cohort below approximates Table 1: body weight is normal with mean 73 kg and SD 12 kg, truncated to the observed 53-93 kg. Each arm has 100 virtual subjects. The 50 mg visit is occasion 1 and the 100 mg visit is occasion 2; each simulated subject contributes a single visit. The inter-occasion variances are the same on both occasions, so this is equivalent to the crossover.
set.seed(2022)
rxode2::rxSetSeed(2022)
mw <- c(ketamine = 237.73, norketamine = 223.70, hnk = 239.70)
# Sampling times of the study (Simons 2022 Methods): film phase and the
# intravenous phase (minutes after the start of the 360-min infusion).
t_film <- c(0, 5, 10, 20, 40, 60, 90, 120, 180, 240, 300, 360)
t_iv <- 360 + c(2, 4, 10, 15, 20, 30, 40, 60, 75, 90, 120)
t_obs <- sort(unique(c(t_film, t_iv, seq(0, 480, by = 2.5))))
make_cohort <- function(n, film_mg, occ, treatment, id_offset = 0L) {
subj <- tibble(
id = id_offset + seq_len(n),
WT = pmin(pmax(rnorm(n, 73, 12), 53), 93)
)
doses <- bind_rows(
subj |> mutate(time = 0, amt = film_mg, rate = -2, cmt = "depot"),
subj |> mutate(time = 0, amt = film_mg, rate = -2, cmt = "depot2"),
subj |> mutate(time = 360, amt = 20, rate = 1, cmt = "central")
) |>
mutate(evid = 1L, dvid = NA_integer_)
obs <- tidyr::crossing(subj, time = t_obs) |>
mutate(amt = 0, rate = 0, cmt = NA_character_, evid = 0L, dvid = 1L)
bind_rows(doses, obs) |>
mutate(OCC = occ, treatment = treatment) |>
arrange(id, time, desc(evid))
}
events <- bind_rows(
make_cohort(100, 50, occ = 1L, treatment = "50 mg OTF", id_offset = 0L),
make_cohort(100, 100, occ = 2L, treatment = "100 mg OTF", id_offset = 100L)
)
stopifnot(!anyDuplicated(unique(events[, c("id", "time", "evid", "cmt")])))Simulation
mod <- readModelDb("Simons_2022_s_ketamine")
sim <- rxode2::rxSolve(mod, events = events, keep = c("treatment", "WT")) |>
as.data.frame() |>
mutate(
ketamine = Cc * mw[["ketamine"]],
norketamine = Cc_snk * mw[["norketamine"]],
hnk = Cc_shnk * mw[["hnk"]],
treatment = factor(treatment, levels = c("50 mg OTF", "100 mg OTF"))
)
#> ℹ parameter labels from comments will be replaced by 'label()'
#> Warning: some etas defaulted to non-mu referenced, possible parsing error: etaiov_fdepot_1, etaiov_fdepot_2, etaiov_d1_1, etaiov_d1_2, etaiov_ka_1, etaiov_ka_2, etaiov_fdepot2_1, etaiov_fdepot2_2, etaiov_d2_1, etaiov_d2_2, etaiov_ka2_1, etaiov_ka2_2, etaiov_mtt_1, etaiov_mtt_2, etaiov_mtt_snk_1, etaiov_mtt_snk_2, etaiov_vp_shnk_1, etaiov_vp_shnk_2, etaiov_cl_shnk_1, etaiov_cl_shnk_2
#> as a work-around try putting the mu-referenced expression on a simple line
stopifnot(!anyNA(sim$Cc), !anyNA(sim$Cc_snk), !anyNA(sim$Cc_shnk))Replicate Figure 1: mean concentrations
analyte_labels <- c(
ketamine = "S-ketamine",
norketamine = "S-norketamine",
hnk = "S-hydroxynorketamine"
)
sim |>
select(id, time, treatment, all_of(names(analyte_labels))) |>
pivot_longer(all_of(names(analyte_labels)), names_to = "analyte", values_to = "conc") |>
mutate(analyte = factor(analyte_labels[analyte], levels = analyte_labels)) |>
group_by(time, treatment, analyte) |>
summarise(
mean = mean(conc),
Q05 = quantile(conc, 0.05),
Q95 = quantile(conc, 0.95),
.groups = "drop"
) |>
ggplot(aes(time, mean, colour = treatment, fill = treatment)) +
geom_ribbon(aes(ymin = Q05, ymax = Q95), alpha = 0.15, colour = NA) +
geom_line() +
facet_wrap(~analyte, scales = "free_y") +
scale_x_continuous(breaks = seq(0, 480, by = 120)) +
labs(
x = "Time after film placement (min)",
y = "Plasma concentration (ng/mL)",
colour = NULL, fill = NULL,
caption = paste(
"Lines: simulated mean; ribbon: 5th-95th percentile.",
"Replicates Figure 1A-C of Simons 2022 (20 mg IV S-ketamine at 360 min)."
)
) +
theme(legend.position = "bottom")
The shape matches Figure 1. The S-ketamine peak is near 20 min, the S-norketamine peak near 60 min and the S-hydroxynorketamine peak later and flatter. The intravenous infusion produces a sharp S-ketamine peak at 380 min and only a small rise in the metabolites.
PKNCA validation against Table 2
Table 2 reports the mean Cmax, Tmax and AUC over 0-6 h of each analyte after each film dose, computed from the study’s sampling times. The simulation is therefore subset to the same sampling times over 0-360 min, before the intravenous dose. Simulated values are summarised as means to match the table. Each analyte gets its own PKNCA block.
dose_df <- events |>
filter(evid == 1, cmt == "depot") |>
select(id, time, amt, treatment)
nca_one_analyte <- function(analyte) {
conc_df <- sim |>
filter(time %in% t_film) |>
transmute(id, time, treatment = as.character(treatment), Cc = .data[[analyte]]) |>
filter(!is.na(Cc))
conc_obj <- PKNCA::PKNCAconc(conc_df, Cc ~ time | treatment + id)
dose_obj <- PKNCA::PKNCAdose(dose_df, amt ~ time | treatment + id)
intervals <- data.frame(start = 0, end = 360, cmax = TRUE, tmax = TRUE, auclast = TRUE)
res <- PKNCA::pk.nca(PKNCA::PKNCAdata(conc_obj, dose_obj, intervals = intervals))
as.data.frame(res) |>
group_by(treatment, PPTESTCD) |>
summarise(PPORRES = mean(PPORRES), .groups = "drop")
}
# Simons 2022 Table 2 (mean; ng/mL, min, ng*min/mL)
published <- tribble(
~analyte, ~treatment, ~cmax, ~tmax, ~auclast,
"ketamine", "50 mg OTF", 96, 18.8, 8363,
"ketamine", "100 mg OTF", 144, 19.1, 13347,
"norketamine", "50 mg OTF", 276, 61, 38497,
"norketamine", "100 mg OTF", 426, 78, 67959,
"hnk", "50 mg OTF", 101, 81, 24087,
"hnk", "100 mg OTF", 189, 109, 44972
)
cmp <- bind_rows(lapply(names(analyte_labels), function(a) {
tbl <- nlmixr2lib::ncaComparisonTable(
simulated = nca_one_analyte(a),
reference = published |> filter(analyte == a) |> select(-analyte),
by = "treatment",
units = c(cmax = "ng/mL", tmax = "min", auclast = "ng*min/mL"),
tolerance_pct = 20
)
tibble(Analyte = analyte_labels[[a]], tbl)
}))
knitr::kable(
cmp,
caption = "Simulated (mean of 100 virtual subjects per arm) vs. Simons 2022 Table 2. * differs from the reference by more than 20%."
)| Analyte | NCA parameter | treatment | Reference | Simulated | % diff |
|---|---|---|---|---|---|
| S-ketamine | Cmax (ng/mL) | 50 mg OTF | 96 | 81.8 | -14.8% |
| S-ketamine | Cmax (ng/mL) | 100 mg OTF | 144 | 169 | +17.2% |
| S-ketamine | Tmax (min) | 50 mg OTF | 18.8 | 19.7 | +4.8% |
| S-ketamine | Tmax (min) | 100 mg OTF | 19.1 | 20.6 | +7.9% |
| S-ketamine | AUClast (ng*min/mL) | 50 mg OTF | 8360 | 7250 | -13.3% |
| S-ketamine | AUClast (ng*min/mL) | 100 mg OTF | 13300 | 15200 | +13.8% |
| S-norketamine | Cmax (ng/mL) | 50 mg OTF | 276 | 201 | -27.0%* |
| S-norketamine | Cmax (ng/mL) | 100 mg OTF | 426 | 430 | +0.9% |
| S-norketamine | Tmax (min) | 50 mg OTF | 61 | 87.5 | +43.4%* |
| S-norketamine | Tmax (min) | 100 mg OTF | 78 | 78.3 | +0.4% |
| S-norketamine | AUClast (ng*min/mL) | 50 mg OTF | 38500 | 34600 | -10.2% |
| S-norketamine | AUClast (ng*min/mL) | 100 mg OTF | 68000 | 71900 | +5.9% |
| S-hydroxynorketamine | Cmax (ng/mL) | 50 mg OTF | 101 | 85.7 | -15.1% |
| S-hydroxynorketamine | Cmax (ng/mL) | 100 mg OTF | 189 | 182 | -4.0% |
| S-hydroxynorketamine | Tmax (min) | 50 mg OTF | 81 | 132 | +62.6%* |
| S-hydroxynorketamine | Tmax (min) | 100 mg OTF | 109 | 114 | +4.6% |
| S-hydroxynorketamine | AUClast (ng*min/mL) | 50 mg OTF | 24100 | 21000 | -12.7% |
| S-hydroxynorketamine | AUClast (ng*min/mL) | 100 mg OTF | 45000 | 43900 | -2.4% |
pct <- cmp[["% diff"]]
pct <- as.numeric(gsub("[%*+]", "", pct))
stopifnot(!anyNA(pct))
auc_rows <- grepl("AUC", cmp[["NCA parameter"]])
stopifnot(
# A mis-transcribed clearance, fraction or unit moves the exposures by tens
# of percent; the centre of the AUC comparison guards against that.
abs(median(pct[auc_rows])) < 15,
# Envelope over all six AUC rows, robust to which subjects land in the tails.
max(abs(pct[auc_rows])) < 35
)All six AUC0-6h values agree with Table 2 to within about 15%. The model is linear, so its exposures double with the dose. The observed means in Table 2 rise less than proportionally, which leaves the model lower than observed at 50 mg and higher at 100 mg:
- S-ketamine Cmax rises 1.5-fold and AUC 1.6-fold from 50 to 100 mg. The paper attributes this to a lower film bioavailability at 100 mg (F1 29% vs 23%), which did not reach significance, so the final model pools F1 at 26.3%.
- S-norketamine Cmax rises only 1.5-fold (276 to 426 ng/mL), against 1.8-fold for its AUC. The 50 mg S-norketamine Cmax is therefore the largest Cmax difference.
The Tmax rows compare the mean of per-subject Tmax values read off the sampling times. The S-norketamine and S-hydroxynorketamine profiles are broad and flat near their peaks (Figure 1B-C), so a small change in shape moves an individual’s Tmax by a whole sampling interval (30 or 60 min). Those rows are noisy and are not used in the assertion. The typical-value Tmax values below, on a 0.5-min grid, are 61.5 min for S-norketamine (Table 2: 61 and 78 min) and 76 min for S-hydroxynorketamine (Table 2: 81 and 109 min).
Typical-value checks
The typical subject (70 kg, no random effects) gives a deterministic check that does not depend on the simulated cohort.
mod_typical <- rxode2::zeroRe(mod)
#> ℹ parameter labels from comments will be replaced by 'label()'
#> Warning: some etas defaulted to non-mu referenced, possible parsing error: etaiov_fdepot_1, etaiov_fdepot_2, etaiov_d1_1, etaiov_d1_2, etaiov_ka_1, etaiov_ka_2, etaiov_fdepot2_1, etaiov_fdepot2_2, etaiov_d2_1, etaiov_d2_2, etaiov_ka2_1, etaiov_ka2_2, etaiov_mtt_1, etaiov_mtt_2, etaiov_mtt_snk_1, etaiov_mtt_snk_2, etaiov_vp_shnk_1, etaiov_vp_shnk_2, etaiov_cl_shnk_1, etaiov_cl_shnk_2
#> as a work-around try putting the mu-referenced expression on a simple line
#> Warning: some etas defaulted to non-mu referenced, possible parsing error: etaiov_fdepot_1, etaiov_fdepot_2, etaiov_d1_1, etaiov_d1_2, etaiov_ka_1, etaiov_ka_2, etaiov_fdepot2_1, etaiov_fdepot2_2, etaiov_d2_1, etaiov_d2_2, etaiov_ka2_1, etaiov_ka2_2, etaiov_mtt_1, etaiov_mtt_2, etaiov_mtt_snk_1, etaiov_mtt_snk_2, etaiov_vp_shnk_1, etaiov_vp_shnk_2, etaiov_cl_shnk_1, etaiov_cl_shnk_2
#> as a work-around try putting the mu-referenced expression on a simple line
typical_events <- function(film_mg, iv_mg = 20, iv_dur = 20, iv_time = 360) {
doses <- tibble(
id = 1L, time = c(0, 0, iv_time), amt = c(film_mg, film_mg, iv_mg),
rate = c(-2, -2, iv_mg / iv_dur), cmt = c("depot", "depot2", "central"),
evid = 1L, dvid = NA_integer_
) |>
filter(amt > 0)
obs <- tibble(
id = 1L, time = seq(0, 480, by = 0.5), amt = 0, rate = 0,
cmt = NA_character_, evid = 0L, dvid = 1L
)
bind_rows(doses, obs) |>
mutate(WT = 70, OCC = 1L) |>
arrange(time, desc(evid))
}
typical_summary <- function(model, film_mg, params = NULL) {
s <- rxode2::rxSolve(model, events = typical_events(film_mg), params = params) |>
as.data.frame()
film <- s[s$time <= 360, ]
auc <- function(x) sum(diff(film$time) * (head(x, -1) + tail(x, -1)) / 2)
tibble(
film_mg = film_mg,
ketamine_cmax = max(film$Cc) * mw[["ketamine"]],
norketamine_tmax = film$time[which.max(film$Cc_snk)],
norketamine_auc = auc(film$Cc_snk) * mw[["norketamine"]],
hnk_tmax = film$time[which.max(film$Cc_shnk)],
hnk_auc = auc(film$Cc_shnk) * mw[["hnk"]],
iv_peak_ketamine = max(s$Cc[s$time > 360]) * mw[["ketamine"]]
)
}
tv <- bind_rows(typical_summary(mod_typical, 50), typical_summary(mod_typical, 100))
#> ℹ omega/sigma items treated as zero: 'etalvc', 'etalcl', 'etalq2', 'etalcl_snk', 'etalmtt_snk', 'etalvc_shnk', 'etalcl_shnk', 'etalq_shnk', 'etaiov_fdepot_1', 'etaiov_fdepot_2', 'etaiov_d1_1', 'etaiov_d1_2', 'etaiov_ka_1', 'etaiov_ka_2', 'etaiov_fdepot2_1', 'etaiov_fdepot2_2', 'etaiov_d2_1', 'etaiov_d2_2', 'etaiov_ka2_1', 'etaiov_ka2_2', 'etaiov_mtt_1', 'etaiov_mtt_2', 'etaiov_mtt_snk_1', 'etaiov_mtt_snk_2', 'etaiov_vp_shnk_1', 'etaiov_vp_shnk_2', 'etaiov_cl_shnk_1', 'etaiov_cl_shnk_2'
#> ℹ omega/sigma items treated as zero: 'etalvc', 'etalcl', 'etalq2', 'etalcl_snk', 'etalmtt_snk', 'etalvc_shnk', 'etalcl_shnk', 'etalq_shnk', 'etaiov_fdepot_1', 'etaiov_fdepot_2', 'etaiov_d1_1', 'etaiov_d1_2', 'etaiov_ka_1', 'etaiov_ka_2', 'etaiov_fdepot2_1', 'etaiov_fdepot2_2', 'etaiov_d2_1', 'etaiov_d2_2', 'etaiov_ka2_1', 'etaiov_ka2_2', 'etaiov_mtt_1', 'etaiov_mtt_2', 'etaiov_mtt_snk_1', 'etaiov_mtt_snk_2', 'etaiov_vp_shnk_1', 'etaiov_vp_shnk_2', 'etaiov_cl_shnk_1', 'etaiov_cl_shnk_2'
knitr::kable(tv, digits = 1, caption = "Typical-value (70 kg) summary.")| film_mg | ketamine_cmax | norketamine_tmax | norketamine_auc | hnk_tmax | hnk_auc | iv_peak_ketamine |
|---|---|---|---|---|---|---|
| 50 | 85.9 | 61.5 | 36287.4 | 76 | 23367.4 | 272.7 |
| 100 | 171.8 | 61.5 | 72574.8 | 76 | 46734.9 | 277.4 |
stopifnot(
# Results text: mean S-ketamine peak after the IV dose 273 ng/mL (50 mg
# visit) and 260 ng/mL (100 mg visit).
abs(tv$iv_peak_ketamine / c(273, 260) - 1) < 0.15,
# Table 2 AUC0-6h of the metabolites
abs(tv$norketamine_auc / c(38497, 67959) - 1) < 0.15,
abs(tv$hnk_auc / c(24087, 44972) - 1) < 0.15
)Structural readings
The paper does not print its equations. Three readings were settled against its own Table 2 rather than assumed. The two alternatives can be expressed as parameter overrides of the packaged model and are simulated below:
- What F2 is a fraction of. In the packaged model the swallowed route receives F2 times the full film dose. This is the usual NONMEM coding, with one dose record per absorption compartment and a bioavailability on each. The alternative is that F2 applies to the swallowed remainder (1 - F1) of the dose. That is equivalent to F2 = (1 - 0.263) x 1.16 = 0.855.
- Rate in the metabolism chains. The paper’s mean transit time is read as the total delay of each two-compartment chain, so each compartment drains at 2 / MTT. The alternative, 1 / MTT per compartment, is equivalent to doubling the MTT.
readings <- bind_rows(
typical_summary(mod_typical, 50) |> mutate(reading = "Packaged model"),
typical_summary(mod_typical, 50, params = c(lfdepot2 = log((1 - 0.263) * 1.16))) |>
mutate(reading = "F2 applied to (1 - F1) of the dose"),
typical_summary(mod_typical, 50, params = c(lmtt_snk = log(2 * 20.1), lmtt_shnk = log(2 * 1.12))) |>
mutate(reading = "Metabolism chains at 1 / MTT per compartment")
) |>
select(reading, norketamine_tmax, norketamine_auc, hnk_auc)
#> ℹ omega/sigma items treated as zero: 'etalvc', 'etalcl', 'etalq2', 'etalcl_snk', 'etalmtt_snk', 'etalvc_shnk', 'etalcl_shnk', 'etalq_shnk', 'etaiov_fdepot_1', 'etaiov_fdepot_2', 'etaiov_d1_1', 'etaiov_d1_2', 'etaiov_ka_1', 'etaiov_ka_2', 'etaiov_fdepot2_1', 'etaiov_fdepot2_2', 'etaiov_d2_1', 'etaiov_d2_2', 'etaiov_ka2_1', 'etaiov_ka2_2', 'etaiov_mtt_1', 'etaiov_mtt_2', 'etaiov_mtt_snk_1', 'etaiov_mtt_snk_2', 'etaiov_vp_shnk_1', 'etaiov_vp_shnk_2', 'etaiov_cl_shnk_1', 'etaiov_cl_shnk_2'
#> ℹ omega/sigma items treated as zero: 'etalvc', 'etalcl', 'etalq2', 'etalcl_snk', 'etalmtt_snk', 'etalvc_shnk', 'etalcl_shnk', 'etalq_shnk', 'etaiov_fdepot_1', 'etaiov_fdepot_2', 'etaiov_d1_1', 'etaiov_d1_2', 'etaiov_ka_1', 'etaiov_ka_2', 'etaiov_fdepot2_1', 'etaiov_fdepot2_2', 'etaiov_d2_1', 'etaiov_d2_2', 'etaiov_ka2_1', 'etaiov_ka2_2', 'etaiov_mtt_1', 'etaiov_mtt_2', 'etaiov_mtt_snk_1', 'etaiov_mtt_snk_2', 'etaiov_vp_shnk_1', 'etaiov_vp_shnk_2', 'etaiov_cl_shnk_1', 'etaiov_cl_shnk_2'
#> ℹ omega/sigma items treated as zero: 'etalvc', 'etalcl', 'etalq2', 'etalcl_snk', 'etalmtt_snk', 'etalvc_shnk', 'etalcl_shnk', 'etalq_shnk', 'etaiov_fdepot_1', 'etaiov_fdepot_2', 'etaiov_d1_1', 'etaiov_d1_2', 'etaiov_ka_1', 'etaiov_ka_2', 'etaiov_fdepot2_1', 'etaiov_fdepot2_2', 'etaiov_d2_1', 'etaiov_d2_2', 'etaiov_ka2_1', 'etaiov_ka2_2', 'etaiov_mtt_1', 'etaiov_mtt_2', 'etaiov_mtt_snk_1', 'etaiov_mtt_snk_2', 'etaiov_vp_shnk_1', 'etaiov_vp_shnk_2', 'etaiov_cl_shnk_1', 'etaiov_cl_shnk_2'
readings |>
rename(
"Reading" = reading,
"S-norketamine Tmax (min)" = norketamine_tmax,
"S-norketamine AUC0-6h (ng*min/mL)" = norketamine_auc,
"S-HNK AUC0-6h (ng*min/mL)" = hnk_auc
) |>
knitr::kable(
digits = 0,
caption = "Typical 50 mg film under each reading. Table 2 means: S-norketamine Tmax 61 min, AUC 38497; S-HNK AUC 24087."
)| Reading | S-norketamine Tmax (min) | S-norketamine AUC0-6h (ng*min/mL) | S-HNK AUC0-6h (ng*min/mL) |
|---|---|---|---|
| Packaged model | 62 | 36287 | 23367 |
| F2 applied to (1 - F1) of the dose | 62 | 28144 | 18089 |
| Metabolism chains at 1 / MTT per compartment | 82 | 35176 | 22184 |
stopifnot(
# The packaged reading is the one that reproduces Table 2 ...
abs(readings$norketamine_auc[1] / 38497 - 1) < 0.15,
abs(readings$norketamine_tmax[1] - 61) < 10,
# ... and each alternative moves away from it.
readings$norketamine_auc[2] / 38497 < 0.8,
readings$norketamine_tmax[3] - 61 > 15
)Replicate Figure 5: intravenous infusion vs. 100 mg film
Figure 5 compares S-ketamine and S-hydroxynorketamine after 0.5 mg/kg S-ketamine given intravenously over 40 min to a 70 kg individual (35 mg) with the profiles after the 100 mg film.
iv_events <- typical_events(film_mg = 0, iv_mg = 35, iv_dur = 40, iv_time = 0)
fig5 <- bind_rows(
rxode2::rxSolve(mod_typical, events = iv_events) |> as.data.frame() |>
mutate(regimen = "0.5 mg/kg IV over 40 min"),
rxode2::rxSolve(mod_typical, events = typical_events(100, iv_mg = 0)) |> as.data.frame() |>
mutate(regimen = "100 mg oral thin film")
) |>
filter(time <= 360) |>
transmute(
time, regimen,
`S-ketamine` = Cc * mw[["ketamine"]],
`S-hydroxynorketamine` = Cc_shnk * mw[["hnk"]]
) |>
pivot_longer(c(`S-ketamine`, `S-hydroxynorketamine`), names_to = "analyte", values_to = "conc")
#> ℹ omega/sigma items treated as zero: 'etalvc', 'etalcl', 'etalq2', 'etalcl_snk', 'etalmtt_snk', 'etalvc_shnk', 'etalcl_shnk', 'etalq_shnk', 'etaiov_fdepot_1', 'etaiov_fdepot_2', 'etaiov_d1_1', 'etaiov_d1_2', 'etaiov_ka_1', 'etaiov_ka_2', 'etaiov_fdepot2_1', 'etaiov_fdepot2_2', 'etaiov_d2_1', 'etaiov_d2_2', 'etaiov_ka2_1', 'etaiov_ka2_2', 'etaiov_mtt_1', 'etaiov_mtt_2', 'etaiov_mtt_snk_1', 'etaiov_mtt_snk_2', 'etaiov_vp_shnk_1', 'etaiov_vp_shnk_2', 'etaiov_cl_shnk_1', 'etaiov_cl_shnk_2'
#> ℹ omega/sigma items treated as zero: 'etalvc', 'etalcl', 'etalq2', 'etalcl_snk', 'etalmtt_snk', 'etalvc_shnk', 'etalcl_shnk', 'etalq_shnk', 'etaiov_fdepot_1', 'etaiov_fdepot_2', 'etaiov_d1_1', 'etaiov_d1_2', 'etaiov_ka_1', 'etaiov_ka_2', 'etaiov_fdepot2_1', 'etaiov_fdepot2_2', 'etaiov_d2_1', 'etaiov_d2_2', 'etaiov_ka2_1', 'etaiov_ka2_2', 'etaiov_mtt_1', 'etaiov_mtt_2', 'etaiov_mtt_snk_1', 'etaiov_mtt_snk_2', 'etaiov_vp_shnk_1', 'etaiov_vp_shnk_2', 'etaiov_cl_shnk_1', 'etaiov_cl_shnk_2'
ggplot(fig5, aes(time, conc, colour = regimen, linetype = analyte)) +
geom_line() +
labs(
x = "Time (min)", y = "Plasma concentration (ng/mL)", colour = NULL, linetype = NULL,
caption = "Typical 70 kg subject. Replicates Figure 5 of Simons 2022."
) +
theme(legend.position = "bottom", legend.box = "vertical")
peaks <- fig5 |>
group_by(regimen, analyte) |>
summarise(cmax = max(conc), .groups = "drop") |>
pivot_wider(names_from = regimen, values_from = cmax)
knitr::kable(peaks, digits = 0, caption = "Typical-value peak concentrations (ng/mL).")| analyte | 0.5 mg/kg IV over 40 min | 100 mg oral thin film |
|---|---|---|
| S-hydroxynorketamine | 30 | 191 |
| S-ketamine | 289 | 172 |
# Discussion: greater S-ketamine but lower S-hydroxynorketamine after the
# infusion than after the 100 mg film.
iv_k <- peaks$`0.5 mg/kg IV over 40 min`[peaks$analyte == "S-ketamine"]
otf_k <- peaks$`100 mg oral thin film`[peaks$analyte == "S-ketamine"]
iv_h <- peaks$`0.5 mg/kg IV over 40 min`[peaks$analyte == "S-hydroxynorketamine"]
otf_h <- peaks$`100 mg oral thin film`[peaks$analyte == "S-hydroxynorketamine"]
stopifnot(iv_k > 1.2 * otf_k, iv_h < 0.8 * otf_h)Figure 4 of the paper is not replicated. It varies D1 together with F1, F2 and D2 according to rules that are described only in words (F1 “converges to 1 exponentially with D1”), with no rate constant printed.
Assumptions and deviations
- S-ketamine residual error. Table 4 prints the S-ketamine relative residual error as 0.012 with an SEE of 0.0004. Both numbers are identical to the row two lines above it (the subject-4 KA1 outlier, 0.012 and 0.0004), so the entry is probably a transcription error. The paper’s Figure 3 rules out 0.012 as a standard deviation. The S-ketamine individual weighted residuals (Figure 3B) lie mostly within +/-2. The scatter of measured against individual-predicted S-ketamine (Figure 3A) is as wide as that for S-norketamine, whose relative error is 0.102. Both observations require a residual SD near 0.1; with a true SD of 0.012 the weighted residuals would spread about ten times wider. Read as a variance, 0.012 gives SD = sqrt(0.012) = 0.1095, which is consistent with the figure, and this is the value used. All other residual terms are standard deviations. The maintainers checked this against the S-norketamine and S-hydroxynorketamine panels: the printed 0.102 and 0.079 relative SDs fit their scatter, and the squared readings (0.32 and 0.28) would not.
- Inter-occasion variability on MTT K->NK. The S-norketamine block of Table 4 prints an inter-occasion variance of 0.751 (SEE 0.349) on the additive residual-error row, where it has no meaning. The Methods state that inter-occasion variability was estimated “for the S-ketamine and S-norketamine absorption parameters”, and the only S-norketamine input-side parameter is the formation delay MTT K->NK. The value is therefore applied to MTT K->NK. The same block prints “11.6” in the inter-occasion column of the VN1 row, which repeats the VN1 estimate and is ignored.
- Inter-occasion variability on S-hydroxynorketamine. The Methods say it was estimated for all S-hydroxynorketamine parameters, but Table 4 prints values only for VH2 and CLH1. Only those two are encoded; nothing is invented for the others.
-
Allometric exponents. Table 4 gives every clearance
and volume “@ 70 kg” but prints no exponent. The standard values, 0.75
for clearances and 1 for volumes (fixed), are used. Over the 53-93 kg
study population the clearance multiplier (WT/70)^0.75 spans 0.81-1.24.
Use
WT = 70to reproduce the printed typical values exactly. - Absorption routing. Figure 2 sends the swallowed S-ketamine from the portal vein into hepatic S-ketamine metabolism with no arrow into the systemic S-ketamine compartment. The model follows the figure, so the swallowed route contributes only to the metabolites. Within the swallowed route, “two delay compartments defined by an absorption rate constant KA2 and a mean transit time (MTTG)” is encoded as a gut depot drained at KA2 followed by one compartment drained at 1 / MTTG. The two first-order steps act in series, so their order does not change the delivered profile.
- F2 above 100%. F2 = 116% of the film dose, together with F1, puts 1.42 times the dose into the system. F2 is not a physical bioavailability. It absorbs the scale of the metabolite volumes, which are identifiable only because the 80% and 70% conversion fractions are fixed. The model reproduces the paper’s metabolite exposures only with F2 applied to the full dose (see “Structural readings”).
- Individual outliers. Table 4 reports subject-specific estimates for subject 4 (KA1 0.012 1/min on occasion 2; MTT K->NK 8.72 min) and subject 9 (CLH2 0.36 L/min on occasion 2). These belong to individual subjects rather than the population and are not part of the packaged model.
- Table 2 unit rows. The nmol/mL rows of Table 2 for S-hydroxynorketamine imply a molecular weight of about 300 g/mol (101 ng/mL = 340 nM), not the 239.70 g/mol of hydroxynorketamine. The comparison above therefore uses the ng/mL rows, which are the units the assay reports.
- Virtual cohort. Body weight is drawn from a truncated normal distribution fitted to the Table 1 mean, SD and range. Sex, age and placement site are not covariates of the model.
- Errata. No correction notice for Simons 2022 was found in Europe PMC (no comment/correction link on the record, and no correction matching the title; checked 2026-10-03).