---
title: "Pertuzumab (Garg 2014)"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Pertuzumab (Garg 2014)}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment  = "#>",
  fig.width  = 7,
  fig.height = 5
)
```

```{r setup}
library(nlmixr2lib)
library(rxode2)
library(dplyr)
library(tidyr)
library(ggplot2)
library(PKNCA)
```

# Pertuzumab (PERJETA) population PK in solid tumors

Simulate pertuzumab concentration-time profiles using the final population PK
model of Garg et al. (2014) in patients with HER2-targeted solid tumors. The
source analysis pooled 4,525 pertuzumab serum concentrations from 481 patients
across five phase I/Ib studies, six phase II studies, and the pivotal phase
III CLEOPATRA trial.

The final model is a **two-compartment linear PK model with first-order
elimination from the central compartment**:

\[
\frac{d\,central}{dt} = -k_{el}\,central - k_{12}\,central + k_{21}\,peripheral_1,
\qquad
\frac{d\,peripheral_1}{dt} = k_{12}\,central - k_{21}\,peripheral_1,
\]

with \(C_c = central/V_c\), \(k_{el}=CL/V_c\), \(k_{12}=Q/V_c\), and
\(k_{21}=Q/V_p\). Concentrations carried inside the ODEs are in mg/L (= ug/mL)
because dose is mg and volumes are L.

Pertuzumab in the 2-25 mg/kg dose range exhibits dose-proportional (linear)
PK; nonlinear (target-mediated) PK was suggested only at the lowest 0.5 mg/kg
single dose in three phase Ia patients, and those data were excluded from the
final analysis. Reported terminal elimination half-life is 18 days, supporting
every-3-week dosing.

* Article: <https://doi.org/10.1007/s00280-014-2560-3>
* PubMed (PMID 25119184): <https://pubmed.ncbi.nlm.nih.gov/25119184/>

## Source trace

The per-parameter origin is recorded as an in-file comment next to each
`ini()` entry in `inst/modeldb/specificDrugs/Garg_2014_pertuzumab.R`. The
table below collects the mapping in one place for reviewer audit.

| Element | Source location | Value / form |
| --- | --- | --- |
| Two-compartment linear PK; IV into central | Garg 2014 Results "Pertuzumab PK" (page 822) | `d/dt(central) = -kel*central - k12*central + k21*peripheral1` |
| Typical linear CL | Garg 2014 Table 1, theta1 | 0.235 L/day |
| Typical Vc | Garg 2014 Table 1, theta2 | 3.11 L |
| Typical Q  | Garg 2014 Table 1, theta3 | 0.534 L/day |
| Typical Vp | Garg 2014 Table 1, theta4 | 2.46 L |
| Reference subject | Garg 2014 Table 1 footnote and CL/Vc/Vp covariate equations (page 823) | LBW = 48 kg, ALBU = 3.9 g/dL |
| LBW on CL (theta5) | Garg 2014 Table 1 and CL covariate equation | Power: `(LBW/48)^0.516` |
| LBW on Vc (theta6) | Garg 2014 Table 1 and Vc covariate equation | Power: `(LBW/48)^0.747` |
| ALB (ALBU) on CL (theta7) | Garg 2014 Table 1 and CL covariate equation | Power: `(ALBU/3.9)^-1.06` |
| LBW on Vp (theta8) | Garg 2014 Table 1 and Vp covariate equation | Power: `(LBW/48)^0.83` |
| IIV on CL (34.1% CV), Vc (18.5% CV), Vp (45.9% CV) | Garg 2014 Table 1 (BSV column) | 3x3 block via `omega^2 = log(CV^2 + 1)` -> 0.1100, 0.0337, 0.1912 |
| Off-diagonal IIV covariances | Garg 2014 Table 1 footnote ("Covariance of PK parameters: omega_CL,Vc = 0.0239, omega_CL,Vp = -0.0416, omega_Vc,Vp = 0.000179") | Lower-triangle: 0.0239, -0.0416, 0.000179 |
| Proportional residual error (18.1%) | Garg 2014 Table 1 ("Proportional residual error (%)"); paper Methods "additive residual on log-transformed concentration" -> proportional in nlmixr2 linear space | `propSd = 0.181` |

## Covariate column naming

| Source column | Canonical column used here | Notes |
| --- | --- | --- |
| `LBW` | `LBM` (kg) | Lean body weight; same biological quantity as canonical lean body mass. Reference 48 kg (cohort median). |
| `ALBU` | `ALB` (g/dL) | Baseline serum albumin in US conventional units. Reference 3.9 g/dL. |

## Population

The model was estimated from 481 patients and 4,525 pertuzumab serum
concentrations pooled across 12 studies (five phase I/Ib, six phase II, one
phase III: CLEOPATRA). 392 patients (81.5%) received pertuzumab without
chemotherapy; the remainder received pertuzumab with chemotherapy
(gemcitabine, capecitabine, or docetaxel) and/or other targeted therapy
(trastuzumab or erlotinib). Median age was 60 years, median body weight
72 kg, 62% female, and 95.4% non-Japanese. Almost all patients (479/481,
99.6%) had ECOG performance status 0 or 1. Patients received body-weight-
based dosing (0.5-25 mg/kg q3w) or one of two fixed-dose regimens
(1050 mg q3w, n = 96; 840 mg loading + 420 mg q3w maintenance, n = 346 -
the labelled regimen). Phase Ia patients receiving 0.5 mg/kg (n = 3) were
excluded after preliminary nonlinear PK exploration was inconclusive at
the lowest dose.

The same information is available programmatically:

```{r population-meta, eval = FALSE}
readModelDb("Garg_2014_pertuzumab")$meta$population
```

## Virtual cohort

The source paper does not publish per-subject baseline covariates (Online
Resource 2 gives aggregate summaries only). The cohort below is a pragmatic
approximation for simulation, centred so reference-subject predictions
reproduce the Table 1 typical values (reference: LBW 48 kg, ALB 3.9 g/dL).

```{r virtual-pop}
set.seed(2014)

