---
title: "Eflornithine (Jansson 2008)"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Eflornithine (Jansson 2008)}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.5)
library(nlmixr2lib)
library(PKNCA)
library(rxode2)
library(dplyr)
library(tidyr)
library(ggplot2)
```

# Model and source

* Citation: Jansson R, Malm M, Roth C, Ashton M. Enantioselective and nonlinear intestinal absorption of eflornithine in the rat. Antimicrob Agents Chemother. 2008;52(8):2842-2848.
* Article: <https://doi.org/10.1128/aac.00050-08>
* Description: stereoselective two-enantiomer popPK of racemic eflornithine in male Sprague-Dawley rats after single oral or IV doses. Each enantiomer (L = active, D) carries its own 2-compartment disposition; oral absorption is modeled with a shared Savic 2007 transit-compartment chain feeding per-enantiomer depots that drain to central via saturable Michaelis-Menten kinetics. Bioavailability differs between L and D and shifts upward at the highest oral dose (3000 mg/kg) via a categorical covariate.

# Population

Sixty-nine male Sprague-Dawley rats weighing 260-320 g (typical 290 g) were dosed once with racemic eflornithine hydrochloride. Forty-one rats received oral doses by gavage at 750, 1500, 2000, or 3000 mg/kg (10 mL/kg solution) for chiral assay and an additional 16 rats received the same dose levels for racemic assay. Twelve rats received IV doses at 375 or 1000 mg/kg via the jugular vein catheter (3-min infusion, modeled as a bolus). Source: Jansson 2008 Table 1.

The same metadata is available programmatically via `rxode2::rxode(readModelDb("Jansson_2008_eflornithine_rat"))$population`.

# Source trace

The per-parameter origin is recorded as an in-file comment next to each `ini()` entry in `inst/modeldb/specificDrugs/Jansson_2008_eflornithine_rat.R`. The table below collects them.

| Parameter | Value (mg / h / L for a 290 g rat) | Paper value | Source location |
|-----------|-----------------------------------|-------------|-----------------|
| `lcl_l`   | log(0.2523) L/h | CL_L = 14.5 mL/min/kg | Table 2 |
| `lcl_d`   | log(0.2192) L/h | CL_D = 12.6 mL/min/kg | Table 2 |
| `lvc_l`   | log(0.1172) L   | Vc_L = 0.404 L/kg | Table 2 |
| `lvc_d`   | log(0.1163) L   | Vc_D = 0.401 L/kg | Table 2 |
| `lq`      | log(0.02088) L/h | Q = 1.2 mL/min/kg (shared) | Table 2 |
| `lvp`     | log(0.1293) L   | Vp = 0.446 L/kg (shared) | Table 2 |
| `lmtt`    | log(1.467) h    | MTT = 88 min (shared) | Table 3 |
| `lnn`     | log(1.424)      | n = 1.424 transit compartments (shared) | Table 3 |
| `ltmax_abs_l` | log(35.18) mg/h | Tmax_L = 11.1 umol/min/kg | Table 3 |
| `ltmax_abs_d` | log(45.95) mg/h | Tmax_D = 14.5 umol/min/kg | Table 3 |
| `lkt_abs_l`   | log(82.41) mg | Kt_L = 1560 umol/kg | Table 3 |
| `lkt_abs_d`   | log(41.42) mg | Kt_D = 784 umol/kg | Table 3 |
| `lfdepot_l`   | log(0.41) | F_L (750-2000 mg/kg) = 41% | Table 3 |
| `lfdepot_d`   | log(0.623) | F_D (750-2000 mg/kg) = 62.3% | Table 3 |
| `e_dose_high_efl_fdepot_l` | 0.1463 | F_L (3000) = 47% (+14.6% vs reference) | Table 3 + Results |
| `e_dose_high_efl_fdepot_d` | 0.3275 | F_D (3000) = 82.7% (+32.8% vs reference) | Table 3 + Results |
| `etalcl_l`    | 0.02197 | IIV CL_L = 14.9% CV | Table 2 |
| `etalcl_d`    | 0.03263 | IIV CL_D = 18.2% CV | Table 2 |
| `etalmtt`     | 0.1349  | IIV MTT = 38% CV (shared) | Table 3 |
| `etalfdepot`  | 0.00995 | IIV F = 10% CV (shared) | Table 3 |
| `propSd_l/d/rac` | 0.277 | Oral residual sigma = 27.7% CV (proportional) | Table 3 + Methods |
| Transit chain (Savic 2007 analytical) | n/a | "transit model followed by a Michaelis-Menten function" | Fig 1 + Results paragraph |
| Saturable absorption rate | n/a | dA_central/dt receives Tmax*A_depot/(Kt+A_depot) | Fig 1 + Results paragraph |
| 2-compartment IV disposition | n/a | "biphasic" IV profile | Methods + Results + Fig 2 |

# Virtual cohort

Original observed data are not publicly available. Below we simulate the typical-value plasma profiles for each dose group reported in the paper. A virtual cohort with between-subject variability is constructed at the end of the vignette for an illustrative VPC.

The dose record convention for this model: each oral administration generates **two dose rows** (one to `depot_l` and one to `depot_d`, each with `amt` = 50% of the total racemic dose) so each enantiomer's transit chain reads its own `podo()` / `tad()`. IV doses follow the same 50/50 split into `central_l` and `central_d`. This is dictated by rxode2's requirement that the dose target compartment be the same compartment whose `d/dt()` carries the `transit()` call.

```{r cohort}
RAT_WT_KG <- 0.290  # typical Sprague-Dawley body weight in the Jansson 2008 cohort
EFL_MW    <- 182.17 # eflornithine molar mass (g/mol), used for uM <-> ug/mL conversions

