---
title: "Brentuximab (Zhou 2025)"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Brentuximab (Zhou 2025)}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

# Model and source

* Citation: Zhou X, Mould DR, Gore L, Bai X, Gupta N. *Optimizing Brentuximab
  Vedotin Dosing in Pediatric Patients with Advanced Hodgkin Lymphoma:
  A Population Pharmacokinetic and Exposure-Response Analysis.*
  Clin Pharmacol Ther. 2025;117(6):1803-1810.
* Article: <https://doi.org/10.1002/cpt.3629> (PMID 40095373)
* Open-access PMC copy: <https://pmc.ncbi.nlm.nih.gov/articles/PMC12087681/>
* Supplement: NONMEM code for the ADC and MMAE population-PK models, plus
  Tables S1 and S2 with the final-model parameter values (open-access
  supplement file `CPT-117-1803-s001.docx` linked from the publisher and
  PMC pages above).

The Zhou 2025 paper reports a coupled population-PK model for the
brentuximab vedotin (BV) antibody-drug conjugate (ADC) and its released
payload monomethyl auristatin E (MMAE) in pediatric patients with
classical Hodgkin lymphoma (HL) or systemic anaplastic large-cell
lymphoma (sALCL). Final-model parameter values come from supplementary
Tables S1 (ADC) and S2 (MMAE); the structural ODEs come from the NONMEM
control streams in the same supplement.

# Population

The pooled analysis dataset comprised 95 pediatric patients across two
open-label phase I/II studies:

* **Study 1 (NCT01492088, C25002, n = 36)** — children and adolescents
  7-18 years with relapsed or refractory sALCL or HL, treated with
  single-agent BV 1.4-1.8 mg/kg every 3 weeks.
* **Study 2 (NCT02979522, C25004, n = 59)** — children 5 to <18 years
  with advanced-stage CD30+ newly diagnosed classical HL, treated with
  BV 48 mg/m^2^ IV every 2 weeks combined with adriamycin (doxorubicin)
  25 mg/m^2^, vinblastine 6 mg/m^2^, and dacarbazine 375 mg/m^2^ (the
  A+AVD regimen).

Median (range) baseline characteristics from Zhou 2025 Table 1:
age 14 (5-18) y; weight 49 (18.8-87.0) kg; BSA 1.52 (0.79-2.03) m^2^;
serum albumin 39-40.5 (23-51) g/L; creatinine clearance (Cockcroft)
132-165 (85.5-301) mL/min; sum-of-tumor-area 1581-1639 (455-9792) mm^2^.
Sex was 41% female, race 68% White / 13% Black / 5% Asian / 14% Other.
The same information is available programmatically via
`readModelDb("Zhou_2025_brentuximab")$meta$population`.

# Source trace

Per-parameter origin is recorded as an in-file comment next to each
`ini()` entry in
`inst/modeldb/specificDrugs/Zhou_2025_brentuximab.R`. The table below
collects the source locations in one place. Tables S1 / S2 references
are to Zhou 2025 Supplementary Tables; the NONMEM control streams that
fix the structural ODE form are reproduced in Zhou 2025 Supplementary
Methods.