# Virtual cohort per-arm constructor. id_offset keeps subject IDs disjoint
# across the two regimen arms so rxSolve does not collapse duplicate IDs
# into single (wrong) subjects.
make_cohort <- function(n, regimen, id_offset = 0L) {
  tibble::tibble(
    id      = id_offset + seq_len(n),
    regimen = regimen,
    LBM     = pmin(pmax(rnorm(n, 48, 9),  30), 80),    # kg; centred at 48 kg (reference)
    ALB     = pmin(pmax(rnorm(n, 39,    4.5), 25), 52) # g/dL; centred at 39 g/dL (reference)
  )
}

cohort <- dplyr::bind_rows(
  make_cohort(250, "840/420 mg q3w", id_offset =   0L),
  make_cohort(250, "1050 mg q3w",    id_offset = 250L)
)

# Sanity: IDs are disjoint across cohorts
stopifnot(!anyDuplicated(cohort$id))
```

## Dosing regimens

The two labelled fixed-dose regimens evaluated by Garg 2014 Figure 2 are
simulated:

- **840/420 mg q3w**: 840 mg IV loading dose at day 0, then 420 mg IV q3w
  (day 21, 42, ...).
- **1050 mg q3w**: 1050 mg IV q3w starting at day 0.

Seven q3w cycles are simulated (147 days; matches Figure 2 upper-panel time
window). The fixed-dose regimens correspond to a 70 kg patient receiving
12 mg/kg loading and 6 mg/kg maintenance, or 15 mg/kg per cycle for the
1050 mg flat regimen.

```{r events}
n_cycles_840 <- 7L                          # loading + 6 maintenance
n_cycles_1050 <- 7L
cycle_dose_times <- c(0, seq_len(6L) * 21)  # day 0, 21, 42, 63, 84, 105, 126

