Skip to contents

Model and source

  • Citation: Betts A, Haddish-Berhane N, Shah DK, van der Graaf PH, Barletta F, King L, Clark T, Kamperschroer C, Root A, Hooper A, Chen X. A translational quantitative systems pharmacology model for CD3 bispecific molecules: application to quantify T cell-mediated tumor cell killing by P-Cadherin LP DART. AAPS J. 2019;21(4):66. doi:10.1208/s12248-019-0332-z
  • Description: QSP. Preclinical (mouse). Translational quantitative systems pharmacology model for the CD3 x P-cadherin LP DART bispecific PF-06671008, fit to HCT-116 xenografts in NSG mice with human PBMC engraftment. Couples bispecific PK (2-cpt linear), permeability-diffusion tumor drug disposition, trimer (drug-CD3-Pcad) formation in the TME, and tumor growth via a 4-compartment transduction (Simeoni-based) model (Betts 2019 AAPS J).
  • Article: https://doi.org/10.1208/s12248-019-0332-z

Betts 2019 develops a translational quantitative systems pharmacology (QSP) model for the CD3 x P-cadherin LP DART bispecific PF-06671008. The mouse fit couples a 2-compartment linear PK, a permeability-diffusion tumor-drug disposition (ref 19 in Betts 2019), mass-action trimer (drug-CD3-Pcad) formation in the tumor microenvironment, and a 4-compartment tumor growth transduction (Simeoni-based) model driving tumor volume dynamics. The model was fit in Monolix v4.3.3 to three mouse tumor models (HCT-116 T cell engrafted, HCT-116 adoptive transfer, SUM-149 adoptive transfer); this extraction packages the primary HCT-116 T cell engrafted parameter set (Betts 2019 Table II column a) as the shipped model. Tumor T cell density tcellst_tumor is a fixed system parameter (steady-state approximation to the paper’s piecewise Prate-driven TIL kinetics of Eqs 9-11); Assumptions and deviations discusses the simplification.

The paper also translates the QSP framework to human simulations that add soluble P-cadherin (sPcad) binding in the central compartment and drop the mouse T cell proliferation dynamics (Eqs 24-26; Table III). Those extensions are not encoded in the shipped model but the parameter values and rationale are documented below and in the model file so a user can enable them.

Population

The primary fit is to female NSG (NOD-scid IL-2rg-null) mice bearing subcutaneous HCT-116 human colorectal xenografts and engrafted with human peripheral blood mononuclear cells (PBMCs) intraperitoneally 7 days before randomization. Mice were 6-8 weeks old at implantation. The mouse serum PK sub-study used n = 3 mice per timepoint per dose across 0.05 and 0.5 mg/kg IV bolus arms; the tumor growth inhibition (TGI) sub-study used n = 10 mice per dose across 0.01, 0.05, 0.1, 0.15 and 0.5 mg/kg q7d x 2 IV arms (Betts 2019 Methods).

str(mod_meta$population, max.level = 1)
#> List of 9
#>  $ species       : chr "mouse (female NSG NOD-scid IL-2rg-null; HCT-116 colorectal xenograft with human PBMC engraftment)"
#>  $ n_subjects    : chr "PK study n = 3/timepoint/dose; TGI n = 10/dose"
#>  $ n_studies     : num 1
#>  $ age_range     : chr "6-8 weeks at implantation"
#>  $ weight_median : chr "0.025 kg (typical NSG mouse; used as ref for volume/CL scaling)"
#>  $ sex_female_pct: num 100
#>  $ disease_state : chr "Established subcutaneous HCT-116 colorectal xenografts (~0.5 g at randomization for PK; ~7 days established for"| __truncated__
#>  $ dose_range    : chr "IV bolus 0.05 and 0.5 mg/kg (PK study); 0.01-0.5 mg/kg q7d x 2 (T cell engrafted TGI). MW PF-06671008 = 105 kDa."
#>  $ notes         : chr "Primary fit parameters (kmax, kc50, tau, kg0, kg, Mmax; Table II p6-7) are from HCT-116 T cell engrafted model "| __truncated__

Source trace

The per-parameter source-location comment is recorded next to each ini() entry in inst/modeldb/specificDrugs/Betts_2019_pf_06671008_qsp.R. The table below collects the key entries.