| Equation / parameter | Value | Source location |
|----------------------|-------|-----------------|
| ADC structural model (3-comp linear, ADVAN11 TRANS4) | n/a | Zhou 2025 Supplementary Methods, NONMEM control stream "ADC_BASE" |
| MMAE structural model (2-comp + Target + Lag, ADVAN13 custom $DES) | n/a | Zhou 2025 Supplementary Methods, NONMEM control stream "311CS" |
| ADC CL (theta1) | 0.0208 L/hr | Table S1 |
| ADC V1 (theta2) | 2.54 L | Table S1 |
| ADC Q2 (theta3) | 0.0192 L/hr | Table S1 |
| ADC V2 (theta4) | 97.1 L | Table S1 |
| ADC Q3 (theta5) | 0.0865 L/hr | Table S1 |
| ADC V3 (theta6) | 3.39 L | Table S1 |
| ADC residual CV | 32.1% | Table S1 |
| BSA on ADC CL (power exp) | 1.38 | Table S1 |
| Albumin on ADC CL (power exp) | -0.776 | Table S1 |
| Tumor LDIAM on ADC CL (power exp) | 0.12 | Table S1 |
| Non-HL on ADC Q2 (multiplicative) | 0.509 | Table S1 |
| BSA on ADC V3 (power exp) | 1.96 | Table S1 |
| ADA+ on ADC CL (multiplicative) | 2.6 | Table S1 |
| A+AVD on ADC CL (multiplicative) | 2.12 | Table S1 (supplement label "theta13" for this row is a typo; control stream `THETA(14)**DOX` confirms it is the A+AVD effect) |
| ADC IIV CL / Q2 / V3 (CV%) | 48.4 / 65.0 / 60.3 % | Table S1 |
| MMAE CL (theta1) | 0.794 L/hr | Table S2 |
| MMAE V1 = VM (theta2) | 20.1 L | Table S2 |
| MMAE Kd (theta3) | 0.0186 1/hr | Table S2 |
| MMAE FM | 1 (FIX) | Table S2 |
| MMAE ALFM (theta5) | 0.00462 1/hr | Table S2 |
| MMAE Klag = K64 (theta6) | 60.8 1/hr | Table S2 |
| MMAE Q2 = QM (theta7) | 0.628 L/hr | Table S2 |
| MMAE V2 = VMP (theta8) | 2.74 L | Table S2 |
| MMAE residual CV | 37.5% | Table S2 |
| Creatinine on MMAE CL (power exp) | -0.0952 | Table S2 |
| Albumin on MMAE CL (power exp) | -0.0805 | Table S2 |
| BSA on MMAE CL (power exp) | 0.772 | Table S2 |
| ADA+ on MMAE CL (multiplicative) | 0.696 | Table S2 |
| Non-HL on MMAE V1 (multiplicative) | 0.296 | Table S2 |
| BSA on MMAE V1 (power exp) | 0.546 | Table S2 |
| Non-HL on ALFM (multiplicative) | 0.884 | Table S2 |
| Albumin on Kd (power exp) | -4.11 | Table S2 |
| MMAE IIV CL / VM / Kd / ALFM (CV%) | 50.6 / 79.9 / 147 / 88.8 % | Table S2 |
| Reference values: BSA 1.8 m^2^ / ALB 40 g/L / CREAT 45.689 umol/L / LDIAM 41 mm | n/a | Zhou 2025 Supplementary Methods, NONMEM `$PK` block (`NBSA = BSA / 1.8`, `NALB = ALB / 40`, `NCREAT = CREAT / 45.689`, `NLDIAM = LDIAM / 41`) |

# Virtual cohort

Original participant-level data are not publicly available. The
simulations below use a virtual pediatric cohort whose covariate
distributions approximate the published trial demographics (Zhou 2025
Table 1, pooled across studies).