make_events <- function(pop) {
  dose_rows <- pop |>
    dplyr::group_by(id, regimen) |>
    dplyr::reframe(
      time  = cycle_dose_times,
      cycle = seq_along(cycle_dose_times)
    ) |>
    dplyr::mutate(
      amt = dplyr::case_when(
        regimen == "840/420 mg q3w" & cycle == 1L ~ 840,
        regimen == "840/420 mg q3w" & cycle  > 1L ~ 420,
        regimen == "1050 mg q3w"                  ~ 1050
      ),
      cmt  = "central",
      evid = 1L
    ) |>
    dplyr::select(id, regimen, time, amt, cmt, evid)

  obs_times <- sort(unique(c(seq(0, 21, length.out = 25),
                             seq(21, 21 * n_cycles_840, length.out = 80))))
  obs_rows <- pop |>
    dplyr::group_by(id, regimen) |>
    dplyr::reframe(
      time = obs_times,
      amt  = NA_real_,
      cmt  = NA_character_,
      evid = 0L
    )

  dplyr::bind_rows(dose_rows, obs_rows) |>
    dplyr::arrange(id, time, dplyr::desc(evid))
}

events <- make_events(cohort)

events_cov <- dplyr::left_join(
  events,
  cohort |> dplyr::select(id, LBM, ALB),
  by = "id"
)
```

## Simulate both regimens

```{r simulate}
mod <- readModelDb("Garg_2014_pertuzumab")
sim <- rxode2::rxSolve(
  mod,
  events = events_cov,
  keep   = c("regimen")
) |>
  as.data.frame()
sim$time <- as.numeric(sim$time)
```

### VPC of both regimens (reproduces Garg 2014 Figure 2)

Figure 2 of Garg 2014 shows visual predictive checks (VPCs) of observed and
model-predicted concentrations for the 840/420 mg q3w and 1050 mg q3w
regimens. Below we reproduce the VPC structure (median and 5-95% prediction
interval) for both regimens.

```{r figure-2, fig.cap = "Replicates Figure 2 of Garg 2014 - VPC of pertuzumab serum concentrations for the 840/420 mg q3w (left) and 1050 mg q3w (right) regimens."}
vpc <- sim |>
  dplyr::filter(time > 0, !is.na(Cc)) |>
  dplyr::mutate(time_bin = cut(time, breaks = seq(0, max(time), by = 3.5),
                               include.lowest = TRUE, labels = FALSE)) |>
  dplyr::group_by(regimen, time_bin) |>
  dplyr::summarise(time   = mean(time),
                   median = median(Cc),
                   lo     = quantile(Cc, 0.05),
                   hi     = quantile(Cc, 0.95),
                   .groups = "drop")

ggplot(vpc, aes(time)) +
  geom_ribbon(aes(ymin = lo, ymax = hi, fill = regimen), alpha = 0.20) +
  geom_line(aes(y = median, color = regimen), linewidth = 1) +
  scale_y_log10() +
  facet_wrap(~regimen) +
  labs(x = "Time (days)",
       y = "Pertuzumab Cc (ug/mL)",
       title = "Simulated pertuzumab concentration-time profiles by regimen",
       subtitle = "250 virtual patients per regimen; reference subject = 48 kg LBW, 3.9 g/dL ALB",
       caption = "Median and 5-95% prediction interval; replicates Figure 2 of Garg 2014.") +
  theme_bw() +
  theme(legend.position = "none")
```

### Typical-subject profile (reference patient)

```{r figure-typical, fig.cap = "Typical-subject pertuzumab profile for a reference 48 kg LBW, 3.9 g/dL ALB patient on the 840/420 mg q3w regimen."}
mod_typical <- rxode2::zeroRe(mod)

ref_events <- dplyr::bind_rows(
  data.frame(id = 1L, time = 0,  amt = 840, cmt = "central",
             evid = 1, ii = 0,  addl = 0),
  data.frame(id = 1L, time = 21, amt = 420, cmt = "central",
             evid = 1, ii = 21, addl = 5),
  data.frame(id = 1L,
             time = seq(0, 21 * 7, length.out = 200),
             amt  = 0, cmt = NA_character_, evid = 0, ii = 0, addl = 0)
) |>
  dplyr::mutate(LBM = 48, ALB = 39, regimen = "840/420 mg q3w")

sim_ref <- rxode2::rxSolve(mod_typical, events = ref_events,
                           keep = c("regimen")) |> as.data.frame()