# Dose levels (mg/kg racemic; Table 1) and matched high-dose indicators.
oral_doses <- tibble(
  dose_mgkg     = c(750, 1500, 2000, 3000),
  DOSE_HIGH_EFL = c(0,   0,    0,    1)
)

iv_doses <- tibble(
  dose_mgkg     = c(375, 1000),
  DOSE_HIGH_EFL = c(0,   0)
)

# Build a single-subject event table for an oral or IV regimen.
# id_offset shifts subject IDs so multiple cohorts can be bind_rows()-ed.
make_oral_events <- function(dose_mgkg, DOSE_HIGH_EFL, obs_times,
                             id = 1L, wt_kg = RAT_WT_KG) {
  amt_total <- dose_mgkg * wt_kg          # total racemic dose (mg)
  amt_half  <- amt_total * 0.5            # 50/50 enantiomer split
  bind_rows(
    tibble(id = id, time = 0, evid = 1L, cmt = "depot_l",  amt = amt_half),
    tibble(id = id, time = 0, evid = 1L, cmt = "depot_d",  amt = amt_half),
    tibble(id = id, time = obs_times, evid = 0L,
           cmt = "Cc_l", amt = NA_real_)
  ) |> mutate(DOSE_HIGH_EFL = DOSE_HIGH_EFL,
              dose_mgkg     = dose_mgkg,
              regimen       = sprintf("Oral %d mg/kg", dose_mgkg))
}

make_iv_events <- function(dose_mgkg, DOSE_HIGH_EFL, obs_times,
                           id = 1L, wt_kg = RAT_WT_KG) {
  amt_total <- dose_mgkg * wt_kg
  amt_half  <- amt_total * 0.5
  bind_rows(
    tibble(id = id, time = 0, evid = 1L, cmt = "central_l", amt = amt_half),
    tibble(id = id, time = 0, evid = 1L, cmt = "central_d", amt = amt_half),
    tibble(id = id, time = obs_times, evid = 0L,
           cmt = "Cc_l", amt = NA_real_)
  ) |> mutate(DOSE_HIGH_EFL = DOSE_HIGH_EFL,
              dose_mgkg     = dose_mgkg,
              regimen       = sprintf("IV %d mg/kg", dose_mgkg))
}

