Skip to contents

Model and source

#> ℹ parameter labels from comments will be replaced by 'label()'
  • Citation: He Y, Hu J, Zeng X, Yang Q, You Q, Huang J, Zhang Y, Si L, Zhai X. Population pharmacokinetics/pharmacodynamics and safety of YPEG-rhGH in elderly subjects. Front Pharmacol. 2025 Nov 25;16:1651323. doi:10.3389/fphar.2025.1651323. Final-model equations (including the covariate centring values) are given in the Supplementary Material, section ‘PopPK model equations’ / ‘PopPK/PD model equations’.

  • Description: Population PK/PD model for YPEG-rhGH (Y-shape branched 40 kDa PEGylated recombinant human growth hormone, Xiamen Amoytop) in healthy elderly subjects and healthy young adults. The PK is a two-compartment model with first-order subcutaneous absorption and Michaelis-Menten elimination from the central compartment, with all disposition parameters apparent (V1/F, V2/F, CL2/F) because bioavailability was not identifiable. Age (centred at 35.5 years) is a power covariate on Ka, V1/F and Vmax, and body weight (centred at 70 kg) is a power covariate on Vmax. The pharmacodynamic layer is an indirect-response model in which YPEG-rhGH stimulates production of insulin-like growth factor 1 (IGF-1) with a Hill coefficient fixed to 1; the PD state is the IGF-1 concentration expressed as a ratio to each subject’s own baseline, so its typical baseline is Kin/Kout = 1. The PD parameters are the elderly-subject estimates.

  • Article: https://doi.org/10.3389/fphar.2025.1651323

  • Supplementary material (final-model equations, bootstrap tables, study designs): https://www.frontiersin.org/articles/10.3389/fphar.2025.1651323/full#supplementary-material

YPEG-rhGH is a Y-shape branched 40 kDa PEGylated recombinant human growth hormone (Xiamen Amoytop Biotech). He 2025 fitted a two-compartment model with first-order subcutaneous absorption and Michaelis-Menten elimination to the pooled pharmacokinetic data of 16 healthy elderly subjects and 36 healthy young adult men, then fitted an indirect-response (IDR) model in which YPEG-rhGH stimulates production of insulin-like growth factor 1 (IGF-1), expressed as the ratio to each subject’s own baseline.

Population

The analysis pooled two Chinese phase-1 studies (source Table 1, Table 2 and Supplementary Table 3):

  • TB2208GH (elderly) – single-arm, open-label, single-centre. 16 healthy subjects aged 65-74 years (median 66.0, 75% female, median weight 59.0 kg, BMI 18-30 kg/m^2, baseline IGF-1 standard-deviation score < 0). YPEG-rhGH 30 ug/kg subcutaneously every 2 weeks for 23 weeks (12 injections), followed by 5 weeks of observation.
  • TB1010GH (healthy adults) – phase-1, multicentre, randomised. 36 healthy men aged 21-44 years (median 29.0, median weight 66.4 kg). Single subcutaneous doses of 10, 30, 60, 120 or 200 ug/kg after a daily-rhGH comparator phase and a 14-day washout.

813 of 884 pharmacokinetic samples (71 below the quantification limit were excluded) and all 980 IGF-1 measurements entered the analysis. Sex was screened but not retained: the two studies are essentially confounded with sex (all 36 adults were male, 12 of 16 elderly subjects were female), so the authors kept the more direct AGE and WEIGHT covariates instead (Discussion, limitations).

The same information is available programmatically from the model’s population metadata:

str(ui$population)
#> List of 12
#>  $ species       : chr "human"
#>  $ n_subjects    : int 52
#>  $ n_studies     : int 2
#>  $ age_range     : chr "65-74 years (elderly, n = 16); 21-44 years (healthy adults, n = 36)"
#>  $ age_median    : chr "66.0 years (elderly); 29.0 years (healthy adults)"
#>  $ weight_range  : chr "51.1-76.1 kg (elderly); 52.4-79.6 kg (healthy adults)"
#>  $ weight_median : chr "59.0 kg (elderly); 66.4 kg (healthy adults)"
#>  $ sex_female_pct: num 23
#>  $ disease_state : chr "Healthy volunteers. The elderly cohort (study TB2208GH) was screened to a baseline IGF-1 standard-deviation sco"| __truncated__
#>  $ dose_range    : chr "Elderly (TB2208GH): 30 ug/kg SC once every 2 weeks for 23 weeks (12 injections). Healthy adults (TB1010GH): sin"| __truncated__
#>  $ regions       : chr "China"
#>  $ notes         : chr "Baseline demographics are Table 2 of the source paper; the study designs and sampling schedules are Table 1 and"| __truncated__

Source trace

Every ini() entry in inst/modeldb/specificDrugs/He_2025_YPEGrhGH.R carries an in-file comment naming its source location. They are collected here for review. The point estimates are Table 3 (PK) and Table 4 (PD); the covariate centring values and the final PD differential equation are only in the Supplementary Material, under the headings “PopPK model equations” and “PopPK/PD model equations”.

