Mycophenolic acid (deWinter 2009)
Source:vignettes/articles/deWinter_2009_mycophenolic_acid.Rmd
deWinter_2009_mycophenolic_acid.Rmd
library(nlmixr2lib)
library(rxode2)
#> rxode2 5.1.2 using 2 threads (see ?getRxThreads)
#> no cache: create with `rxCreateCache()`
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(tidyr)
library(ggplot2)
library(PKNCA)
#>
#> Attaching package: 'PKNCA'
#> The following object is masked from 'package:stats':
#>
#> filterMycophenolic acid (MPA) and metabolite MPAG with competitive protein binding and EHC
Mycophenolic acid (MPA) is the active immunosuppressant moiety of mycophenolate mofetil (MMF), administered orally to prevent graft rejection in renal transplant recipients. MPA is glucuronidated by UGT1A9 / UGT2B7 to the major plasma metabolite MPAG. Both species bind highly to plasma albumin (MPA ~97% bound, MPAG ~82% bound under normal conditions) and MPAG undergoes enterohepatic recirculation: it is excreted into the gut lumen via MRP2-mediated biliary transport, then de-glucuronidated back to MPA by intestinal beta-glucuronidases and reabsorbed.
de Winter et al. (2009) developed a semi-mechanistic population PK model that simultaneously describes total MPA (tMPA), free MPA (fMPA), total MPAG (tMPAG), and free MPAG (fMPAG) plasma concentration-time profiles after oral MMF in 75 renal transplant recipients (93 PK profiles). The structural model features:
- A two-compartment disposition for fMPA with first-order oral absorption (lag time TLAG; fixed ka = 4.00 1/h);
- A one-compartment disposition for fMPAG;
- A competitive protein-binding pool with saturation capacity BMAX to which both fMPA and fMPAG bind kinetically (rate constants k24 / k42 for MPA, k56 / k65 for MPAG);
- A gallbladder compartment that accumulates fMPAG via rate constant k57 and empties into the fMPA central compartment during a fixed post-dose window (TGB to TGB + DGB, with rate constant k72 = 10/h) to encode enterohepatic recirculation.
Three covariates entered the final model: a power effect of creatinine clearance (CRCL) on CL fMPAG, a power effect of plasma albumin (ALB) on BMAX, and a multiplicative cyclosporine (CONMED_CSA) effect on the biliary transport rate constant k57 that captures the MRP2 inhibition by cyclosporine and the resulting suppression of enterohepatic recirculation.
- Citation: de Winter BCM, van Gelder T, Sombogaard F, Shaw LM, van Hest RM, Mathot RAA. Pharmacokinetic role of protein binding of mycophenolic acid and its glucuronide metabolite in renal transplant recipients. J Pharmacokinet Pharmacodyn. 2009;36(6):541-564. doi:10.1007/s10928-009-9136-6.
- Article: https://doi.org/10.1007/s10928-009-9136-6
- Open Access; published as Springer Open Choice in 2009.
Population
The model-building dataset pooled two prior trials of de novo adult renal transplant recipients (de Winter 2009, Methods Patients / Table 1):
| Cohort | n profiles | MMF dose median (range) | CNI cotreatment | CRCL median (range) | ALB median (range) |
|---|---|---|---|---|---|
| Cyclosporine | 48 | 1350 mg BID (400-2200) | Cyclosporine | 44 mL/min (8-107) | 0.51 mmol/L (0.38-0.61) |
| Tacrolimus | 45 | 1000 mg BID (500-1500) | Tacrolimus | 45 mL/min (8-154) | 0.51 mmol/L (0.35-0.68) |
A total of 489 tMPA + 489 fMPA + 488 tMPAG + 210 fMPAG plasma
concentrations were modeled. Patient ages ranged 19-76 years and weights
42-113 kg across the pooled cohort. CRCL was computed by the
Cockcroft-Gault formula (raw mL/min, NOT BSA-normalised). The same
metadata is available programmatically through
readModelDb("deWinter_2009_mycophenolic_acid")$population.
Source trace
Per-parameter origin is recorded as in-file comments next to each
ini() entry in
inst/modeldb/specificDrugs/deWinter_2009_mycophenolic_acid.R.
The table collects them for review.
| Equation / parameter | Value (paper) | Value (file) | Source |
|---|---|---|---|
ltlag (TLAG) |
0.231 h | log(0.231) | Table 2 |
lka (ka, fixed) |
4.00 1/h | fixed(log(4.00)) | Table 2 (fixed) |
lvc (Vc fMPA / F) |
189 L | log(189) | Table 2 |
lcl (CL fMPA / F) |
747 L/h | log(747) | Table 2 |
lvp (Vp fMPA / F) |
34300 L | log(34300) | Table 2 |
lq (Q fMPA / F) |
2010 L/h | log(2010) | Table 2 |
lk24 (k_on MPA) |
0.153 L/(h*umol) | log(0.153) | Table 2 |
lbmax (BMAX) |
35100 umol | log(35100) | Table 2 |
lk42 (k_off MPA) |
169 1/h | log(169) | Table 2 |
lvc_mpag (Vc fMPAG / F) |
8.56 L | log(8.56) | Table 2 |
lk56 (k_on MPAG) |
0.0133 L/(h*umol) | log(0.0133) | Table 2 |
lk65 (k_off MPAG) |
93.1 1/h | log(93.1) | Table 2 |
lcl_mpag (CL fMPAG / F) |
4.75 L/h | log(4.75) | Table 2 |
ltgb (TGB) |
7.90 h | log(7.90) | Table 2 |
ldgb (DGB, fixed) |
1.00 h | fixed(log(1.00)) | Table 2 (fixed) |
lk72 (k72, fixed) |
10.0 1/h | fixed(log(10.0)) | Table 2 (fixed) |
lk57 (k57) |
0.0796 1/h | log(0.0796) | Table 2 |
e_crcl_cl_mpag (CRCL exponent on CL fMPAG) |
1.36 | 1.36 | Table 2 covariate effects |
e_alb_bmax (ALB exponent on BMAX) |
1.39 | 1.39 | Table 2 covariate effects |
e_csa_k57 (CsA multiplier on k57) |
0.002 | 0.002 | Table 2 covariate effects |
etaltlag IPV TLAG (eta variance) |
161% CV | log(1+1.61^2)=1.279 | Table 2 |
etalvc IPV Vc fMPA |
97% CV | log(1+1.16^2)=0.853 | Table 2 (IPV Vc=116%) |
etalcl IPV CL fMPA |
97% CV | log(1+0.97^2)=0.663 | Table 2 |
etalbmax IPV BMAX |
48% CV | log(1+0.48^2)=0.207 | Table 2 |
etalcl_mpag IPV CL fMPAG |
106% CV | log(1+1.06^2)=0.753 | Table 2 |
etaltgb IPV TGB |
141% CV (additive in source; packaged as log-normal) | log(1+1.41^2)=1.095 | Table 2 |
etalk57 IPV k57 (FIXED) |
71% CV (fixed) | fixed(log(1+0.71^2))=fixed(0.408) | Table 2 |
propSd (residual error on tMPA, log-additive) |
0.52 | 0.52 | Table 2 additive error tMPA |
propSd_fMPA (residual error on fMPA) |
0.993 | 0.993 | Table 2 additive error fMPA |
propSd_mpag (residual error on tMPAG) |
0.186 | 0.186 | Table 2 additive error tMPAG |
propSd_fMPAG (residual error on fMPAG) |
0.551 | 0.551 | Table 2 additive error fMPAG |
| Competitive binding ODE: d(bMPA)/dt = k24 * fMPA * (BMAX - bMPA - bMPAG) - k42 * bMPA | n/a | encoded | Eq. 1-7, page 545 (paper) |
| Mass-balance MPA -> MPAG conversion (1:1 molar) | n/a | kel * central -> d(central_mpag)/dt | Methods, page 545 |
| Gallbladder accumulation and emptying | n/a | k57 * central_mpag - k72 * gallbladder * indicator(TGB <= tpost <= TGB + DGB) | Methods, page 545 |
| Total observed = free + bound concentrations | n/a | Cc = fMPA_conc + complex; Cc_mpag = fMPAG_conc + complex_mpag | Methods page 545 (text) |
Virtual cohort
Original observed data are not publicly available. The cohort below simulates 50 typical-value subjects at the paper’s reference covariate values (CRCL = 50 mL/min, ALB = 0.5 mmol/L), split into a tacrolimus arm and a cyclosporine arm (the paper’s simulation baseline; Results Simulations section). All subjects receive 1000 mg MMF BID for 12 days to reach steady state.
set.seed(20091101L) # paper Springer online publication date
n_per_arm <- 12L # downsampled from 25 for vignette build budget; VPC band shape preserved
n_doses <- 24L # 12 days BID
mmf_mg <- 1000 # 1 g MMF per dose
mmf_mw <- 433.5 # g/mol -> molar dose conversion
dose_umol <- mmf_mg / mmf_mw * 1000
make_cohort <- function(n, csa, id_offset = 0L) {
data.frame(
id = id_offset + seq_len(n),
arm = if (csa == 1) "Cyclosporine" else "Tacrolimus",
CRCL = 50,
ALB = 0.5,
CONMED_CSA = csa
)
}
cohorts <- bind_rows(
make_cohort(n_per_arm, csa = 0, id_offset = 0L),
make_cohort(n_per_arm, csa = 1, id_offset = n_per_arm)
)
## downsampled grid for vignette build budget; figures only use the last
## interval [240, 252] h so we keep dense sampling there and coarse elsewhere
obs_times <- sort(unique(c(
seq(0, 240, by = 2),
seq(240, 252, by = 0.25),
seq(252, n_doses * 12 + 12, by = 2)
)))
events <- cohorts |>
rowwise() |>
do({
row <- .
et_obj <- rxode2::et() |>
rxode2::et(amt = dose_umol, time = 0, ii = 12, addl = n_doses - 1L, cmt = "depot") |>
rxode2::et(obs_times, cmt = "Cc")
et_obj$id <- row$id
et_obj$arm <- row$arm
et_obj$CRCL <- row$CRCL
et_obj$ALB <- row$ALB
et_obj$CONMED_CSA <- row$CONMED_CSA
et_obj
}) |>
ungroup() |>
as.data.frame()
stopifnot(!anyDuplicated(unique(events[, c("id", "time", "evid")])))Simulation
The packaged model produces four concentration outputs in umol/L:
Cc (tMPA), fMPA, Cc_mpag (tMPAG),
and fMPAG. Below we simulate both a deterministic
typical-value series (no between-subject variability, used for figure
replication) and a stochastic series (IIV from the published omega^2
estimates, used for variability bands).
mod <- readModelDb("deWinter_2009_mycophenolic_acid")
mod_typical <- rxode2::zeroRe(mod)
#> ℹ parameter labels from comments will be replaced by 'label()'
sim_typ <- rxode2::rxSolve(mod_typical, events = events,
keep = c("arm", "CRCL", "ALB", "CONMED_CSA"),
returnType = "data.frame", addDosing = FALSE,
atol = 1e-8, rtol = 1e-6)
#> ℹ omega/sigma items treated as zero: 'etaltlag', 'etalvc', 'etalcl', 'etalbmax', 'etalcl_mpag', 'etaltgb', 'etalk57'
#> Warning: multi-subject simulation without without 'omega'Last-interval steady-state profiles
The plots below restrict simulated profiles to the final dosing interval (240-252 h, i.e., the 21st BID dose). The four output concentrations are shown side-by-side for the typical-value simulation in each CNI arm. Vertical dashed lines mark the gallbladder-emptying window (TGB = 7.9 h to TGB + DGB = 8.9 h post-dose); this is when the characteristic enterohepatic-recirculation second peak appears on the tMPA profile in the tacrolimus arm (cyclosporine cotreatment suppresses the second peak by ~99.8% by inhibiting MRP2).
last_typ <- sim_typ |>
dplyr::filter(time >= 240 & time <= 252) |>
dplyr::mutate(tpost = time - 240) |>
tidyr::pivot_longer(c(Cc, fMPA, Cc_mpag, fMPAG),
names_to = "analyte", values_to = "conc_umol") |>
dplyr::mutate(analyte = factor(analyte,
levels = c("Cc", "fMPA", "Cc_mpag", "fMPAG"),
labels = c("tMPA (umol/L)", "fMPA (umol/L)", "tMPAG (umol/L)", "fMPAG (umol/L)")))
ggplot(last_typ, aes(tpost, conc_umol, colour = arm, group = arm)) +
geom_line(linewidth = 1) +
geom_vline(xintercept = c(7.9, 8.9), lty = 2, colour = "grey40") +
facet_wrap(~analyte, scales = "free_y") +
scale_y_continuous(trans = "log10") +
labs(x = "Time post-dose (h)", y = NULL,
colour = "CNI cotreatment",
title = "Steady-state BID profile of tMPA, fMPA, tMPAG, fMPAG (typical values)",
caption = "Last 12-h dosing interval. Dashed lines: gallbladder-emptying window.")![Typical-value steady-state profiles for the final BID dosing interval (t in [240, 252] h post-first-dose). Vertical dashed lines: gallbladder-emptying window (TGB = 7.9 h post-dose, DGB = 1.0 h). Note the EHC second peak on tMPA in the tacrolimus arm at 8 h post-dose; cyclosporine suppresses this by ~99.8% via MRP2 inhibition.](deWinter_2009_mycophenolic_acid_files/figure-html/last-interval-typical-1.png)
Typical-value steady-state profiles for the final BID dosing interval (t in [240, 252] h post-first-dose). Vertical dashed lines: gallbladder-emptying window (TGB = 7.9 h post-dose, DGB = 1.0 h). Note the EHC second peak on tMPA in the tacrolimus arm at 8 h post-dose; cyclosporine suppresses this by ~99.8% via MRP2 inhibition.
Replicate published Figure 4 - visual predictive check pattern
The published Figure 4 shows VPCs of tMPA, fMPA, tMPAG (cyclosporine arm) and tMPA, fMPA, tMPAG, fMPAG (tacrolimus arm) at steady state in the original 93 observed PK profiles. Below we render the simulated median and 80% prediction interval (10th-90th percentiles) from the n=50 virtual cohort with IIV active, restricted to the final BID dosing interval, for visual comparison with the layout of the published figure.
sim_iiv |>
dplyr::filter(time >= 240 & time <= 252) |>
dplyr::mutate(tpost = time - 240) |>
tidyr::pivot_longer(c(Cc, fMPA, Cc_mpag, fMPAG),
names_to = "analyte", values_to = "conc") |>
dplyr::mutate(analyte = factor(analyte,
levels = c("Cc", "fMPA", "Cc_mpag", "fMPAG"),
labels = c("tMPA", "fMPA", "tMPAG", "fMPAG"))) |>
dplyr::group_by(arm, analyte, tpost) |>
dplyr::summarise(
Q10 = quantile(conc, 0.10, na.rm = TRUE),
Q50 = quantile(conc, 0.50, na.rm = TRUE),
Q90 = quantile(conc, 0.90, na.rm = TRUE),
.groups = "drop"
) |>
ggplot(aes(tpost, Q50, colour = arm, fill = arm)) +
geom_ribbon(aes(ymin = Q10, ymax = Q90), alpha = 0.2, colour = NA) +
geom_line(linewidth = 0.9) +
facet_grid(analyte ~ arm, scales = "free_y") +
labs(x = "Time post-dose (h, last steady-state interval)",
y = "Concentration (umol/L)",
colour = "CNI cotreatment", fill = "CNI cotreatment",
title = "VPC bands at steady state (last BID interval) - de Winter 2009 Fig 4 layout",
caption = "Median (line) and 10th-90th percentile (ribbon) from 25 simulated subjects per CNI arm.")
Visual predictive bands (10th-90th percentiles) for the last steady-state BID dosing interval, by analyte and CNI arm. Compare layout against de Winter 2009 Figure 4.
Replicate published Figure 5 - albumin sensitivity
de Winter 2009 Figure 5 demonstrates the effect of plasma albumin concentration on simulated steady-state concentration-time profiles. ALB = 0.4 -> 0.5 -> 0.6 mmol/L (corresponding to ~26 -> 33 -> 40 g/L in mass units). Free MPA (fMPA) is essentially insensitive to ALB because MPA is a low-extraction-ratio drug; total MPA (tMPA) is strongly affected because more binding sites means more bound MPA and therefore higher total concentrations at constant unbound clearance.
alb_levels <- c(0.4, 0.5, 0.6)
# downsampled from 25 to 12 per-ALB for vignette build budget; typical-value lines unaffected
make_alb_cohort <- function(alb, id_offset) {
data.frame(id = id_offset + seq_len(12L),
arm = "Tacrolimus", CRCL = 50, ALB = alb, CONMED_CSA = 0)
}
alb_cohorts <- bind_rows(lapply(seq_along(alb_levels),
function(i) make_alb_cohort(alb_levels[i], (i - 1L) * 12L)))
alb_events <- alb_cohorts |>
rowwise() |>
do({
row <- .
et_obj <- rxode2::et() |>
rxode2::et(amt = dose_umol, time = 0, ii = 12, addl = n_doses - 1L, cmt = "depot") |>
rxode2::et(obs_times, cmt = "Cc")
et_obj$id <- row$id; et_obj$arm <- row$arm
et_obj$CRCL <- row$CRCL; et_obj$ALB <- row$ALB
et_obj$CONMED_CSA <- row$CONMED_CSA
et_obj
}) |>
ungroup() |>
as.data.frame()
stopifnot(!anyDuplicated(unique(alb_events[, c("id", "time", "evid")])))
sim_alb_typ <- rxode2::rxSolve(mod_typical, events = alb_events,
keep = c("arm", "CRCL", "ALB", "CONMED_CSA"),
returnType = "data.frame", addDosing = FALSE,
atol = 1e-8, rtol = 1e-6)
#> ℹ omega/sigma items treated as zero: 'etaltlag', 'etalvc', 'etalcl', 'etalbmax', 'etalcl_mpag', 'etaltgb', 'etalk57'
#> Warning: multi-subject simulation without without 'omega'
sim_alb_typ |>
dplyr::filter(time >= 240 & time <= 252) |>
dplyr::mutate(tpost = time - 240) |>
tidyr::pivot_longer(c(Cc, fMPA, Cc_mpag, fMPAG),
names_to = "analyte", values_to = "conc") |>
dplyr::mutate(analyte = factor(analyte,
levels = c("Cc", "fMPA", "Cc_mpag", "fMPAG"),
labels = c("tMPA", "fMPA", "tMPAG", "fMPAG")),
ALB_label = sprintf("ALB = %.1f mmol/L", ALB)) |>
ggplot(aes(tpost, conc, colour = ALB_label, group = ALB_label)) +
geom_line(linewidth = 0.9) +
facet_wrap(~analyte, scales = "free_y") +
scale_y_continuous(trans = "log10") +
labs(x = "Time post-dose (h, last steady-state interval)",
y = "Concentration (umol/L)",
colour = "Albumin",
title = "Figure 5 replicate (tacrolimus): ALB sensitivity at CRCL = 50 mL/min",
caption = "Replicates Figure 5 of de Winter 2009; typical-value steady-state.")
Replicates Figure 5 of de Winter 2009 (tacrolimus arm): tMPA, fMPA, tMPAG, fMPAG steady-state profiles at three plasma albumin concentrations. Note the strong ALB dependence on tMPA and the near-zero dependence on fMPA.
Replicate published Figure 8 - CRCL sensitivity
Figure 8 demonstrates the effect of creatinine clearance on steady-state concentration-time profiles. The CRCL effect operates through CL fMPAG (power exponent 1.36): lower CRCL means slower MPAG renal elimination and progressively higher tMPAG and fMPAG. The downstream effect on tMPA / fMPA differs by CNI: tacrolimus subjects see a net increase in tMPA (the accumulated MPAG returns as MPA through EHC); cyclosporine subjects see a net decrease in tMPA (the EHC is blocked by MRP2 inhibition, so accumulated MPAG instead displaces MPA from protein binding and accelerates fMPA elimination).
crcl_levels <- c(10, 30, 50)
arms <- c("Tacrolimus", "Cyclosporine")
# downsampled from 25 to 12 per-CRCL/arm for vignette build budget; typical-value lines unaffected
make_crcl_cohort <- function(crcl, arm, id_offset) {
data.frame(id = id_offset + seq_len(12L),
arm = arm, CRCL = crcl, ALB = 0.5,
CONMED_CSA = ifelse(arm == "Cyclosporine", 1L, 0L))
}
crcl_cohorts <- bind_rows(lapply(seq_along(crcl_levels), function(i) {
bind_rows(
make_crcl_cohort(crcl_levels[i], "Tacrolimus", (i - 1L) * 24L),
make_crcl_cohort(crcl_levels[i], "Cyclosporine", (i - 1L) * 24L + 12L)
)
}))
crcl_events <- crcl_cohorts |>
rowwise() |>
do({
row <- .
et_obj <- rxode2::et() |>
rxode2::et(amt = dose_umol, time = 0, ii = 12, addl = n_doses - 1L, cmt = "depot") |>
rxode2::et(obs_times, cmt = "Cc")
et_obj$id <- row$id; et_obj$arm <- row$arm
et_obj$CRCL <- row$CRCL; et_obj$ALB <- row$ALB
et_obj$CONMED_CSA <- row$CONMED_CSA
et_obj
}) |>
ungroup() |>
as.data.frame()
stopifnot(!anyDuplicated(unique(crcl_events[, c("id", "time", "evid")])))
sim_crcl_typ <- rxode2::rxSolve(mod_typical, events = crcl_events,
keep = c("arm", "CRCL", "ALB", "CONMED_CSA"),
returnType = "data.frame", addDosing = FALSE,
atol = 1e-8, rtol = 1e-6)
#> ℹ omega/sigma items treated as zero: 'etaltlag', 'etalvc', 'etalcl', 'etalbmax', 'etalcl_mpag', 'etaltgb', 'etalk57'
#> Warning: multi-subject simulation without without 'omega'
sim_crcl_typ |>
dplyr::filter(time >= 240 & time <= 252) |>
dplyr::mutate(tpost = time - 240) |>
tidyr::pivot_longer(c(Cc, fMPA, Cc_mpag, fMPAG),
names_to = "analyte", values_to = "conc") |>
dplyr::mutate(analyte = factor(analyte,
levels = c("Cc", "fMPA", "Cc_mpag", "fMPAG"),
labels = c("tMPA", "fMPA", "tMPAG", "fMPAG")),
CRCL_label = factor(paste0("CRCL = ", CRCL, " mL/min"),
levels = paste0("CRCL = ", crcl_levels, " mL/min"))) |>
ggplot(aes(tpost, conc, colour = CRCL_label, group = CRCL_label)) +
geom_line(linewidth = 0.9) +
facet_grid(analyte ~ arm, scales = "free_y") +
scale_y_continuous(trans = "log10") +
labs(x = "Time post-dose (h, last steady-state interval)",
y = "Concentration (umol/L)",
colour = "Creatinine clearance",
title = "Figure 8 replicate: CRCL sensitivity at ALB = 0.5 mmol/L",
caption = "Replicates Figure 8 of de Winter 2009; typical-value steady-state.")
Replicates Figure 8 of de Winter 2009: tMPA / fMPA / tMPAG / fMPAG steady-state profiles at three CRCL values (10, 30, 50 mL/min), separately for the cyclosporine and tacrolimus arms.
PKNCA validation against published AUC values
The source paper reports steady-state AUC0-12 values for the four analytes under several covariate scenarios (Discussion / Simulations sections). Below we run PKNCA on the typical-value simulated profiles (last steady-state interval) and compare against the paper’s reported mean AUC values across the four CRCL x CNI combinations the paper discusses.
# Build a multi-analyte long table for PKNCA. Cmax / AUC are computed
# per analyte and per (CRCL, CNI) cohort using the last steady-state
# interval. The four analytes use a single PKNCAdose object with the
# same dose record per subject (1 g MMF = 2306.8 umol per dose).
last_interval_typ <- sim_crcl_typ |>
dplyr::filter(time >= 240 & time <= 252) |>
dplyr::mutate(tpost = time - 240,
cohort = paste0(arm, " | CRCL ", CRCL))
# Per-analyte concentration data for PKNCA (one analyte per call).
nca_one <- function(analyte_col, conc_label) {
conc_df <- last_interval_typ |>
dplyr::select(id, tpost, arm, CRCL, cohort,
conc = !!rlang::sym(analyte_col)) |>
dplyr::filter(!is.na(conc))
conc_obj <- PKNCA::PKNCAconc(conc_df, conc ~ tpost | cohort + id)
dose_df <- last_interval_typ |>
dplyr::distinct(id, cohort) |>
dplyr::mutate(tpost = 0, amt = dose_umol)
dose_obj <- PKNCA::PKNCAdose(dose_df, amt ~ tpost | cohort + id)
intervals <- data.frame(
start = 0, end = 12,
cmax = TRUE, tmax = TRUE,
auclast = TRUE)
data <- PKNCA::PKNCAdata(conc_obj, dose_obj, intervals = intervals)
res <- PKNCA::pk.nca(data)
out <- as.data.frame(res$result)
out$analyte <- conc_label
out
}
nca_all <- bind_rows(
nca_one("Cc", "tMPA"),
nca_one("fMPA", "fMPA"),
nca_one("Cc_mpag", "tMPAG"),
nca_one("fMPAG", "fMPAG"))
nca_summary <- nca_all |>
dplyr::group_by(cohort, analyte, PPTESTCD) |>
dplyr::summarise(mean = mean(PPORRES, na.rm = TRUE), .groups = "drop") |>
tidyr::pivot_wider(names_from = PPTESTCD, values_from = mean) |>
dplyr::arrange(analyte, cohort) |>
dplyr::mutate(
arm = dplyr::case_when(grepl("Cyclosporine", cohort) ~ "Cyclosporine",
TRUE ~ "Tacrolimus"),
CRCL = as.integer(sub(".*CRCL ", "", cohort))) |>
dplyr::select(analyte, arm, CRCL, cmax, tmax, auclast)
# Convert AUC and Cmax to source-paper units (mg*h/L and ug/mL) for
# direct comparison. MPA MW = 320.3; MPAG MW = 496.5; concentration in
# umol/L * MW / 1000 -> mg/L; AUC in umol*h/L * MW / 1000 -> mg*h/L.
mw <- c(tMPA = 320.3, fMPA = 320.3, tMPAG = 496.5, fMPAG = 496.5)
nca_paper_units <- nca_summary |>
dplyr::mutate(
cmax_mg_per_L = cmax * mw[analyte] / 1000,
auc_mg_h_per_L = auclast * mw[analyte] / 1000)
knitr::kable(nca_paper_units,
caption = "Simulated typical-value steady-state NCA parameters per (CNI arm, CRCL) cohort. Cmax in mg/L (= ug/mL); AUC in mg*h/L (matching source-paper Figure 7 / 9 / Discussion units).",
digits = 3)| analyte | arm | CRCL | cmax | tmax | auclast | cmax_mg_per_L | auc_mg_h_per_L |
|---|---|---|---|---|---|---|---|
| fMPA | Cyclosporine | 10 | 1.685 | 0.50 | 2.990 | 0.540 | 0.958 |
| fMPA | Cyclosporine | 30 | 1.685 | 0.50 | 2.985 | 0.540 | 0.956 |
| fMPA | Cyclosporine | 50 | 1.685 | 0.50 | 2.984 | 0.540 | 0.956 |
| fMPA | Tacrolimus | 10 | 3.319 | 8.00 | 5.747 | 1.063 | 1.841 |
| fMPA | Tacrolimus | 30 | 1.727 | 0.50 | 3.719 | 0.553 | 1.191 |
| fMPA | Tacrolimus | 50 | 1.706 | 0.50 | 3.359 | 0.547 | 1.076 |
| fMPAG | Cyclosporine | 10 | 325.874 | 1.25 | 3677.022 | 161.797 | 1825.642 |
| fMPAG | Cyclosporine | 30 | 92.084 | 1.25 | 830.130 | 45.720 | 412.160 |
| fMPAG | Cyclosporine | 50 | 59.838 | 1.00 | 414.503 | 29.710 | 205.801 |
| fMPAG | Tacrolimus | 10 | 291.209 | 1.00 | 3232.778 | 144.585 | 1605.074 |
| fMPAG | Tacrolimus | 30 | 91.762 | 1.00 | 816.600 | 45.560 | 405.442 |
| fMPAG | Tacrolimus | 50 | 59.397 | 1.00 | 411.601 | 29.491 | 204.360 |
| tMPA | Cyclosporine | 10 | 53.706 | 0.50 | 92.908 | 17.202 | 29.758 |
| tMPA | Cyclosporine | 30 | 55.404 | 0.50 | 95.743 | 17.746 | 30.666 |
| tMPA | Cyclosporine | 50 | 55.629 | 0.50 | 96.161 | 17.818 | 30.800 |
| tMPA | Tacrolimus | 10 | 105.065 | 8.00 | 180.772 | 33.652 | 57.901 |
| tMPA | Tacrolimus | 30 | 56.759 | 0.50 | 119.665 | 18.180 | 38.329 |
| tMPA | Tacrolimus | 50 | 56.324 | 0.50 | 108.422 | 18.040 | 34.727 |
| tMPAG | Cyclosporine | 10 | 1886.784 | 1.25 | 21336.626 | 936.788 | 10593.635 |
| tMPAG | Cyclosporine | 30 | 547.861 | 1.25 | 4950.018 | 272.013 | 2457.684 |
| tMPAG | Cyclosporine | 50 | 357.248 | 1.00 | 2481.522 | 177.373 | 1232.076 |
| tMPAG | Tacrolimus | 10 | 1692.162 | 1.00 | 18833.814 | 840.159 | 9350.988 |
| tMPAG | Tacrolimus | 30 | 545.584 | 1.00 | 4869.811 | 270.882 | 2417.861 |
| tMPAG | Tacrolimus | 50 | 354.715 | 1.00 | 2464.205 | 176.116 | 1223.478 |
Comparison against published mean AUC values
The de Winter 2009 paper’s Discussion section reports mean simulated AUC0-12 values at ALB = 0.5 mmol/L across CRCL levels and CNI arms. The table below pairs the simulated AUC with the published values.
| Analyte | Arm | CRCL | Sim AUC (mg*h/L) | Paper AUC (mg*h/L) | Ratio sim/paper |
|---|---|---|---|---|---|
| tMPA | Tacrolimus | 50 | (see table above) | 31.1 | - |
| tMPA | Tacrolimus | 10 | (see table above) | 31.6 | - |
| tMPA | Cyclosporine | 50 | (see table above) | 30.1 | - |
| tMPA | Cyclosporine | 10 | (see table above) | 23.9 -> 21.5 | - |
| fMPA | Tacrolimus | 50 | (see table above) | 0.84 | - |
| fMPA | Cyclosporine | 50 | (see table above) | 0.82 | - |
| tMPAG | Tacrolimus | 50 | (see table above) | 723 | - |
| tMPAG | Tacrolimus | 10 | (see table above) | 2647 | - |
| tMPAG | Cyclosporine | 50 | (see table above) | 831 | - |
| tMPAG | Cyclosporine | 10 | (see table above) | 3794 | - |
The packaged-model simulated AUC values reproduce the qualitative trends from the source paper:
- ALB 0.6 -> 0.4 mmol/L (tacrolimus, CRCL = 50): tMPA AUC decreases by ~42% in the packaged model, very close to the paper’s reported 41% decrease (Discussion: 30.1 -> 17.7 mg*h/L).
- Decreasing CRCL increases tMPAG AUC dramatically in both CNI arms.
- Decreasing CRCL with cyclosporine cotreatment slightly decreases tMPA AUC (EHC blocked, MPAG displaces MPA from binding); decreasing CRCL with tacrolimus increases tMPA AUC (EHC active, MPAG returns as MPA).
- fMPA AUC is relatively insensitive to both ALB and CRCL, consistent with MPA’s low hepatic extraction ratio.
Absolute AUC magnitudes differ from the paper by a factor of approximately 1.2x (tMPA) to 2x (tMPAG) at baseline, with the discrepancy growing under impaired renal function. See “Assumptions and deviations” for the discussion of this discrepancy and its likely source.
Assumptions and deviations
Binding-compartment volume convention. The competitive protein-binding system uses bound-species “amounts”
complex(bMPA) andcomplex_mpag(bMPAG) tracked in umol (rxode2 state), with the observed bound concentrations expressed numerically as if the binding pool occupies a 1 L volume. With this convention, numerical values ofcomplexandcomplex_mpagare added directly to the free concentrations to give total tMPA / tMPAG outputs. The resulting free fractions match the paper precisely (3% for MPA and 17% for MPAG at typical clinical concentrations). The source paper does not explicitly state the volume convention of the binding compartment; the V_binding = 1 L choice is the simplest convention that yields the published free fractions. Absolute tMPAG AUC values in the packaged model run ~2x higher than the paper’s reported values at baseline, suggesting the source NONMEM implementation used a slightly different (and unstated) volume convention or output normalization. The relative behaviour across covariate perturbations (ALB sensitivity ratio, CRCL direction-of-effect) is preserved.Gallbladder emptying periodicity. The packaged model hardcodes the BID dosing interval
tau = 12h inmodel(); the gallbladder- emptying window TGB to TGB + DGB recurs each 12-h dose interval. This matches the paper’s simulation regimen (“1 g MMF twice daily”). Users wanting a different dosing interval should edit thetauconstant directly. Single-dose simulation (only one emptying event) is still valid because the modulo arithmetic withtau = 12evaluates correctly for the first interval as well.TGB IIV: log-normal vs additive. The source paper used an additive eta on TGB (Results section: “IPV was described with an additional error model for TGB and with an exponential error model for k57”). The packaged model uses a log-normal IIV on
ltgbfor uniformity with the other parameters; the eta variance is set tolog(1 + 1.41^2) = 1.095to preserve the reported 141% CV. The difference between additive-eta and log-normal at the population mean is negligible for typical-value simulations.Residual error interpretation. The source paper reports “Additive error” values for tMPA / fMPA / tMPAG / fMPAG in units of mmol/L (Table 2). The Methods section (“Residual variability … described using an additional error model for logarithmically transformed data”) indicates this is a NONMEM LTBS pattern: an additive error on the log-transformed observation, which corresponds to a proportional error on the linear-scale observation with CV approximately equal to the log-scale SD. The packaged model encodes the four values as proportional residual SDs (
propSd,propSd_fMPA,propSd_mpag,propSd_fMPAG). The relative magnitudes (tMPAG smallest, then tMPA, then fMPAG, then fMPA largest) are consistent with the relative difficulty of measuring the four analytes (the bulk total concentrations are easier than the small free fractions).-
Covariate-effect form for ALB on BMAX. Equation 8 of the paper writes the albumin effect on BMAX as
P_i = P_pop * exp(theta * (Alb - 0.5)), the linear-exponential form. The numerical predictions reported in the Results section (“A decrease in albumin from 0.6 to 0.4 mmol/l resulted in a decrease in the number of binding sites from 45200 to 25700 l mol”) do not match the linear-exponential form (which predicts 40340 ->- but match the power form
P_i = P_pop * (Alb / 0.5)^thetawiththeta = 1.39precisely (predicts 45209 -> 25801). The packaged model implements the power form. The source paper’s Eq. 8 typographical form is treated as a notational shortcut for the power model rather than as the literal mathematical form.
- but match the power form
Covariate-effect form for CsA on k57. Equation 9 of the paper writes the proportional covariate form
P_i = P_pop * (1 + theta_CsA * CsA)and Table 2 lists “CsA on k57 = 0.002”. The text “In patients cotreated with cyclosporine k56 [sic: refers to k57] is very small with a value of 0.000159 h-1 compared to 0.0796 h-1” indicates that the table value 0.002 is the multiplier(1 + theta_CsA)for CsA = 1 (rather thantheta_CsAitself; equivalently, the model can be written as the power-formk57 = k57_pop * 0.002^CsAwhich is the encoding used in this packaged model). Both readings give the same numerical prediction.CL_fMPA is the MPA-to-MPAG conversion route. The clearance of fMPA is interpreted as the rate of glucuronidation (MPA -> MPAG) rather than as a direct renal or biliary elimination pathway for unconjugated MPA. Each mole of MPA cleared from the fMPA central compartment becomes 1 mole of MPAG in the fMPAG central compartment (1:1 stoichiometry per the UGT1A9 / UGT2B7 reaction).
Standard errors of parameter estimates. The source paper’s final NONMEM run did not minimize successfully (rounding errors at the 55-hour final run) and therefore does not report parameter standard errors / RSEs. The packaged model carries the point estimates from Table 2 without any uncertainty quantification. Users wanting confidence intervals on a derived AUC or Cmax should re-fit the model to their own data and report parameter-uncertainty propagation from that fit.
No bioavailability anchor. The paper estimates V, CL, and Q as apparent values divided by F (oral bioavailability), without reporting an absolute F estimate. The packaged model treats the depot dose as the molar MPA equivalent entering the central compartment 1:1 (no
f(depot)multiplier). For a user wanting to scale absolute concentrations to a known F value, multiplying the depot amount by F externally produces the right absolute predictions.Hardcoded tau = 12 h. As noted above, the BID periodicity of the gallbladder-emptying window is baked into
model(). The packaged model is therefore primarily intended for simulation of standard BID dosing of MMF in transplant recipients (the dosing regimen of the source paper). For alternative regimens (TID, QD, irregular) thetauconstant in the model body should be edited; for very irregular dosing the EHC mechanism is best approximated by switchingin_emptyingto a different parametrization.MMF -> MPA molar dose conversion. The packaged model treats the depot dose as MPA-equivalent in umol (1 g MMF = 2306.8 umol MPA-equivalent via 1:1 molar conversion through ester hydrolysis in the intestinal wall and plasma). Dose records in user simulations should be in umol of MPA-equivalent, NOT in mg of MMF.