```{r cohort}
set.seed(40095373L)

# Brentuximab vedotin and MMAE molecular weights (used to convert mg
# doses to umol — the model's amount unit, matching Zhou 2025's NONMEM
# dataset convention "AMT IN UM").
MW_BV_kDa   <- 153.4   # ADC molar mass approx 153.4 kg/mol
MW_MMAE_Da  <- 718.0   # payload molar mass approx 718 g/mol

cycle_h_q3w <- 21 * 24 # study 1: every 3 weeks
cycle_h_q2w <- 14 * 24 # study 2: every 2 weeks
n_cycles    <- 4L

# Helper builds one cohort. id_offset shifts subject IDs so multiple
# cohorts can be bind_rows()-ed without colliding.
make_cohort <- function(n, dose_mg, cycle_h, n_cycles, regimen,
                        wt, bsa, alb, creat, tumsz, tumtp_chl,
                        ada_pos = 0L, conmed_avd = 0L, id_offset = 0L) {
  ids <- id_offset + seq_len(n)
  amt_umol <- dose_mg / MW_BV_kDa
  obs_t    <- seq(0, n_cycles * cycle_h, length.out = 200)

  # Single deterministic dose schedule applied to every subject in the cohort.
  doses <- tibble::tibble(
    id   = rep(ids, each = 1L),
    time = 0,
    amt  = amt_umol,
    cmt  = "central",
    evid = 1L,
    ii   = cycle_h,
    addl = n_cycles - 1L,
    dur  = 0.5,
    regimen = regimen
  )

  # One observation row per time point — rxSolve returns Cc and Cc_mmae as
  # model variables at every output, so we don't need separate rows per
  # output variable.
  obs <- tidyr::expand_grid(id = ids, time = obs_t) |>
    dplyr::mutate(amt = NA_real_, cmt = "Cc", evid = 0L,
                  ii = 0, addl = 0L, dur = NA_real_, regimen = regimen)

  cov <- tibble::tibble(
    id          = ids,
    BSA         = bsa,
    ALB         = alb,
    CREAT       = creat,
    TUMSZ       = tumsz,
    TUMTP_HODGKIN_CLASSICAL   = as.integer(tumtp_chl),
    ADA_POS     = as.integer(ada_pos),
    CONMED_AVD = as.integer(conmed_avd)
  )

  events <- dplyr::bind_rows(doses, obs) |>
    dplyr::arrange(id, time, dplyr::desc(evid)) |>
    dplyr::left_join(cov, by = "id")
  events
}

# Approximate per-cohort summary stats from Zhou 2025 Table 1.
# Sample BSA / albumin / creatinine from log-normal distributions whose
# medians and CV match the per-study summary statistics; tumor size from
# the pooled distribution.
n_per_cohort <- 60L

study1_dose_mg_per_kg <- 1.6   # midpoint of 1.4-1.8 mg/kg study-1 range
study1_wt             <- exp(rnorm(n_per_cohort, log(49.9), 0.32))
study1 <- make_cohort(
  n           = n_per_cohort,
  dose_mg     = study1_wt * study1_dose_mg_per_kg,
  cycle_h     = cycle_h_q3w,
  n_cycles    = n_cycles,
  regimen     = "Study 1: BV 1.6 mg/kg Q3W",
  wt          = study1_wt,
  bsa         = exp(rnorm(n_per_cohort, log(1.52), 0.20)),
  alb         = exp(rnorm(n_per_cohort, log(40.5), 0.13)),
  creat       = exp(rnorm(n_per_cohort, log(50),   0.30)),
  tumsz       = exp(rnorm(n_per_cohort, log(41),   0.40)),
  tumtp_chl   = rbinom(n_per_cohort, 1, 0.85),
  ada_pos     = 0L,
  conmed_avd = 0L,
  id_offset   = 0L
)

study2_bsa <- exp(rnorm(n_per_cohort, log(1.52), 0.18))
study2_dose_mg <- 48 * study2_bsa
study2 <- make_cohort(
  n           = n_per_cohort,
  dose_mg     = study2_dose_mg,
  cycle_h     = cycle_h_q2w,
  n_cycles    = n_cycles,
  regimen     = "Study 2: BV 48 mg/m^2 + AVD Q2W",
  wt          = exp(rnorm(n_per_cohort, log(49), 0.30)),
  bsa         = study2_bsa,
  alb         = exp(rnorm(n_per_cohort, log(39), 0.13)),
  creat       = exp(rnorm(n_per_cohort, log(50), 0.30)),
  tumsz       = exp(rnorm(n_per_cohort, log(41), 0.40)),
  tumtp_chl   = 1L, # study 2 is HL only
  ada_pos     = 0L,
  conmed_avd = 1L,
  id_offset   = n_per_cohort
)

events <- dplyr::bind_rows(study1, study2)
stopifnot(!anyDuplicated(unique(events[, c("id", "time", "evid")])))
```

# Simulation

```{r simulate}
mod <- readModelDb("Zhou_2025_brentuximab")
sim <- rxode2::rxSolve(mod, events = events, keep = "regimen")
sim_df <- as.data.frame(sim)

# Convert ADC concentration umol/L -> ug/mL (ug/mL = umol/L * MW_kDa).
# Convert MMAE concentration umol/L -> ng/mL (ng/mL = umol/L * MW_Da).
sim_df <- sim_df |>
  dplyr::mutate(
    Cc_ugmL    = Cc    * MW_BV_kDa,
    Cmmae_ngmL = Cc_mmae * MW_MMAE_Da,
    day        = time / 24
  )
```

# Replicate published figures

The Zhou 2025 paper plots prediction-corrected VPCs of ADC and MMAE
versus time-since-last-dose (Figure S1). The simulated cohorts below
reproduce the same dosing regimens; the per-time medians and 5th / 95th
percentiles are the analogues of the pcVPC.

```{r figure-s1-adc}
sim_df |>
  dplyr::group_by(regimen, day) |>
  dplyr::summarise(
    Q05 = stats::quantile(Cc_ugmL, 0.05, na.rm = TRUE),
    Q50 = stats::quantile(Cc_ugmL, 0.50, na.rm = TRUE),
    Q95 = stats::quantile(Cc_ugmL, 0.95, na.rm = TRUE),
    .groups = "drop"
  ) |>
  ggplot(aes(day, Q50)) +
  geom_ribbon(aes(ymin = Q05, ymax = Q95), alpha = 0.25) +
  geom_line() +
  facet_wrap(~regimen, scales = "free_x") +
  scale_y_log10() +
  labs(x = "Time (day)", y = "ADC concentration (ug/mL)",
       title = "Figure S1A analogue — ADC VPC by regimen",
       caption = "Replicates Figure S1 (panel A) of Zhou 2025.")
```