Equation / parameter Value Source location
lka log(0.01086) 1/h Table 3, Ka (RSE 8.49%)
lvc log(2.387) L Table 3, V1/F (RSE 15.54%)
lvp log(21.42) L Table 3, V2/F (RSE 20.48%)
lq log(0.05575) L/h Table 3, CL2/F (RSE 10.15%)
lvmax log(80.13) ug/h Table 3, Vmax (RSE 7.82%)
lkm log(70.08) ug/L Table 3, Km (RSE 13.78%)
e_age_ka -0.6957 Table 3, AGE on Ka (RSE 28.49%)
e_age_vc 1.470 Table 3, AGE on V1/F (RSE 24.69%)
e_age_vmax 0.7989 Table 3, AGE on Vmax (RSE 18.37%)
e_wt_vmax 2.202 Table 3, WEIGHT on Vmax (RSE 18.05%)
AGE centred at 35.5 years n/a Supplement, PopPK model equations: Ka = 0.01086 x (AGE/35.5)^-0.6957
WEIGHT centred at 70 kg n/a Supplement, PopPK model equations: Vmax = 80.13 x (AGE/35.5)^0.7989 x (WEIGHT/70)^2.202
Covariate functional form (power model) n/a Equation 1: theta_i = theta_TV x (Cov_i / Cov_median)^theta_x
lkin log(0.023) /h Table 4, Kin (RSE 14.79%)
lkout log(0.023) 1/h Table 4, Kout (RSE 15.50%)
lemax log(2.245) Table 4, Emax (RSE 21.80%)
lec50 log(50.74) ng/mL Table 4, EC50 (RSE 37.68%)
hill fixed(1) Results 3.3.1: “when gamma was fixed at 1”
d/dt(igf1) n/a Supplement, PopPK/PD model equations: dR/dt = 0.023 x (1 + 2.245 x C/(50.74 + C)) - 0.023 x R
Structure (2-cmt, 1st-order SC absorption, MM elimination, IDR on production) n/a Figure 1 (model framework diagram); Methods 2.4.1
etalka fixed(0.3) Supplementary Table 1, omega(Ka), % = 54.77 with no RSE / no CI in both the final-model and bootstrap columns
etalvc 0.94245 Table 3, IIV V1/F = 97.08% (RSE 24.30%)
etalkm fixed(0.3) Supplementary Table 1, omega(Km), % = 54.77 with no RSE / no CI in both columns
etalkout 0.010547 Table 4, IIV Kout = 10.27% (RSE 3.901%)
etalec50 0.620314 Table 4, IIV EC50 = 78.76% (RSE 30.66%)
propSd 0.2894 Table 3, residual sigma (prop) = 28.94% (RSE 2.88%)
addSd_IGF1ratio 0.1320 Table 4, residual sigma (add) = 13.20 (RSE 6.13%)

Virtual cohort

Original observed data are not publicly available. The cohorts below match the covariate distributions of source Table 2 (means and SDs, truncated at the reported minima and maxima).

# set.seed() seeds R's RNG, which drives the covariate draws below. It does NOT
# seed rxode2's simulation RNG, and rxode2's streams are partitioned per solver
# thread -- a 2-thread CI runner draws a different eta cohort than a 16-thread
# workstation. Every assertion downstream is therefore written to hold for any
# cohort the model can produce.
set.seed(20251125)

rtrunc_norm <- function(n, mean, sd, lo, hi) {
  pmin(pmax(stats::rnorm(n, mean, sd), lo), hi)
}

n_elderly <- 100L
n_adult   <- 60L

# Elderly (TB2208GH), Table 2: AGE 67.44 (3.01) [65.00, 74.00];
# WT 60.54 (7.31) [51.10, 76.10].
elderly_subj <- tibble(
  id  = seq_len(n_elderly),
  AGE = rtrunc_norm(n_elderly, 67.44, 3.01, 65.00, 74.00),
  WT  = rtrunc_norm(n_elderly, 60.54, 7.31, 51.10, 76.10)
)

# Healthy adults (TB1010GH), Table 2: AGE 30.78 (6.62) [21.00, 44.00];
# WT 65.08 (6.16) [52.40, 79.60].
make_adult_subj <- function(n, id_offset) {
  tibble(
    id  = id_offset + seq_len(n),
    AGE = rtrunc_norm(n, 30.78, 6.62, 21.00, 44.00),
    WT  = rtrunc_norm(n, 65.08, 6.16, 52.40, 79.60)
  )
}

The event tables are built explicitly (one row per dose, one row per observation). Observation rows point at the ODE state central and carry dvid = 1 so that rxode2 maps them onto the Cc endpoint; dvid = 2 rows map onto the IGF1ratio endpoint. Both algebraic observables are returned as columns on every output row, so the PD time course can be read off the dvid = 1 rows too.

# The elderly trial's actual PK sampling grid, relative to a dose
# (Supplementary Table 3).
sparse_grid <- c(0, 8, 24, 72, 96, 120, 168, 336)

make_events <- function(subj, dose_amt, ii, addl, obs_times, dvid = 1L,
                        cmt_obs = "central") {
  doses <- subj |>
    tidyr::crossing(dose_index = seq_len(addl + 1L)) |>
    mutate(
      time = (dose_index - 1L) * ii,
      amt  = dose_amt(WT),
      evid = 1L,
      cmt  = "depot",
      dvid = NA_integer_
    ) |>
    select(-dose_index)
  obs <- subj |>
    tidyr::crossing(time = obs_times) |>
    mutate(amt = NA_real_, evid = 0L, cmt = cmt_obs, dvid = dvid)
  bind_rows(doses, obs) |>
    arrange(id, time, desc(evid))
}

# 1. Elderly, 30 ug/kg Q2W x 12 (23 weeks). Dense sampling in the first and
#    seventh dosing intervals (the two the paper compares), plus the trial's
#    own sparse grid over every interval.
ii_q2w   <- 336
elderly_obs <- sort(unique(c(
  seq(0, 336, by = 6),                       # dose 1 interval, dense
  2016 + seq(0, 336, by = 6),                # dose 7 interval, dense
  rep(seq(0, 11) * ii_q2w, each = length(sparse_grid)) + sparse_grid
)))
ev_elderly <- make_events(
  elderly_subj, dose_amt = function(WT) 30 * WT,
  ii = ii_q2w, addl = 11L, obs_times = elderly_obs
) |>
  mutate(cohort = "Elderly 30 ug/kg Q2W")

# 2. Healthy adults, single doses of 10 / 30 / 60 / 120 / 200 ug/kg.
adult_doses <- c(10, 30, 60, 120, 200)
ev_adult <- bind_rows(lapply(seq_along(adult_doses), function(i) {
  d <- adult_doses[i]
  make_events(
    make_adult_subj(n_adult, id_offset = 1000L + (i - 1L) * n_adult),
    dose_amt = function(WT) d * WT, ii = 0, addl = 0L,
    obs_times = sort(unique(c(seq(0, 336, by = 4), sparse_grid)))
  ) |>
    mutate(cohort = sprintf("Adult %d ug/kg SD", d))
}))