oral_obs <- c(0.05, seq(0.5, 30, by = 0.5))
iv_obs   <- c(0.083, 0.25, seq(0.5, 18, by = 0.5))
```

# Simulation

Typical-value profiles (random effects zeroed) for each oral and IV dose level reported in the paper:

```{r simulate-typical}
mod         <- readModelDb("Jansson_2008_eflornithine_rat")
mod_typical <- rxode2::zeroRe(mod)

sim_oral <- purrr::map2_dfr(oral_doses$dose_mgkg, oral_doses$DOSE_HIGH_EFL,
  function(d, hi) {
    ev <- make_oral_events(d, hi, oral_obs)
    rxode2::rxSolve(mod_typical, events = ev,
                    keep = c("dose_mgkg", "regimen", "DOSE_HIGH_EFL")) |>
      as_tibble() |>
      mutate(id = 1L)
  }
)

sim_iv <- purrr::map2_dfr(iv_doses$dose_mgkg, iv_doses$DOSE_HIGH_EFL,
  function(d, hi) {
    ev <- make_iv_events(d, hi, iv_obs)
    rxode2::rxSolve(mod_typical, events = ev,
                    keep = c("dose_mgkg", "regimen", "DOSE_HIGH_EFL")) |>
      as_tibble() |>
      mutate(id = 1L)
  }
)
```

# Replicate published figures

Concentrations on the plots are converted to micromolar (the paper's units) via `Cc [uM] = Cc [ug/mL] * 1000 / MW`.

```{r figure-2-iv}
# Replicates Figure 2 of Jansson 2008: IV plasma profile for L, D, and racemic
# eflornithine after 375 or 1000 mg/kg racemic dose. Concentrations in uM.
sim_iv |>
  pivot_longer(c(Cc_l, Cc_d, Cc_rac), names_to = "analyte", values_to = "Cc_ugmL") |>
  mutate(
    Cc_uM   = Cc_ugmL * 1000 / EFL_MW,
    analyte = factor(analyte,
                     levels = c("Cc_l", "Cc_d", "Cc_rac"),
                     labels = c("L-eflornithine", "D-eflornithine", "Racemic"))
  ) |>
  filter(time > 0, Cc_uM > 5) |>
  ggplot(aes(time, Cc_uM, color = factor(dose_mgkg), linetype = analyte)) +
  geom_line() +
  scale_y_log10() +
  facet_wrap(~ analyte) +
  labs(x = "Time (h)", y = "Plasma concentration (uM)",
       color = "IV dose (mg/kg)", linetype = "Analyte",
       title = "Figure 2 (typical-value) - IV eflornithine in rat",
       caption = "Replicates Figure 2 of Jansson 2008.")
```

```{r figure-4-oral}
# Replicates Figure 4 of Jansson 2008: oral plasma profile for L, D, and racemic
# eflornithine after 750, 1500, 2000, or 3000 mg/kg racemic doses. Tmax shifts
# from ~1.5 h at 750 mg/kg to ~6 h at 3000 mg/kg via the saturable absorption.
sim_oral |>
  pivot_longer(c(Cc_l, Cc_d, Cc_rac), names_to = "analyte", values_to = "Cc_ugmL") |>
  mutate(
    Cc_uM   = Cc_ugmL * 1000 / EFL_MW,
    analyte = factor(analyte,
                     levels = c("Cc_l", "Cc_d", "Cc_rac"),
                     labels = c("L-eflornithine", "D-eflornithine", "Racemic"))
  ) |>
  filter(Cc_uM > 5) |>
  ggplot(aes(time, Cc_uM, color = factor(dose_mgkg))) +
  geom_line() +
  scale_y_log10() +
  facet_wrap(~ analyte) +
  labs(x = "Time (h)", y = "Plasma concentration (uM)",
       color = "Oral dose (mg/kg)",
       title = "Figure 4 (typical-value) - Oral eflornithine in rat",
       caption = "Replicates Figure 4 of Jansson 2008. Tmax shifts upward with dose; F_L stays at 41-47% while F_D jumps to 83% at the highest dose.")