Element Value Source location
V1 (central Vd) 49.6 mL/kg Betts 2019 Table II p6: V1 = 49.6 (9% CV)
V2 (peripheral Vd) 60.7 mL/kg Betts 2019 Table II p6: V2 = 60.7 (16% CV)
CL (mouse) 0.45 mL/h/kg Betts 2019 Table II p6: CL = 0.45 (12% CV)
CLd (mouse) 4.95 mL/h/kg Betts 2019 Table II p6: CLd = 4.95 (28% CV)
kon_cd3 1.72 1/nM/h Betts 2019 Table II p6, source ref 7 SPR
koff_cd3 19.66 1/h Betts 2019 Table II p6
kon_pcad 1.57 1/nM/h Betts 2019 Table II p6
koff_pcad 0.74 1/h Betts 2019 Table II p6
P (permeability) 334 um/day Betts 2019 Table II p6, source ref 19
D (diffusivity) 0.022 cm^2/day Betts 2019 Table II p6
epsilon (void frac) 0.24 Betts 2019 Table II p6
Rcap 8 um Betts 2019 Table II p6
Rkrogh 75 um Betts 2019 Table II p6
CD3 receptors / T cell 100000 Betts 2019 Table II p6, source refs 17,18
mPcad (HCT-116) 28706 Betts 2019 Table II p6, source ref 15
Tumor cells / g tumor 1e8 Betts 2019 Table II p6, source ref 21
kint 0.1728 1/day Betts 2019 Table II p6 (96 h half-life)
kg0 (HCT-116 engrafted) 0.30 1/day Betts 2019 Table II p6 col a
kg (HCT-116 engrafted) 105 mm^3/day Betts 2019 Table II p6 col a
Mmax (HCT-116 engrafted) 3800 mm^3 Betts 2019 Table II p6 col a
psi 20 Betts 2019 Table II p6, ref 22 Simeoni
kmax (HCT-116 engrafted) 2.71 1/day Betts 2019 Table II p7 col a (14% CV)
kc50 (HCT-116 engrafted) 2.0e-4 nM Betts 2019 Table II p7 col a (15% CV)
tau (HCT-116 engrafted) 2.25 day Betts 2019 Table II p7 col a (3% CV)
Omega kg0 0.12 Betts 2019 Table II p7 col a (25% CV)
Omega kg 0.16 Betts 2019 Table II p7 col a (28% CV)
Additive serum error 0.067 nM Betts 2019 Table II p6, a = 0.067 (32%)
Prop serum error 0.207 Betts 2019 Table II p6, b = 0.207 (15%)
Additive tumor error 60 mm^3 Betts 2019 Table II p7 col a
Prop tumor error 0.01 Betts 2019 Table II p7 col a (50% CV)
Eq 1 dC1/dt central PK + tumor exchange Betts 2019 p5, Eq 1
Eq 2 dC2/dt peripheral PK Betts 2019 p5, Eq 2
Eq 3 Tumor disposition permeability + diffusion terms Betts 2019 p5, Eq 3
Eq 4 dC3/dt tumor free drug with binding Betts 2019 p5, Eq 4
Eqs 14-16 DCD3t, DPcadt, Trimer mass-action binding in TME Betts 2019 p7
Eqs 17-22 Tumor growth + kill 4-cpt transduction (Simeoni-based) Betts 2019 p7

The paper also fit HCT-116 adoptive transfer (kmax 1.32 /day, kc50 6.9e-5 nM, tau 3.99 day, TSC 0.011 pM) and SUM-149 adoptive transfer (kmax 0.74 /day, kc50 1.0e-4 nM, tau 4.78 day, TSC 0.0092 pM), all from Betts 2019 Table II p7 columns b (see paper for the parameter table); users can swap these values into the model file to simulate those cell lines. Human simulations (Betts 2019 Table III p10) use allometrically scaled PK (CL 4.61 mL/h/kg, V1 40.2 mL/kg, V2 211 mL/kg, CLd 25.2 mL/h/kg) with sPcad = 1.1 nM, kdeg = 0.15 /h and kdegcx = 0.115 /h; those PK parameters and the sPcad binding extension (Eqs 24-26) are not enabled in the mouse-fit shipped model.