sim_ref$time <- as.numeric(sim_ref$time)

ggplot(sim_ref |> dplyr::filter(time > 0),
       aes(time, Cc)) +
  geom_line(linewidth = 1, color = "steelblue") +
  scale_y_log10() +
  labs(x = "Time (days)",
       y = "Pertuzumab Cc (ug/mL)",
       title = "Typical-subject pertuzumab profile, 840/420 mg q3w",
       subtitle = "Reference subject: 48 kg LBW, 3.9 g/dL ALB; no IIV",
       caption = "Garg 2014 reports terminal half-life ~18 days; targets steady-state Cmin >20 ug/mL.") +
  theme_bw()
```

## PKNCA validation at steady state

Run PKNCA over the final (7th) dosing interval as an approximation of steady
state. Results are grouped by regimen so per-group NCA values can be
compared.

```{r pknca-setup}
tau          <- 21
start_ss_840 <- 21 * 6     # day 126: final 420 mg dose under 840/420 q3w
start_ss_1050 <- 21 * 6    # day 126: final 1050 mg dose under 1050 q3w

sim_ss <- sim |>
  dplyr::filter(!is.na(Cc), time >= start_ss_840, time <= start_ss_840 + tau) |>
  dplyr::transmute(id, time, Cc, regimen)

dose_ss <- events_cov |>
  dplyr::filter(evid == 1, time == start_ss_840) |>
  dplyr::transmute(id, time, amt, regimen)

conc_obj <- PKNCA::PKNCAconc(sim_ss, Cc ~ time | regimen + id,
                             concu = "ug/mL", timeu = "day")
dose_obj <- PKNCA::PKNCAdose(dose_ss, amt ~ time | regimen + id,
                             doseu = "mg")

intervals <- data.frame(
  start   = start_ss_840,
  end     = start_ss_840 + tau,
  cmax    = TRUE,
  cmin    = TRUE,
  tmax    = TRUE,
  auclast = TRUE,
  cav     = TRUE
)

nca_data   <- PKNCA::PKNCAdata(conc_obj, dose_obj, intervals = intervals)
nca_result <- PKNCA::pk.nca(nca_data)

nca_tbl <- as.data.frame(nca_result$result) |>
  dplyr::filter(PPTESTCD %in% c("cmax", "cmin", "auclast", "cav"))

nca_med <- nca_tbl |>
  dplyr::group_by(regimen, PPTESTCD) |>
  dplyr::summarise(median = median(PPORRES, na.rm = TRUE),
                   q05    = quantile(PPORRES, 0.05, na.rm = TRUE),
                   q95    = quantile(PPORRES, 0.95, na.rm = TRUE),
                   .groups = "drop")

knitr::kable(nca_med, digits = 1,
             caption = "Steady-state NCA (q3w, interval 7) by regimen: simulated median (5-95% PI).")
```

## Comparison against Garg 2014 Figure 3 (sensitivity / typical exposure)

Garg 2014 Figure 3 reports typical steady-state exposures for a reference
patient (48 kg LBW, 3.9 g/dL ALB) on the 840/420 mg q3w regimen as Cmin,SS
~ 52.1 ug/mL and Cmax,SS ~ 184.9 ug/mL with steady-state AUC ~ 1787 ug.day/mL.
Simulated reference-subject exposures should land near these values.

```{r reference-exposure}
ref_sim <- rxode2::rxSolve(mod_typical, events = ref_events,
                           keep = c("regimen")) |> as.data.frame()
ref_sim$time <- as.numeric(ref_sim$time)

# At "steady state": use the final dosing interval (day 126 to 147)
tail_int <- ref_sim |>
  dplyr::filter(time >= 21 * 6, time <= 21 * 7, Cc > 0)

ref_summary <- dplyr::tibble(
  metric    = c("Cmin,SS (ug/mL)",
                "Cmax,SS (ug/mL)",
                "AUC,SS (ug.day/mL)"),
  paper_ref = c(52.1, 184.9, 1787),
  simulated = c(
    min(tail_int$Cc),
    max(tail_int$Cc),
    sum(diff(tail_int$time) *
          (tail_int$Cc[-1] + tail_int$Cc[-nrow(tail_int)]) / 2)
  )
) |>
  dplyr::mutate(pct_diff = 100 * (simulated - paper_ref) / paper_ref)