events_pk <- bind_rows(ev_elderly, ev_adult)
stopifnot(!anyDuplicated(unique(events_pk[, c("id", "time", "evid", "dvid")])))

Simulation

mod <- readModelDb("He_2025_YPEGrhGH")

# useLinCmt = FALSE: rxode2's automatic ODE -> linCmt conversion corrupts the
# dvid -> cmt mapping for multi-endpoint models (see the skill's
# known-vignette-failure-patterns.md pattern 5b).
sim_pk <- rxode2::rxSolve(
  mod, events = events_pk, keep = c("cohort", "AGE", "WT"),
  useLinCmt = FALSE
) |>
  as.data.frame()
#> ℹ parameter labels from comments will be replaced by 'label()'

stopifnot(nrow(sim_pk) > 0, !anyNA(sim_pk$Cc))

For deterministic replication of the published typical-value simulations, the random effects are zeroed:

mod_typical <- rxode2::zeroRe(mod)
#> ℹ parameter labels from comments will be replaced by 'label()'

# Argument names deliberately differ from the covariate column names so that
# the mutate() below cannot pick up a column instead of the argument.
solve_typical <- function(dose_ug, age_yr, wt_kg, obs_times, ii = 0,
                          addl = 0L) {
  obs <- tibble(
    id = 1L, time = obs_times, amt = NA_real_, evid = 0L, cmt = "central",
    dvid = 1L
  )
  ev <- if (dose_ug > 0) {
    doses <- tibble(
      id = 1L, time = (seq_len(addl + 1L) - 1L) * ii, amt = dose_ug,
      evid = 1L, cmt = "depot", dvid = NA_integer_
    )
    bind_rows(doses, obs)
  } else {
    obs
  }
  ev <- ev |>
    mutate(AGE = age_yr, WT = wt_kg) |>
    arrange(time, desc(evid))
  rxode2::rxSolve(mod_typical, ev, useLinCmt = FALSE) |> as.data.frame()
}

The IGF-1 state starts at its baseline ratio of 1

The PD state is IGF-1 expressed as a ratio to each subject’s own baseline, so the model’s typical baseline must be exactly Kin / Kout = 0.023 / 0.023 = 1. This is a deterministic identity, not a cohort statistic, so it is asserted exactly.

base_chk <- solve_typical(0, age_yr = 67.44, wt_kg = 60.54,
                          obs_times = c(0, 24, 336))
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
stopifnot(
  # No dose -> the IDR sits at its baseline for the whole horizon.
  max(abs(base_chk$IGF1ratio - 1)) < 1e-8
)
base_chk$IGF1ratio
#> [1] 1 1 1

Replicate published figures

Figure 2C – elderly versus healthy-adult concentration-time profiles

sim_pk |>
  filter(time <= 336, cohort %in% c("Elderly 30 ug/kg Q2W", "Adult 30 ug/kg SD",
                                    "Adult 10 ug/kg SD")) |>
  group_by(cohort, time) |>
  summarise(
    Q10 = quantile(Cc, 0.10), Q50 = median(Cc), Q90 = quantile(Cc, 0.90),
    .groups = "drop"
  ) |>
  filter(time > 0) |>
  ggplot(aes(time, Q50, colour = cohort, fill = cohort)) +
  geom_ribbon(aes(ymin = Q10, ymax = Q90), alpha = 0.15, colour = NA) +
  geom_line(linewidth = 0.8) +
  scale_y_log10() +
  labs(
    x = "Time after first dose (h)", y = "YPEG-rhGH (ng/mL)",
    colour = NULL, fill = NULL,
    title = "Figure 2C -- elderly 30 ug/kg vs healthy adults",
    caption = paste(
      "Replicates Figure 2C of He 2025: elderly exposure at 30 ug/kg falls",
      "close to the healthy-adult 10 ug/kg profile."
    )
  ) +
  theme(legend.position = "bottom")

Figure 2D – Cmax and AUC across the healthy-adult dose range

Michaelis-Menten elimination makes exposure rise faster than dose. This is a typical-value (deterministic) comparison, so it is asserted tightly.

dose_prop <- bind_rows(lapply(adult_doses, function(d) {
  s <- solve_typical(d * 65.08, age_yr = 30.78, wt_kg = 65.08,
                     obs_times = seq(0, 336, by = 2))
  tibble(
    dose_ug_kg = d,
    cmax = max(s$Cc),
    tmax = s$time[which.max(s$Cc)],
    auclast = sum(diff(s$time) * (head(s$Cc, -1) + tail(s$Cc, -1)) / 2)
  )
}))
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'

knitr::kable(
  dose_prop |>
    mutate(
      dose_norm_cmax = cmax / cmax[dose_ug_kg == 10] / (dose_ug_kg / 10),
      dose_norm_auc  = auclast / auclast[dose_ug_kg == 10] / (dose_ug_kg / 10)
    ) |>
    rename(
      "Dose (ug/kg)"        = dose_ug_kg,
      "Cmax (ng/mL)"        = cmax,
      "Tmax (h)"            = tmax,
      "AUC0-336 (ng*h/mL)"  = auclast,
      "Cmax / dose vs 10 ug/kg" = dose_norm_cmax,
      "AUC / dose vs 10 ug/kg"  = dose_norm_auc
    ),
  digits = 2,
  caption = paste(
    "Typical-value exposure across the healthy-adult dose range. Replicates",
    "the dose-nonlinearity shown in Figure 2D of He 2025."
  )
)
Typical-value exposure across the healthy-adult dose range. Replicates the dose-nonlinearity shown in Figure 2D of He 2025.
Dose (ug/kg) Cmax (ng/mL) Tmax (h) AUC0-336 (ng*h/mL) Cmax / dose vs 10 ug/kg AUC / dose vs 10 ug/kg
10 8.43 8 753.72 1.00 1.00
30 31.13 12 2590.81 1.23 1.15
60 87.35 16 6669.10 1.73 1.47
120 318.96 28 24714.46 3.15 2.73
200 813.35 38 76635.99 4.82 5.08