```

# PKNCA validation

We compute Cmax, Tmax, AUC0-inf, and apparent half-life for each enantiomer and the racemic sum after each oral dose. The paper does not tabulate NCA values directly, but we use them to verify three model features mentioned in the Results: (i) Tmax shifts upward with dose, (ii) racemic-eflornithine bioavailability rises with dose (F_rac = (F_L + F_D)/2 = 51.6% at the lower doses and 64.9% at 3000 mg/kg), and (iii) the model's saturable absorption produces non-proportional dose-AUC scaling.

```{r pknca-oral}
# Reshape oral typical-value simulation into a long PKNCA-friendly form.
sim_oral_long <- sim_oral |>
  pivot_longer(c(Cc_l, Cc_d, Cc_rac),
               names_to = "analyte", values_to = "Cc_ugmL") |>
  filter(!is.na(Cc_ugmL), time > 0) |>
  mutate(amt_dose_l   = dose_mgkg * RAT_WT_KG * 0.5,
         amt_dose_rac = dose_mgkg * RAT_WT_KG) |>
  select(id, regimen, dose_mgkg, analyte, time, Cc_ugmL,
         amt_dose_l, amt_dose_rac)

run_pknca_one <- function(df, dose_amt_col) {
  conc_df <- df |>
    mutate(uid = paste(regimen, id)) |>
    select(uid, time, Cc = Cc_ugmL, regimen)
  dose_df <- df |>
    distinct(uid = paste(regimen, id), regimen,
             amt = .data[[dose_amt_col]]) |>
    mutate(time = 0)
  cobj <- PKNCA::PKNCAconc(conc_df, Cc ~ time | regimen + uid)
  dobj <- PKNCA::PKNCAdose(dose_df, amt ~ time | regimen + uid)
  intervals <- data.frame(start = 0, end = Inf,
                          cmax = TRUE, tmax = TRUE,
                          aucinf.obs = TRUE, half.life = TRUE)
  PKNCA::pk.nca(PKNCA::PKNCAdata(cobj, dobj, intervals = intervals))
}

nca_l <- sim_oral_long |>
  filter(analyte == "Cc_l") |>
  run_pknca_one("amt_dose_l")

nca_d <- sim_oral_long |>
  filter(analyte == "Cc_d") |>
  run_pknca_one("amt_dose_l")    # D dose = same 50% split

nca_rac <- sim_oral_long |>
  filter(analyte == "Cc_rac") |>
  run_pknca_one("amt_dose_rac")  # racemic dose denominator

summarise_nca <- function(nca_obj, label) {
  as.data.frame(nca_obj$result) |>
    filter(PPTESTCD %in% c("cmax", "tmax", "aucinf.obs", "half.life")) |>
    mutate(analyte = label)
}

nca_table <- bind_rows(
  summarise_nca(nca_l,   "L-eflornithine"),
  summarise_nca(nca_d,   "D-eflornithine"),
  summarise_nca(nca_rac, "Racemic")
) |>
  select(analyte, regimen, PPTESTCD, PPORRES) |>
  pivot_wider(names_from = PPTESTCD, values_from = PPORRES)

knitr::kable(nca_table,
             caption = "Simulated NCA parameters by oral dose group (typical value). Cmax in ug/mL; AUC in ug*h/mL; Tmax and half-life in h.",
             digits = 3)
```

The Tmax column should rise monotonically with dose (a structural prediction of the saturable absorption from the depot - higher amounts saturate the rate and prolong the time-to-peak). The AUC column should rise less than proportionally with dose at the lower dose levels (saturation lowers F_eff) and then jump at 3000 mg/kg via the categorical F adjustment.

# Optional VPC with between-subject variability

```{r vpc, eval = TRUE}
N_PER_GROUP <- 12L
oral_obs_vpc <- seq(0, 24, by = 1)

vpc_events <- purrr::pmap_dfr(oral_doses,
  function(dose_mgkg, DOSE_HIGH_EFL) {
    purrr::map_dfr(seq_len(N_PER_GROUP),
      function(k) {
        id <- as.integer(dose_mgkg * 100L + k)  # disjoint IDs across groups
        make_oral_events(dose_mgkg, DOSE_HIGH_EFL, oral_obs_vpc, id = id)
      })
  })