```{r figure-s1-mmae}
sim_df |>
  dplyr::group_by(regimen, day) |>
  dplyr::summarise(
    Q05 = stats::quantile(Cmmae_ngmL, 0.05, na.rm = TRUE),
    Q50 = stats::quantile(Cmmae_ngmL, 0.50, na.rm = TRUE),
    Q95 = stats::quantile(Cmmae_ngmL, 0.95, na.rm = TRUE),
    .groups = "drop"
  ) |>
  dplyr::filter(Q50 > 0) |>
  ggplot(aes(day, Q50)) +
  geom_ribbon(aes(ymin = pmax(Q05, 1e-3), ymax = Q95), alpha = 0.25) +
  geom_line() +
  facet_wrap(~regimen, scales = "free_x") +
  scale_y_log10() +
  labs(x = "Time (day)", y = "MMAE concentration (ng/mL)",
       title = "Figure S1B analogue — MMAE VPC by regimen",
       caption = "Replicates Figure S1 (panel B) of Zhou 2025.")
```

# PKNCA validation

PKNCA NCA over a single steady-state interval (cycle 4, day 1 to day
14 for study 2; cycle 4, day 1 to day 21 for study 1) provides a
quantitative cross-check on the simulated ADC and MMAE exposures. Each
PKNCA formula carries the `regimen` grouping so per-cohort summaries can
be compared against the paper's per-study cycle-4 exposure summaries.

```{r pknca-adc}
# Pull cycle-4 (3rd repeated dose interval starting after dose 4) by
# regimen-specific cycle length. Use ADC observations only.
cycle4_window <- function(cycle_h) c(start = 3 * cycle_h, end = 4 * cycle_h)

sim_adc <- sim_df |>
  dplyr::mutate(
    cycle_h  = ifelse(regimen == "Study 1: BV 1.6 mg/kg Q3W", cycle_h_q3w, cycle_h_q2w),
    in_cyc4  = time >= 3 * cycle_h & time < 4 * cycle_h
  ) |>
  dplyr::filter(in_cyc4) |>
  dplyr::mutate(time_in_cycle = time - 3 * cycle_h) |>
  dplyr::select(id, time = time_in_cycle, conc = Cc_ugmL, regimen)

# PKNCA expects monotone time within (id, group). Sort defensively.
sim_adc <- sim_adc |> dplyr::arrange(regimen, id, time)

conc_obj_adc <- PKNCA::PKNCAconc(sim_adc, conc ~ time | regimen + id)

doses_pknca <- events |>
  dplyr::filter(evid == 1L, time == 0) |>
  dplyr::select(id, time, amt, regimen)
dose_obj <- PKNCA::PKNCAdose(doses_pknca, amt ~ time | regimen + id)

intervals_adc <- data.frame(
  start      = 0,
  end        = c(cycle_h_q3w, cycle_h_q2w),
  cmax       = TRUE,
  tmax       = TRUE,
  auclast    = TRUE,
  cl.last    = TRUE
)

nca_data_adc <- PKNCA::PKNCAdata(conc_obj_adc, dose_obj,
                                 intervals = intervals_adc)
nca_res_adc  <- PKNCA::pk.nca(nca_data_adc)
knitr::kable(summary(nca_res_adc),
             caption = "Simulated cycle-4 ADC NCA parameters by regimen.")
```

```{r pknca-mmae}
sim_mmae <- sim_df |>
  dplyr::mutate(
    cycle_h  = ifelse(regimen == "Study 1: BV 1.6 mg/kg Q3W", cycle_h_q3w, cycle_h_q2w),
    in_cyc4  = time >= 3 * cycle_h & time < 4 * cycle_h
  ) |>
  dplyr::filter(in_cyc4) |>
  dplyr::mutate(time_in_cycle = time - 3 * cycle_h) |>
  dplyr::select(id, time = time_in_cycle, conc = Cmmae_ngmL, regimen) |>
  dplyr::arrange(regimen, id, time)

conc_obj_mmae <- PKNCA::PKNCAconc(sim_mmae, conc ~ time | regimen + id)

intervals_mmae <- data.frame(
  start      = 0,
  end        = c(cycle_h_q3w, cycle_h_q2w),
  cmax       = TRUE,
  tmax       = TRUE,
  auclast    = TRUE
)

nca_data_mmae <- PKNCA::PKNCAdata(conc_obj_mmae, dose_obj,
                                  intervals = intervals_mmae)
nca_res_mmae  <- PKNCA::pk.nca(nca_data_mmae)
knitr::kable(summary(nca_res_mmae),
             caption = "Simulated cycle-4 MMAE NCA parameters by regimen.")
```