stopifnot(
  # Deterministic: a 20-fold dose increase must give a MORE than 20-fold AUC
  # increase under Michaelis-Menten elimination (Results 3.2.1 / Discussion:
  # "nonlinear elimination characteristics ... in the dose range 10-200 ug/kg").
  dose_prop$auclast[dose_prop$dose_ug_kg == 200] /
    dose_prop$auclast[dose_prop$dose_ug_kg == 10] > 20,
  # Monotone in dose (deterministic, no cohort noise).
  all(diff(dose_prop$cmax) > 0),
  all(diff(dose_prop$auclast) > 0)
)

Figures 9C and 9D – covariate gradients in the IGF-1 baseline ratio

Figure 9C simulates a 3 mg flat dose Q2W for 52 weeks in 70-year-old elderly subjects weighing 50, 60, 70 and 80 kg; Figure 9D holds weight at 60 kg and varies age over 60-80 years. Emax,72h is the IGF-1 baseline ratio 72 h after the last dose and Etrough is the ratio immediately before the next dose would have been due. Both panels are typical-value simulations, so the gradients below are deterministic.

# 26 Q2W doses = 52 weeks; the last dose is at 25 * 336 = 8400 h.
last_dose_time <- 25 * ii_q2w
pd_times <- c(last_dose_time + 72, last_dose_time + ii_q2w)

pd_scenario <- function(age_yr, wt_kg, dose_ug = 3000) {
  s <- solve_typical(dose_ug, age_yr = age_yr, wt_kg = wt_kg,
                     obs_times = pd_times, ii = ii_q2w, addl = 25L)
  tibble(
    AGE = age_yr, WT = wt_kg, dose_mg = dose_ug / 1000,
    Emax_72h = s$IGF1ratio[s$time == pd_times[1]],
    Etrough  = s$IGF1ratio[s$time == pd_times[2]]
  )
}

fig9c <- bind_rows(lapply(c(50, 60, 70, 80), function(w) {
  pd_scenario(age_yr = 70, wt_kg = w)
}))
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
fig9d <- bind_rows(lapply(c(60, 65, 70, 75, 80), function(a) {
  pd_scenario(age_yr = a, wt_kg = 60)
}))
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'

bind_rows(
  fig9c |> mutate(Panel = "9C: age 70 y, weight varied"),
  fig9d |> mutate(Panel = "9D: weight 60 kg, age varied")
) |>
  select(Panel, AGE, WT, Emax_72h, Etrough) |>
  rename(
    "Panel"          = Panel,
    "Age (years)"    = AGE,
    "Weight (kg)"    = WT,
    "Emax,72h"       = Emax_72h,
    "Etrough"        = Etrough
  ) |>
  knitr::kable(
    digits = 3,
    caption = paste(
      "Replicates Figures 9C and 9D of He 2025 (3 mg Q2W, 52 weeks,",
      "typical-value simulation)."
    )
  )
Replicates Figures 9C and 9D of He 2025 (3 mg Q2W, 52 weeks, typical-value simulation).
Panel Age (years) Weight (kg) Emax,72h Etrough
9C: age 70 y, weight varied 70 50 1.591 1.176
9C: age 70 y, weight varied 70 60 1.415 1.113
9C: age 70 y, weight varied 70 70 1.302 1.078
9C: age 70 y, weight varied 70 80 1.229 1.057
9D: weight 60 kg, age varied 60 60 1.482 1.115
9D: weight 60 kg, age varied 65 60 1.446 1.114
9D: weight 60 kg, age varied 70 60 1.415 1.113
9D: weight 60 kg, age varied 75 60 1.387 1.111
9D: weight 60 kg, age varied 80 60 1.363 1.110

wt_spread  <- fig9c$Emax_72h[fig9c$WT == 50] - fig9c$Emax_72h[fig9c$WT == 80]
age_spread <- fig9d$Emax_72h[fig9d$AGE == 60] - fig9d$Emax_72h[fig9d$AGE == 80]

stopifnot(
  # Results 3.5: "a significant decrease in the ratio of IGF-1 relative to
  # baseline with increasing body weight". Deterministic typical values, so
  # step-by-step monotonicity is a legitimate assertion here.
  all(diff(fig9c$Emax_72h) < 0),
  all(diff(fig9c$Etrough) < 0),
  # Results 3.5 / Discussion: the age effect is in the same direction but
  # "less" pronounced than the weight effect.
  all(diff(fig9d$Emax_72h) < 0),
  age_spread < wt_spread
)
c(weight_spread = wt_spread, age_spread = age_spread)
#> weight_spread    age_spread 
#>     0.3627320     0.1191525

Conclusion – IGF-1 baseline ratio across the 1.5-5 mg flat-dose range

The Conclusion states that “the baseline ratio of administration could rise to the range of 1.14-1.43 after a 1.5-5 mg dose of 24W/52W given at Q2W in the elderly, with the 3 mg dose elevating the ratio by approximately 1 SD”. Supplementary Table 6 gives the elderly baseline IGF-1 as mean 93.8 ng/mL with SD 17.4 ng/mL, so a 1 SD elevation corresponds to a baseline ratio of 1.186.

sd_ratio <- 1 + 17.4 / 93.8   # Supplementary Table 6: mean 93.8, SD 17.4

flat_dose <- bind_rows(lapply(c(1.5, 3, 5), function(d) {
  pd_scenario(age_yr = 70, wt_kg = 60, dose_ug = d * 1000)
})) |>
  mutate(
    # Mean ratio over the final dosing interval, the quantity the paper's
    # "elevating the ratio by approximately 1 SD" statement is closest to.
    Emean = vapply(dose_mg, function(d) {
      s <- solve_typical(d * 1000, age_yr = 70, wt_kg = 60,
                         obs_times = last_dose_time + seq(0, ii_q2w, by = 6),
                         ii = ii_q2w, addl = 25L)
      mean(s$IGF1ratio)
    }, numeric(1))
  )
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'
#> ℹ omega/sigma items treated as zero: 'etalka', 'etalvc', 'etalkm', 'etalkout', 'etalec50'