set.seed(459L)
vpc_sim <- rxode2::rxSolve(mod, events = vpc_events,
                           keep = c("dose_mgkg", "regimen", "DOSE_HIGH_EFL")) |>
  as_tibble()

vpc_sim |>
  filter(time > 0, Cc_rac > 0) |>
  mutate(Cc_uM = Cc_rac * 1000 / EFL_MW) |>
  group_by(regimen, time) |>
  summarise(Q05 = quantile(Cc_uM, 0.05),
            Q50 = quantile(Cc_uM, 0.50),
            Q95 = quantile(Cc_uM, 0.95),
            .groups = "drop") |>
  ggplot(aes(time, Q50, color = regimen, fill = regimen)) +
  geom_ribbon(aes(ymin = Q05, ymax = Q95), alpha = 0.18, color = NA) +
  geom_line() +
  scale_y_log10() +
  labs(x = "Time (h)", y = "Racemic plasma concentration (uM)",
       title = sprintf("VPC of racemic eflornithine after oral dosing (n=%d per group)", N_PER_GROUP),
       caption = "Median and 5th-95th percentile envelope from rxSolve with between-subject variability.")
```

# Assumptions and deviations

- **Residual error documentation conflict in the source.** Jansson 2008 Methods text says "Random residual variability was modeled as proportional to the observed concentrations" and Table 3's sigma column carries the header "(% coefficient of variation)" - both consistent with a proportional residual on the linear scale. Table 3 footnote 'a' mislabels sigma as "additive residual error", which is internally inconsistent. The model encodes proportional residual error (`prop(propSd_l/d/rac)`).
- **IV vs oral residual.** The paper estimated separate proportional residuals for the IV fit (sigma = 19.9% CV, Table 2) and the oral fit (sigma = 27.7% CV, Table 3). For multi-output simulation simplicity, this model uses 27.7% CV for all three plasma outputs (L, D, racemic). The IV residual is slightly overestimated as a consequence. Downstream users running IV-only simulations may override `propSd_l/d/rac` to 0.199 to recover the paper's IV residual exactly.
- **Body weight reference (290 g).** Per-kg parameter values from Tables 2 and 3 are converted to absolute units (L, L/h, mg, mg/h) using the midpoint study weight 0.290 kg. The cohort range was 260-320 g; users simulating individual rats outside that midpoint may need to scale linearly (volumes) or via allometric exponent 0.75 (clearances) - the package's standard allometric scaffolding is not built in here because the within-cohort weight range is narrow.
- **Dose record convention.** Each racemic administration creates two dose rows (50% to depot_l / central_l for L, 50% to depot_d / central_d for D), reflecting the 50:50 molecular composition of racemic eflornithine. rxode2's `transit()` function requires the dose target compartment to coincide with the d/dt() call site, so a single shared "depot" compartment is not used; instead each enantiomer carries its own depot and central. Users feeding the model their own data must therefore split each racemic dose into the two halves before passing it to `rxSolve()`.
- **Shared transit-chain IIV (etalmtt) acts on both enantiomers via the single shared `mtt` parameter.** Shared bioavailability IIV (etalfdepot) is applied as one random effect that scales both `fdepot_l` and `fdepot_d` simultaneously (perfectly correlated between enantiomers). This matches the paper's statement that "Interindividual variability values for mean transit time and bioavailability could not be estimated separately for D- and L-eflornithine and were therefore assumed to be identical."
- **High-dose categorical effect on F.** Bioavailability rises at the 3000 mg/kg oral dose level via a binary indicator (`DOSE_HIGH_EFL = 1`). The paper explicitly states that linear or power dose-F relationships did not improve the fit, so the categorical encoding is the published model form. Users simulating intermediate dose levels (>2000 and <3000 mg/kg) should set the indicator according to the paper's threshold; the model does not interpolate.
- **Original NONMEM control stream not on disk.** The Savic 2007 transit-chain Stirling approximation cited by the paper is the analytical input form built into rxode2's `transit()` function, so no Stirling code is required in the model file.