Virtual cohort

We simulate 3 dose arms with a small preclinical n per arm, matching the TGI-study design (n = 10 mice/dose). Doses are converted to nmol per mouse using MW = 105 kDa and BW = 0.025 kg.

set.seed(20260711)

mw_kda    <- 105     # MW PF-06671008 (kDa) - Betts 2019 Table II footnote
bw_kg     <- 0.025   # typical NSG mouse body weight
v1_perkg  <- 49.6    # mL/kg, Betts 2019 Table II p6
mice_per_arm <- 10   # Betts 2019 TGI n = 10/dose

# For an IV bolus into the central compartment the model expects the *central-
# compartment concentration equivalent* (C1(0) = dose_nmol / V1_L) as the dose
# amount, because states central/peripheral1/tumor carry concentrations (nM)
# per Betts 2019 Eqs 1-4. Convert mg/kg to nM using MW and V1:
#   dose_nM = (dose_mgkg * BW_kg * 1e6 ng/mg / MW_kda / 1000 ng/nmol) / (V1_perkg * BW_kg / 1000 mL/L)
#           = dose_mgkg * 1e6 / (MW_kda * 1000 * V1_perkg / 1000)
#           = dose_mgkg * 1e6 / (MW_kda * V1_perkg)
mgkg_to_nM <- function(mgkg) mgkg * 1e6 / (mw_kda * v1_perkg)

arms <- tibble::tribble(
  ~arm,           ~dose_mgkg, ~dose_ugkg,
  "0.05 mg/kg",   0.05,       50,
  "0.15 mg/kg",   0.15,       150,
  "0.50 mg/kg",   0.50,       500
) |>
  mutate(dose_nM = mgkg_to_nM(dose_mgkg))

make_cohort <- function(dose_row, id_offset = 0L) {
  # Betts 2019 TGI: q7dx2 IV bolus. Initial tumor ~200 mm^3 (T cell engrafted, ~7 days established).
  # T cell steady-state assumption: TCELLST_TUMOR set to ~5e8 cells/L, matching
  # the paper's mouse Fig 3 baseline before proliferation. Full Prate-driven
  # trajectory (Eqs 9-11) is discussed in "Assumptions and deviations".
  dose_times <- c(0, 7)
  obs_times  <- c(seq(0, 0.5, by = 0.05),                # early PK sampling: hours
                  seq(1,   7, by = 1),
                  seq(7.5, 14, by = 0.5),
                  seq(15,  21, by = 1))
  ids <- id_offset + seq_len(mice_per_arm)
  bind_rows(
    # Dosing rows into central (IV bolus). amt = concentration equivalent (nM);
    # rxode2 adds this to the central state which is a concentration in this model.
    tidyr::crossing(id = ids, time = dose_times) |>
      mutate(evid = 1L, amt = dose_row$dose_nM, cmt = "central"),
    # Observation rows on central (serum) and M1 (tumor growth)
    tidyr::crossing(id = ids, time = obs_times) |>
      mutate(evid = 0L, amt = NA_real_, cmt = "central")
  ) |>
    arrange(id, time, dplyr::desc(evid)) |>
    mutate(
      WT        = bw_kg,
      arm       = dose_row$arm,
      dose_mgkg = dose_row$dose_mgkg,
      dose_ugkg = dose_row$dose_ugkg
    )
}

events <- bind_rows(
  make_cohort(arms[1, ], id_offset =  0L),
  make_cohort(arms[2, ], id_offset = 10L),
  make_cohort(arms[3, ], id_offset = 20L)
)

stopifnot(!anyDuplicated(unique(events[, c("id", "time", "evid")])))

Simulation

mod <- readModelDb("Betts_2019_pf_06671008_qsp")

# Initial tumor volume set to 200 mm^3 (typical established xenograft at
# randomization) by seeding the cycling_cells compartment via inits.
init_tumor <- 200  # mm^3

# Use zeroRe() first so plots reflect typical-value trajectories, then
# separately show variability by omitting zeroRe().
mod_typical <- rxode2::zeroRe(mod)
#> ℹ parameter labels from comments will be replaced by 'label()'