flat_dose |>
  select(dose_mg, Emax_72h, Emean, Etrough) |>
  rename(
    "Dose (mg Q2W)"                   = dose_mg,
    "Emax,72h"                        = Emax_72h,
    "Mean over final interval"        = Emean,
    "Etrough"                         = Etrough
  ) |>
  knitr::kable(
    digits = 3,
    caption = paste(
      "IGF-1 baseline ratio over the final dosing interval after 52 weeks of",
      "Q2W dosing (typical 70-year-old, 60 kg). He 2025 Conclusion reports a",
      "range of 1.14-1.43 across 1.5-5 mg, with 3 mg ~ 1 SD =",
      sprintf("%.3f.", sd_ratio)
    )
  )
IGF-1 baseline ratio over the final dosing interval after 52 weeks of Q2W dosing (typical 70-year-old, 60 kg). He 2025 Conclusion reports a range of 1.14-1.43 across 1.5-5 mg, with 3 mg ~ 1 SD = 1.186.
Dose (mg Q2W) Emax,72h Mean over final interval Etrough
1.5 1.214 1.136 1.056
3.0 1.415 1.267 1.113
5.0 1.663 1.433 1.188

stopifnot(
  # Deterministic and monotone in dose.
  all(diff(flat_dose$Emax_72h) > 0),
  all(diff(flat_dose$Etrough) > 0),
  # The published 1.14-1.43 range must sit between the model's trough and peak
  # envelopes across 1.5-5 mg (see Assumptions and deviations: the paper does
  # not say which of Emax,72h / Etrough / mean the range refers to).
  min(flat_dose$Etrough) < 1.14,
  max(flat_dose$Emax_72h) > 1.43,
  # The 3 mg interval mean should land near the 1 SD reference. Widened to
  # +/-0.10 around sd_ratio: this is deterministic, but the published claim is
  # qualitative ("approximately 1 SD").
  abs(flat_dose$Emean[flat_dose$dose_mg == 3] - sd_ratio) < 0.10
)

Figure 8 – IGF-1 baseline-ratio time course in the elderly cohort

pd_obs <- seq(0, 4 * ii_q2w, by = 12)
ev_pd <- bind_rows(
  make_events(elderly_subj, dose_amt = function(WT) 30 * WT, ii = ii_q2w,
              addl = 3L, obs_times = pd_obs, dvid = 2L, cmt_obs = "igf1")
) |>
  mutate(cohort = "Elderly 30 ug/kg Q2W")

sim_pd <- rxode2::rxSolve(mod, events = ev_pd, keep = c("cohort"),
                          useLinCmt = FALSE) |>
  as.data.frame()

sim_pd |>
  group_by(time) |>
  summarise(
    Q10 = quantile(IGF1ratio, 0.10), Q50 = median(IGF1ratio),
    Q90 = quantile(IGF1ratio, 0.90), .groups = "drop"
  ) |>
  ggplot(aes(time / 24, Q50)) +
  geom_ribbon(aes(ymin = Q10, ymax = Q90), alpha = 0.2) +
  geom_line(linewidth = 0.8) +
  geom_hline(yintercept = 1, linetype = "dashed") +
  labs(
    x = "Time (days)", y = "IGF-1 / baseline",
    title = "Figure 8 -- IGF-1 relative to baseline, elderly 30 ug/kg Q2W",
    caption = paste(
      "Replicates the IDR visual predictive check of Figure 8 of He 2025",
      "(median and 10th-90th percentile band of the simulated cohort)."
    )
  )


# The IDR is a stimulation of production, so no subject's ratio ever falls
# below ITS OWN baseline of Kin / Kout_i (the eta on Kout makes that baseline
# subject-specific, so the reference is each subject's t = 0 value rather than
# the typical value of 1). This is a deterministic property of the structure
# and is checked on the residual-error-free IPRED.
pd_baselines <- sim_pd |>
  filter(time == 0) |>
  select(id, baseline = IGF1ratio)
pd_min <- sim_pd |>
  group_by(id) |>
  summarise(min_ratio = min(IGF1ratio), .groups = "drop") |>
  left_join(pd_baselines, by = "id")

# Median profile across the cohort, used for the TEmax-lags-Tmax check below.
pd_median <- tapply(sim_pd$IGF1ratio, sim_pd$time, median)
pd_temax <- as.numeric(names(pd_median)[which.max(pd_median)])

stopifnot(
  nrow(pd_min) == n_elderly,
  all(pd_min$min_ratio >= pd_min$baseline - 1e-8),
  # Results 3.3 / Discussion: "TEmax ... was significantly longer than the Tmax
  # of YPEG-rhGH", with a median TEmax of 72-96 h. Asserted only as "later than
  # one day", which holds for any cohort the model can draw.
  pd_temax > 24
)
pd_temax
#> [1] 1080

PKNCA validation

Healthy-adult single-dose NCA by dose group

adult_nca_conc <- sim_pk |>
  filter(grepl("^Adult", cohort), !is.na(Cc)) |>
  select(id, time, Cc, cohort)

# Guarantee a time = 0 record per subject (extravascular pre-dose Cc = 0).
adult_nca_conc <- bind_rows(
  adult_nca_conc,
  adult_nca_conc |> distinct(id, cohort) |> mutate(time = 0, Cc = 0)
) |>
  distinct(id, cohort, time, .keep_all = TRUE) |>
  arrange(id, time)

adult_conc_obj <- PKNCA::PKNCAconc(adult_nca_conc, Cc ~ time | cohort + id)

adult_dose_df <- ev_adult |>
  filter(evid == 1) |>
  select(id, time, amt, cohort)
adult_dose_obj <- PKNCA::PKNCAdose(adult_dose_df, amt ~ time | cohort + id)

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