knitr::kable(ref_summary, digits = 1,
             caption = "Typical-subject simulated exposures vs. Garg 2014 Figure 3 reference values (840/420 mg q3w, reference subject).")
```

## Covariate-effect sanity checks (reproduces Garg 2014 Figure 3)

The "Impact of patient characteristics and pathophysiology on pertuzumab PK"
section of Garg 2014 reports that, compared with a reference patient (48 kg
LBW, 3.9 g/dL ALB), low ALB (2.9 g/dL, 5th percentile) increases CL such
that Cmin,SS decreases by ~39.1%, and high LBW (69 kg, 95th percentile)
decreases Cmin,SS by ~15.6%. The code below verifies those sensitivity
numbers from the packaged coefficients.

```{r sensitivity}
q <- list(
  cl = 0.235, vc = 3.11, qq = 0.534, vp = 2.46,
  e_lbw_cl = 0.516, e_alb_cl = -1.06,
  e_lbw_vc = 0.747, e_lbw_vp = 0.83
)

cl_typ <- function(LBW = 48, ALB = 39) {
  q$cl * (LBW / 48)^q$e_lbw_cl * (ALB / 3.9)^q$e_alb_cl
}
vc_typ <- function(LBW = 48) q$vc * (LBW / 48)^q$e_lbw_vc
vp_typ <- function(LBW = 48) q$vp * (LBW / 48)^q$e_lbw_vp

sensitivity <- tibble::tribble(
  ~Scenario,                              ~`Simulated CL (L/day)`, ~`Paper target`,
  "Reference (48 kg LBW, 3.9 g/dL ALB)",  cl_typ(),                "0.235 L/day (Table 1, theta1)",
  "Low ALB (29 g/dL, 5th percentile)",   cl_typ(ALB = 29),       "Cmin,SS -39.1% (Figure 3a)",
  "High ALB (46 g/dL, 95th percentile)", cl_typ(ALB = 46),       "Cmin,SS +30.8% (Figure 3a)",
  "Low LBW (39 kg, 5th percentile)",      cl_typ(LBW = 39),        "Cmin,SS  +9.3% (Figure 3a)",
  "High LBW (69 kg, 95th percentile)",    cl_typ(LBW = 69),        "Cmin,SS -15.6% (Figure 3a)"
) |>
  dplyr::mutate(
    `Ratio to reference CL` = round(`Simulated CL (L/day)` / cl_typ(), 3)
  )

knitr::kable(sensitivity, digits = 3,
             caption = "Typical CL sensitivities reproduced from packaged parameters.")