sim_typical <- rxode2::rxSolve(
  mod_typical,
  events    = events,
  keep      = c("arm", "dose_mgkg", "dose_ugkg"),
  inits     = c(cycling_cells = init_tumor)
) |> as.data.frame()
#> ℹ omega/sigma items treated as zero: 'etalkg0', 'etalkg'
#> Warning: multi-subject simulation without without 'omega'

Replicate Figure 2 - serum PK after single IV bolus

Betts 2019 Figure 2a shows serum PK profiles of PF-06671008 in PBMC-engrafted HCT-116 tumor-bearing mice after IV bolus of 0.05 and 0.5 mg/kg. The paper reports dose-proportional AUC between the two dose levels. We simulate all three arms and plot the typical-value serum trajectory over the first 10 days.

sim_typical |>
  filter(time <= 10, time >= 0.01) |>
  ggplot(aes(time, Cc, colour = arm)) +
  geom_line(size = 0.6) +
  scale_y_log10() +
  labs(x = "Time (day)", y = "PF-06671008 serum (nM, log scale)",
       colour = "Dose",
       title = "Simulated mouse serum PK (typical value)",
       caption = "Replicates Figure 2a of Betts 2019 (0.05 and 0.5 mg/kg PBMC engrafted).")
#> Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
#> ℹ Please use `linewidth` instead.
#> This warning is displayed once per session.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
Replicates Figure 2 of Betts 2019 (mouse serum PK).

Replicates Figure 2 of Betts 2019 (mouse serum PK).

Tumor growth inhibition trajectory

The shipped model uses the HCT-116 T cell engrafted parameter set (kmax = 2.71 1/day, kc50 = 2.0e-4 nM, tau = 2.25 day). Total tumor volume w = M1 + M2 + M3 + M4 is the model’s observable tumorVol.

sim_typical |>
  filter(time <= 21) |>
  ggplot(aes(time, tumorVol, colour = arm)) +
  geom_line(size = 0.7) +
  labs(x = "Time (day)", y = "Total tumor volume (mm^3)",
       colour = "Dose",
       title = "Simulated HCT-116 T-cell engrafted tumor kinetics",
       caption = "Q7d x 2 IV bolus PF-06671008 with steady-state Tcellst = 5e8 cells/L; initial tumor 200 mm^3.")
Simulated tumor volume trajectories under q7d x 2 dosing.

Simulated tumor volume trajectories under q7d x 2 dosing.

PKNCA validation on serum PK

We use PKNCA to compute Cmax and AUC0-inf on the first-dose serum trajectory and compare across the three simulated arms.

sim_nca <- sim_typical |>
  filter(!is.na(Cc)) |>
  select(id, time, Cc, arm)

# Ensure a t = 0 row (Cc = 0 pre-dose for IV bolus is unusual, but the model
# solves right after the bolus; add a baseline row to anchor PKNCA).
sim_nca <- bind_rows(
  sim_nca,
  sim_nca |> distinct(id, arm) |> mutate(time = 0, Cc = 0)
) |>
  distinct(id, arm, time, .keep_all = TRUE) |>
  arrange(id, arm, time)

conc_obj <- PKNCA::PKNCAconc(sim_nca, Cc ~ time | arm + id)

dose_df <- events |>
  filter(evid == 1L, time == 0) |>
  select(id, time, amt, arm)

dose_obj <- PKNCA::PKNCAdose(dose_df, amt ~ time | arm + id)

intervals <- data.frame(
  start      = 0,
  end        = 7,
  cmax       = TRUE,
  tmax       = TRUE,
  auclast    = TRUE,
  half.life  = TRUE
)

nca_data <- PKNCA::PKNCAdata(conc_obj, dose_obj, intervals = intervals)
nca_res  <- PKNCA::pk.nca(nca_data)
#> Warning: Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)
#> Too few points for half-life calculation (min.hl.points=3 with only 0 points)

nca_summary <- as.data.frame(nca_res$result) |>
  select(arm, PPTESTCD, PPORRES) |>
  group_by(arm, PPTESTCD) |>
  summarise(value = mean(PPORRES, na.rm = TRUE), .groups = "drop") |>
  tidyr::pivot_wider(names_from = PPTESTCD, values_from = value)