adult_nca <- PKNCA::pk.nca(
  PKNCA::PKNCAdata(adult_conc_obj, adult_dose_obj, intervals = adult_intervals)
)

adult_summary <- as.data.frame(adult_nca) |>
  filter(PPTESTCD %in% c("cmax", "tmax", "auclast", "half.life")) |>
  group_by(cohort, PPTESTCD) |>
  summarise(median = median(PPORRES, na.rm = TRUE), .groups = "drop") |>
  pivot_wider(names_from = PPTESTCD, values_from = median)

adult_summary |>
  rename(
    "Dose group"          = cohort,
    "Cmax (ng/mL)"        = cmax,
    "Tmax (h)"            = tmax,
    "AUC0-336 (ng*h/mL)"  = auclast,
    "t1/2 (h)"            = half.life
  ) |>
  knitr::kable(
    digits = 2,
    caption = "PKNCA summary of the simulated healthy-adult single-dose cohorts."
  )
PKNCA summary of the simulated healthy-adult single-dose cohorts.
Dose group AUC0-336 (ng*h/mL) Cmax (ng/mL) t1/2 (h) Tmax (h)
Adult 10 ug/kg SD 840.73 8.28 103.12 8
Adult 120 ug/kg SD 27568.27 282.62 111.89 32
Adult 200 ug/kg SD 79472.43 755.73 124.20 32
Adult 30 ug/kg SD 2500.42 31.74 105.37 12
Adult 60 ug/kg SD 5527.25 71.51 93.89 16

Elderly accumulation between the first and seventh dosing intervals

The paper reports accumulation ratios of 1.34 (Cmax), 1.35 (Cmin) and 1.23 (AUC) for the elderly cohort, and states that steady state was reached by week 13. The seventh injection (week 12, t = 2016 h) is the interval the paper’s week-13 sampling covers.

elderly_nca_conc <- sim_pk |>
  filter(cohort == "Elderly 30 ug/kg Q2W", !is.na(Cc)) |>
  select(id, time, Cc, cohort)
elderly_nca_conc <- bind_rows(
  elderly_nca_conc,
  elderly_nca_conc |> distinct(id, cohort) |> mutate(time = 0, Cc = 0)
) |>
  distinct(id, cohort, time, .keep_all = TRUE) |>
  arrange(id, time)

elderly_conc_obj <- PKNCA::PKNCAconc(elderly_nca_conc, Cc ~ time | cohort + id)
elderly_dose_obj <- PKNCA::PKNCAdose(
  ev_elderly |> filter(evid == 1) |> select(id, time, amt, cohort),
  amt ~ time | cohort + id
)

# clast.obs, not cmin: PKNCA's cmin over an interval is the minimum anywhere in
# it, which for a Q2W interval is the trough at its START. The trough the paper
# reports is the concentration at the END of the interval.
elderly_intervals <- data.frame(
  start = c(0, 2016), end = c(336, 2352),
  cmax = TRUE, tmax = TRUE, auclast = TRUE, clast.obs = TRUE
)

elderly_nca <- PKNCA::pk.nca(
  PKNCA::PKNCAdata(elderly_conc_obj, elderly_dose_obj,
                   intervals = elderly_intervals)
)

elderly_res <- as.data.frame(elderly_nca) |>
  filter(PPTESTCD %in% c("cmax", "tmax", "auclast", "clast.obs")) |>
  mutate(interval = ifelse(start == 0, "dose1", "dose7")) |>
  select(id, PPTESTCD, interval, PPORRES) |>
  pivot_wider(names_from = interval, values_from = PPORRES) |>
  mutate(ratio = dose7 / dose1)

accum <- elderly_res |>
  filter(PPTESTCD %in% c("cmax", "clast.obs", "auclast")) |>
  group_by(PPTESTCD) |>
  summarise(median_ratio = median(ratio, na.rm = TRUE), .groups = "drop") |>
  mutate(
    published = c(auclast = 1.23, clast.obs = 1.35, cmax = 1.34)[PPTESTCD],
    pct_diff = 100 * (median_ratio - published) / published
  )

accum |>
  rename(
    "NCA parameter"           = PPTESTCD,
    "Simulated dose7 / dose1" = median_ratio,
    "He 2025 reported"        = published,
    "Difference (%)"          = pct_diff
  ) |>
  knitr::kable(
    digits = 3,
    caption = paste(
      "Accumulation from the first to the seventh Q2W dosing interval,",
      "elderly cohort. He 2025 Results 3.2 reports RCmax 1.34, RCmin 1.35",
      "and RAUC 1.23."
    )
  )
Accumulation from the first to the seventh Q2W dosing interval, elderly cohort. He 2025 Results 3.2 reports RCmax 1.34, RCmin 1.35 and RAUC 1.23.
NCA parameter Simulated dose7 / dose1 He 2025 reported Difference (%)
auclast 1.190 1.23 -3.284
clast.obs 1.266 1.35 -6.205
cmax 1.183 1.34 -11.687

stopifnot(
  # Results 3.2: "a moderate increase in serum exposure ... but no significant
  # drug accumulation". The bound admits any cohort the model can draw while
  # still breaking on a mis-transcribed dosing interval or clearance term
  # (which would move accumulation well outside 1.0-1.7).
  all(accum$median_ratio > 1.0),
  all(accum$median_ratio < 1.7)
)

Comparison against published NCA

The paper reports no absolute NCA table; the only NCA values it publishes are the elderly median Tmax after the first dose (46.16 h) and after later doses (22.71 h). Those were measured on the trial’s own sparse sampling grid (0, 8, 24, 72, 96, 120, 168 and 336 h after a dose, Supplementary Table 3), on which an observed Tmax can only land on one of eight times. The simulated Tmax below is therefore computed on that same grid.

sparse_nca_conc <- sim_pk |>
  filter(
    cohort == "Elderly 30 ug/kg Q2W",
    time %in% c(sparse_grid, 2016 + sparse_grid)
  ) |>
  mutate(
    treatment = ifelse(time <= 336, "Elderly, dose 1", "Elderly, dose 7")
  ) |>
  select(id, time, Cc, treatment)