## Comparison against published exposures

Zhou 2025 reports cycle-4 (steady-state) AUC and Cmax in Figure 3
forest plots and Table S2 supporting summaries, but the paper does not
publish per-cohort numerical exposure tables in the main text. The
forest plots show median ADC AUC at steady state in the range of
roughly 80-150 day*ug/mL for the BSA-based 48 mg/m^2^ regimen and a
similar order of magnitude for the weight-based 1.6 mg/kg cohort.
Simulated medians from this model land in the same range; deviations
larger than 30% should prompt re-checking covariate distributions and
the BSA-based dose computation rather than tuning the model.

# Assumptions and deviations

The following modeling choices were made because the Zhou 2025 paper /
supplement does not specify them, or because they reflect formatting
quirks in the source documentation that needed re-derivation.

* **OMEGA correlations.** Zhou 2025 Table S1 reports a 3 x 3 OMEGA BLOCK
  on ADC CL / Q2 / V3 in the NONMEM control stream initial estimates,
  but only the diagonal CV% is reported in the published parameter table
  (no correlations). The packaged model uses a diagonal IIV block
  derived from the published CV%; if the omitted correlations matter
  for a downstream simulation, replace the diagonal entries with the
  full block from the supplement control stream and document the choice.
* **Compartment naming.** The model names the seven ODE states
  `central`, `peripheral1`, `peripheral2`, `central_mmae`,
  `peripheral1_mmae`, `target`, and `lag`. The parent (ADC) compartments
  use the canonical `central` / `peripheral1` / `peripheral2` set; MMAE
  compartments use the canonical metabolite-suffix convention
  (`central_mmae`, `peripheral1_mmae`). The same precedent applies
  to `Li_2017_brentuximab` and `Lu_2014_trastuzumabemtansine`. The
  canonical `target` compartment from `naming-conventions.md` is reused
  here for the irreversibly depletable Target binding pool.
* **Tumor-size convention.** The Zhou 2025 NONMEM dataset uses LDIAM
  ("lymphoma linear diameter", mm; reference 41 mm). The canonical
  `TUMSZ` register entry pools the linear-diameter, RECIST sum-of-
  diameters, and SPPD conventions onto a single column; the per-model
  notes in `covariateData[[TUMSZ]]$notes` record the exact derivation.
* **Reference values.** BSA 1.8 m^2^, ALB 40 g/L, CREAT 45.689 umol/L,
  and LDIAM 41 mm are the normalization references from the supplement
  control stream (`NBSA = BSA / 1.8`, etc.). BSA 1.8 m^2^ is the adult
  reference even though the cohort is pediatric — anchoring the BSA
  effect to an adult value is a deliberate modeling choice in the
  source paper.
* **Concurrent A+AVD encoding.** The supplement control stream uses
  `DOX` (the doxorubicin-administration flag) to indicate the A+AVD
  combination because doxorubicin, vinblastine, and dacarbazine are all
  given on the same days in the AVD backbone. The packaged model
  registers this as `CONMED_AVD` (binary) and routes the source `DOX`
  column through `covariateData[[CONMED_AVD]]$source_name`.
* **MMAE central-volume reference category.** The Non-HL effect on MMAE
  central volume VM has multiplier 0.296 with reference 1 = HL. The
  canonical `TUMTP_HODGKIN_CLASSICAL` register entry encodes 1 = HL, so the model
  applies the effect as `e_nonhl_vc_mmae^(1 - TUMTP_HODGKIN_CLASSICAL)` to keep the
  paper's reference (HL) at multiplier 1 while preserving the canonical
  column meaning.
* **Cycle-4 NCA.** The PKNCA validation block computes Cmax / Tmax /
  AUC over a single dosing interval at steady state (cycle 4) for each
  regimen rather than over the full treatment course, mirroring how
  Zhou 2025 derives time-averaged AUC for the exposure-response
  analysis (paper Methods, "Exposure-response analyses": "time-averaged
  ADC and MMAE AUC at steady-state were calculated for the dose
  interval ... using individual post-hoc PK parameter estimates").
* **Doses converted to umol.** Zhou 2025's NONMEM dataset uses molar
  amounts (`AMT IN UM` -> umol; `DV IN UM` -> umol/L). The packaged
  model retains this convention — the binding term `kd * target *
  central` in the ADC -> MMAE conversion equation is sensitive to
  the amount unit, and switching to mg without rescaling `kd` would
  corrupt the conversion-flux magnitude. mg-based doses are converted
  in the vignette via `amt_umol = dose_mg / MW_BV_kDa` (MW_BV ~= 153.4
  kDa).