nca_summary |>
  dplyr::rename(
    "Dose arm"           = arm,
    "Cmax (nM)"          = cmax,
    "Tmax (day)"         = tmax,
    "AUC0-7d (nM*day)"   = auclast,
    "t1/2 (day)"         = half.life
  ) |>
  knitr::kable(digits = 3, caption = "Simulated NCA of PF-06671008 serum after single IV bolus.")
Simulated NCA of PF-06671008 serum after single IV bolus.
Dose arm adj.r.squared AUC0-7d (nM*day) clast.pred Cmax (nM) t1/2 (day) lambda.z lambda.z.n.points lambda.z.time.first lambda.z.time.last r.squared span.ratio tlast Tmax (day)
0.05 mg/kg NaN 26.718 NaN 11.694 NaN NaN NaN NaN NaN NaN NaN 7 7
0.15 mg/kg NaN 80.250 NaN 35.110 NaN NaN NaN NaN NaN NaN NaN 7 7
0.50 mg/kg NaN 267.787 NaN 117.133 NaN NaN NaN NaN NaN NaN NaN 7 7

The paper reports dose-proportional serum AUC between the 0.05 and 0.5 mg/kg arms (Betts 2019 Results section “Serum and Tumor PK of PF-06671008 in Mouse”; also Supplemental Fig 1) and a terminal half-life of 3.7-6 days in mouse. The simulated dose-proportional pattern above (10-fold dose ~ 10-fold Cmax and AUC) is consistent with the paper.

Assumptions and deviations

  • T cell kinetics in the tumor (tcellst_tumor). Betts 2019’s mouse fit uses a 5-day lag on plasma-to-tumor T cell migration (Eqs 7-8), followed by piecewise proliferation for 7 days at rate Prate = 0.014/(4 + dose_ugkg) + 1.5e-5 * dose_ugkg (Eq 9; 1/h) and contraction thereafter at k_exhaust = 0.0412 /h (Eq 11). Encoding this full time-varying trajectory as an ODE state in the shipped model would add substantial dose-conditional logic; instead the model uses a fixed scalar tcellst_tumor = 5e8 cells/L (Betts 2019 Fig 3 baseline) that approximates a mid-range tumor T cell density. Users reproducing the paper’s exact TIL dynamics can either edit this scalar per simulation segment or add a time-varying regressor via rxode2::rxSetIni() / event-table modifications.
  • CD3 dimer in plasma (DCD3p) dropped. The paper’s Eq 6 tracks plasma drug-CD3 dimer, but with plasma T cell density ~5e8 cells/L the total plasma CD3 concentration is ~0.1 nM against a CD3 Kd of 11.4 nM; the fraction of drug bound is < 1% and has negligible effect on central PK. The model drops this state; the mouse serum PK Eq 1 is implemented without the DCD3p term.
  • T cell trafficking ODEs (Eqs 7-8) collapsed into TCELLST_TUMOR. As above, T cell plasma and migration ODEs are not maintained; T cell content in the tumor is a user-supplied time-varying covariate.
  • Soluble P-cadherin binding excluded. In the mouse model the paper states “PF-06671008 does not bind to sPcad” (Betts 2019 p3), so the sPcad state and its Eqs 25-26 are only relevant to human simulations and are not encoded in the shipped mouse-fit model.
  • Initial tumor volume. Set to 200 mm^3 (typical established xenograft at randomization) via the inits = c(M1 = init_tumor) argument to rxSolve(). Users can override this to reproduce specific mouse arms.
  • Body weight. WT = 0.025 kg is used as a nominal NSG mouse weight; V1, V2, CL and CLd (reported per kg in Betts 2019 Table II) scale linearly with WT, so passing a different weight into the events data changes the effective volumes and clearances.
  • HCT-116 T cell engrafted parameters are shipped. The paper reports distinct HCT-116 adoptive transfer and SUM-149 adoptive transfer fits (Table II p7 columns b); those parameters are documented in the source- trace table and paper but are not selectable via a model-file switch.
  • Bell-shaped dose-response. At high concentrations the paper predicts a bell-shaped trimer-vs-dose curve (Betts 2019 Fig 1b, Supplementary Fig 4); the shipped model reproduces this behaviour because the trimer state depends non-monotonically on drug concentration through the free- CD3 / free-Pcad terms. Users interested in exploring the bell can simulate a dose escalation and monitor the Trimer state.