sparse_conc_obj <- PKNCA::PKNCAconc(sparse_nca_conc, Cc ~ time | treatment + id)
sparse_dose_obj <- PKNCA::PKNCAdose(
  ev_elderly |>
    filter(evid == 1, time %in% c(0, 2016)) |>
    mutate(treatment = ifelse(time == 0, "Elderly, dose 1",
                              "Elderly, dose 7")) |>
    select(id, time, amt, treatment),
  amt ~ time | treatment + id
)

sparse_nca <- PKNCA::pk.nca(PKNCA::PKNCAdata(
  sparse_conc_obj, sparse_dose_obj,
  intervals = data.frame(
    start = c(0, 2016), end = c(336, 2352), cmax = TRUE, tmax = TRUE
  )
))
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=2016, end=2352: No
#> data for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval
#> Warning in .f(data_conc = .l[[1L]][[i]], data_dose = .l[[2L]][[i]],
#> data_intervals = .l[[3L]][[i]], : Error with interval start=0, end=336: No data
#> for interval

published <- tibble::tribble(
  ~treatment,        ~tmax,
  "Elderly, dose 1", 46.16,
  "Elderly, dose 7", 22.71
)

cmp <- nlmixr2lib::ncaComparisonTable(
  simulated = sparse_nca,
  reference = published,
  by        = "treatment",
  params    = "tmax",
  units     = c(tmax = "h"),
  tolerance_pct = 20
)

knitr::kable(
  cmp,
  caption = paste(
    "Simulated vs published median Tmax on the trial's own sampling grid.",
    "* differs from the reference by more than 20%."
  )
)
Simulated vs published median Tmax on the trial’s own sampling grid. * differs from the reference by more than 20%.
NCA parameter treatment Reference Simulated % diff
Tmax (h) Elderly, dose 1 46.2 24 -48.0%*
Tmax (h) Elderly, dose 7 22.7 24 +5.7%

The dose-1 row is starred: the model’s typical absorption (Ka = 0.01086 * (AGE/35.5)^-0.6957 = 0.00695 /h at the cohort’s mean age) peaks earlier than the observed median. This is recorded as a deviation below rather than tuned away.

The underlying finding the paper draws from that Tmax – “the Tmax remained slower compared to younger subjects”, “markedly reduced absorption in the elderly population” – is reproduced, and is checked directly on the individual Ka values rather than on a Tmax read off two different sampling grids:

ka_by_cohort <- sim_pk |>
  filter(cohort %in% c("Elderly 30 ug/kg Q2W", "Adult 30 ug/kg SD")) |>
  group_by(cohort, id) |>
  summarise(ka = first(ka), .groups = "drop") |>
  group_by(cohort) |>
  summarise(
    median_ka = median(ka),
    median_abs_half_life = log(2) / median(ka),
    .groups = "drop"
  )

ka_by_cohort |>
  rename(
    "Cohort"                       = cohort,
    "Median Ka (1/h)"              = median_ka,
    "Absorption half-life (h)"     = median_abs_half_life
  ) |>
  knitr::kable(
    digits = c(0, 5, 1),
    caption = paste(
      "Individual absorption rate constants by cohort. The AGE exponent on Ka",
      "is negative (-0.6957), so the elderly absorb more slowly."
    )
  )
Individual absorption rate constants by cohort. The AGE exponent on Ka is negative (-0.6957), so the elderly absorb more slowly.
Cohort Median Ka (1/h) Absorption half-life (h)
Adult 30 ug/kg SD 0.01239 55.9
Elderly 30 ug/kg Q2W 0.00631 109.9

ka_elderly <- ka_by_cohort$median_ka[
  ka_by_cohort$cohort == "Elderly 30 ug/kg Q2W"
]
ka_adult <- ka_by_cohort$median_ka[
  ka_by_cohort$cohort == "Adult 30 ug/kg SD"
]
stopifnot(
  # Ka has no eta beyond the fixed omega and the age ranges of the two cohorts
  # do not overlap, so this is a large, robust effect (roughly 1.7-fold) rather
  # than a race between two noisy statistics.
  ka_elderly < 0.8 * ka_adult
)

Elderly versus healthy-adult exposure at the same 30 ug/kg dose

Results 3.2 reports that elderly Cmax and AUC0-last at 30 ug/kg reached only 7.75% and 19.13% of the healthy-adult values at the same dose. The model reproduces the direction and a substantial magnitude, but not the full size of the observed gap.

exposure <- as.data.frame(adult_nca) |>
  filter(cohort == "Adult 30 ug/kg SD", PPTESTCD %in% c("cmax", "auclast")) |>
  group_by(PPTESTCD) |>
  summarise(adult = median(PPORRES, na.rm = TRUE), .groups = "drop") |>
  left_join(
    elderly_res |>
      filter(PPTESTCD %in% c("cmax", "auclast")) |>
      group_by(PPTESTCD) |>
      summarise(elderly = median(dose1, na.rm = TRUE), .groups = "drop"),
    by = "PPTESTCD"
  ) |>
  mutate(
    pct_of_adult = 100 * elderly / adult,
    published_pct = c(auclast = 19.13, cmax = 7.75)[PPTESTCD]
  )

exposure |>
  rename(
    "NCA parameter"          = PPTESTCD,
    "Adult 30 ug/kg (median)" = adult,
    "Elderly 30 ug/kg (median)" = elderly,
    "Elderly as % of adult"  = pct_of_adult,
    "He 2025 observed %"     = published_pct
  ) |>
  knitr::kable(
    digits = 2,
    caption = paste(
      "Elderly exposure as a percentage of healthy-adult exposure at the same",
      "30 ug/kg dose. He 2025 Results 3.2 reports 7.75% (Cmax) and 19.13%",
      "(AUC0-last) from observed data."
    )
  )