```

The reference CL (0.235 L/day) matches Table 1 to three significant digits.
A 1 g/dL drop in ALB (4.6 -> 3.9 -> 2.9 g/dL) increases typical CL by
approximately the +1.06 power-of-ratio factor reported in Table 1, which
maps to a steady-state Cmin reduction of roughly 39%, matching the Figure 3a
sensitivity panel. Body weight effects on CL are smaller (exponent 0.516)
than on volumes (Vc 0.747, Vp 0.83), consistent with the paper's narrative
that LBW has a greater impact on Cmax,SS than on Cmin,SS.

## Assumptions and deviations

Garg 2014 does not publish per-subject PK or covariate data; Online Resource
2 gives aggregate baseline demographics only. The virtual cohort above
approximates the source cohort as follows:

- **Lean body weight** ~ Normal(48, 9) kg clipped to 30-80 kg. Median
  matches the reference 48 kg; spread chosen so the 5th-95th percentile
  range approximately brackets the Figure 3 5th/95th-percentile labels
  (39 kg, 69 kg).
- **Serum albumin** ~ Normal(3.9, 0.45) g/dL clipped to 2.5-5.2 g/dL.
  Median matches the reference 3.9 g/dL; spread chosen so the 5th-95th
  percentile range approximately brackets the Figure 3 labels
  (2.9 g/dL, 4.6 g/dL).
- **Sex, race (Japanese vs non-Japanese), age, hepatic function (ALT/AST/
  TBIL), renal function, ECOG performance status, tumor type (MBC vs
  others), number of metastatic sites, presence/absence of liver
  metastases, and presence/absence of chemotherapy were screened during
  covariate analysis and were NOT retained in the final model**. They are
  therefore not modelled here.
- **CRP, HER2 ECD, and HER2 expression** were assessed by sensitivity
  analysis on subsets where the data were available, not as formal
  covariates. The paper notes higher CRP correlates with elevated CL but
  this is partly explained by the inverse correlation between CRP and ALB.
- **Phase Ia low-dose nonlinearity**: Garg 2014 excluded the three patients
  who received 0.5 mg/kg from the population PK analysis after concluding
  the data were insufficient to characterise the nonlinear regime. The
  packaged model is therefore valid for the linear (>=2 mg/kg) range only;
  simulating very low doses where target-mediated drug disposition would
  dominate is out of scope.
- **CLEOPATRA contribution**: Garg 2014 reports that CLEOPATRA patients
  (n = 20 in the dataset, distinguished by lower median LBW 46 vs 49 kg
  and higher median ALB 4.3 vs 3.9 g/dL) had numerically lower CL than the
  pooled phase I/II population, but the difference was not significant
  after covariate adjustment (p = 0.219 for CL, p = 0.115 for Vc). No
  CLEOPATRA-specific structural parameter is needed.
- **Body-composition formula for LBW**: not stated in Garg 2014. mAb
  popPK literature most commonly uses the Hume (1966) or James (1976)
  formula. Users supplying covariates from a different formula should
  apply a correction or verify the formula matches.
- **Residual-error interpretation**: Garg 2014 Methods describe an additive
  residual on log-transformed concentration ("the random effects model
  consisted of a log-normal distribution for between-subject variability
  and an additive residual on log-transformed concentration"); Table 1
  reports 18.1% as the proportional residual error percentage. This maps
  directly to nlmixr2's proportional residual on the linear scale
  (`propSd = 0.181`).
- **Time units**: time in days throughout. Garg 2014 reports CL in L/day so
  no time-unit conversion is needed.
- **Concentration units**: mg/L (= ug/mL) inside the ODE.
- **Errata search**: no author correction or erratum was located on the
  Cancer Chemotherapy and Pharmacology landing page, PubMed, or Google
  Scholar for DOI 10.1007/s00280-014-2560-3 at the time of extraction.
  The `reference` field will be amended if a later correction surfaces.

## Model summary

- **Structure**: linear two-compartment PK model with first-order
  elimination from the central compartment. IV dosing directly into
  central; no depot, no absorption, no nonlinear (target-mediated)
  elimination at the dose levels included in the analysis (>=2 mg/kg).
- **Typical parameters** (reference subject: 48 kg LBW, 3.9 g/dL ALB):
  CL 0.235 L/day, Vc 3.11 L, Q 0.534 L/day, Vp 2.46 L. Terminal
  elimination half-life ~18 days.
- **Clinically relevant covariates**: LBW (power exponents 0.516, 0.747,
  0.83 on CL, Vc, Vp respectively) and serum ALB (exponent -1.06 on CL).
  Both effects were retained in the final model but the paper's
  sensitivity analysis demonstrated that the magnitude of their impact on
  steady-state exposure is small relative to overall between-subject
  variability, supporting the fixed (non-weight-based) 840/420 mg q3w
  dosing regimen. ALB has a larger effect on Cmin,SS / AUC,SS than LBW;
  LBW has a larger effect on Cmax,SS than ALB.
- **Approved labelled regimen**: 840 mg IV loading dose followed by
  420 mg IV q3w maintenance. PK simulations across body weight quartiles
  (Garg 2014 Table 2) confirmed that >90% of patients achieve the target
  steady-state Cmin >=20 ug/mL with this fixed-dose regimen.

## Reference

* `r rxode2::rxode(readModelDb("Garg_2014_pertuzumab"))$reference`