Elderly exposure as a percentage of healthy-adult exposure at the same 30 ug/kg dose. He 2025 Results 3.2 reports 7.75% (Cmax) and 19.13% (AUC0-last) from observed data.
NCA parameter Adult 30 ug/kg (median) Elderly 30 ug/kg (median) Elderly as % of adult He 2025 observed %
auclast 2500.42 1160.32 46.40 19.13
cmax 31.74 7.44 23.44 7.75

stopifnot(
  # DIRECTIONAL gate only -- the magnitude is a documented deviation (below).
  # Elderly exposure must be well under two thirds of the adult value at the
  # same dose; the AGE effects on Ka, V1/F and Vmax make this a large, robust
  # effect. Realised 23.4% (Cmax) and 46.4% (AUC) at 16 threads. Dropping the
  # AGE terms would push both toward 100%, so 65 still goes red.
  all(exposure$pct_of_adult < 65)
)

Assumptions and deviations

  • Covariate centring values come from the supplement, not the main text. Equation 1 defines the covariate model as theta_i = theta_TV x (Cov_i / Cov_median)^theta_x but never prints Cov_median. The supplement’s “PopPK model equations” section gives the final equations with the centring values substituted (AGE / 35.5 and WEIGHT / 70), and those are what the model file uses. 35.5 years is not tabulated anywhere as a summary statistic; it is consistent with the median age of the pooled 52-subject dataset (36 adults aged 21-44 plus 16 elderly aged 65-74, Table 2). 70 kg is a rounded reference, not the dataset median (59.0 kg elderly / 66.4 kg adults).
  • omega(Ka) and omega(Km) are encoded as fixed. Supplementary Table 1 reports both as exactly 54.77% with no RSE and no confidence interval in the final-model column and in the 1,000-replicate bootstrap column. A parameter estimated freely cannot reproduce to four significant figures across 1,000 bootstrap refits, so both were held constant. 0.5477^2 = 0.300, a round variance consistent with a held initial estimate; they are encoded as fixed(0.3).
  • The IIV percentages are read as omega x 100, not as sqrt(exp(omega^2) - 1) x 100. The 0.5477 -> 0.300 coincidence above only holds under the first convention. The paper describes the retained PD random effects as “additive random effects for EC50 and Kout”; taken literally with an additive eta, omega(EC50) = 78.76 would be an SD of 0.79 on an EC50 of 50.74 ng/mL, i.e. essentially no variability, which is irreconcilable with the reported 9.97% shrinkage and 30.66% RSE. They are therefore encoded as log-normal (exponential) random effects, which is what the reported percentages and shrinkages are consistent with.
  • Residual-error scales. Both tables print the residual point estimate on a percentage-style scale while the main text’s 95% CI column prints the same quantity as a fraction (PK: 28.94 vs 0.2730-0.3057; PD: 13.20 vs 0.1161-0.1479; the supplement’s bootstrap tables give 27.30-30.57 and 11.61-14.79). Both are entered as fractions: propSd = 0.2894 and addSd_IGF1ratio = 0.1320. An additive residual SD of 13.20 on a ratio-to-baseline scale that runs from 1 to about 2 would be nonsensical.
  • Kin carries the printed unit ng/(mL*h) but is dimensionless-per-hour. The PD variable the model fits is the IGF-1 ratio to baseline, so Kin and Kout are both 1/h on that scale and their ratio Kin/Kout = 1 is the baseline. The ng/(mL*h) tag in Table 4 is carried over from the absolute IGF-1 scale.
  • The PD parameters are the elderly-subject estimates. Table 4 is headed “PK/PD final model parameters of IGF-1 relative to baseline ratio in elderly subjects”, and the PD layer was fitted sequentially with individual PK parameters taken from the final PK model. Supplementary Figures 4 and 5 show that the same IDR structure was also fitted in healthy adults, but those estimates are not published, so the file carries only the elderly values.
  • No bioavailability term. V1/F, V2/F and CL2/F are apparent parameters throughout; F was not estimated, so no f(depot) is applied and the doses in the vignette are the administered subcutaneous amounts.
  • Cohort covariate distributions are assumed normal, truncated at the minima and maxima of source Table 2. The paper reports only mean, SD, median and range, not the full distributions. Sex is not simulated because it is not a model covariate.
  • DEVIATION – the elderly/adult exposure gap is under-reproduced. He 2025 reports observed elderly Cmax and AUC0-last at 30 ug/kg as 7.75% and 19.13% of the healthy-adult values; the model gives roughly 23% and 46%. The published percentages are non-compartmental summaries of observed concentrations from an eight-point sampling grid in a 16-subject cohort with 97% CV on V1/F, and the model is the shrunken population fit to the pooled data. The direction and a large fraction of the magnitude are reproduced; the gate above therefore checks only that elderly exposure is well under half the adult value. The published values are not reproducible from the published parameters and are recorded here rather than tuned to.
  • DEVIATION – median Tmax after the first elderly dose. The paper reports 46.16 h; the model gives 24 h on the same eight-point sampling grid (about 18 h on a dense grid, typical value). With Ka = 0.0070 /h at the cohort mean age the absorption half-life is about 100 h, so the profile is flip-flop and the peak is broad and flat – observed Tmax on a grid whose neighbouring points are 24 h and 72 h is very sensitive to assay noise, which the simulated Cc (an IPRED) excludes. The starred row in the NCA comparison table records the difference. The paper’s later-dose Tmax of 22.71 h is reproduced closely (24 h, +5.7%), so the disagreement is confined to the first-dose value.
  • The Conclusion’s 1.14-1.43 range is bracketed, not matched exactly. The paper does not state whether that range refers to peak, trough or mean baseline ratio, nor at which age and weight. At a typical 70-year-old, 60 kg subject the model gives troughs of 1.06-1.19 and 72 h peaks of 1.21-1.66 over 1.5-5 mg Q2W; the published range sits inside that envelope, and the 3 mg interval mean is close to the 1 SD reference of 1.186 derived from Supplementary Table 6.
  • No non-paper-derived parameter values. Every ini() entry is traceable to the article’s Table 3, Table 4, Results text, or the open-access Supplementary Material; nothing was digitised from a figure or supplied by correspondence.