Skip to contents

Model and source

Schlender 2016 is not a drug model. It is the system layer that a whole-body physiologically based pharmacokinetic (PBPK) model sits on, extended for the first time across the full course of healthy aging to 100 years of age. The paper’s deliverable is an age- and sex-resolved description of human physiology: body weight and height, thirteen organ and tissue masses, the cardiac index, ten organ blood flow rates, and a new aging function for the glomerular filtration rate (GFR).

That system layer is what this package extracts. Everything in it is published as numbers – Tables 2 and 3 and Equation 1 – and is reproduced here exactly.

What is deliberately not extracted, and why

Schlender 2016 verifies its physiology by building PBPK models of two intravenously dosed probe drugs, morphine (high hepatic extraction) and furosemide (mainly renal), in PK-Sim v5.5. Those two drug models are not part of this extraction, because they are not reproducible from the paper:

  • Table 1 is a compound card (molecular weight, log P, pKa, fraction unbound, specific clearances) – the inputs to the platform, not the model.
  • The tissue:plasma partition coefficients and the cellular permeabilities, which are the drug-specific parameters a whole-body PBPK actually runs on, are given only as a named method (“Willmann et al., defined as the PK-Sim standard”). A named method is an algorithm, not a set of values.
  • No PK-Sim project file was deposited, and the supplement contains only the aging-physiology literature review and Supplement Table 1 (the list of studies) – it holds no partition coefficients, no permeabilities and no ODEs.

Counting what would be needed against what is printed, the drug layer scores roughly 5 published parameters out of 35+ required per compound. The physiology layer, by contrast, is complete and is verified below.

Users who want a morphine or furosemide PK model to pair with this physiology can chain an independent compartmental model already in the registry, for example Elkomy_2015_morphine, Knibbe_2009_morphine or Hornik_2025_furosemide, or the PK-Sim-independent Gu_2025_furosemide_pbpk.

Population

  • Species: human
  • Age range: 30 to 100 years (the aging model; the underlying PK-Sim adult model extends below 30 y, and Fig. 2 shows organ weights from newborns, but the trajectories extracted here are defined on the 30-100 y decade knots of Tables 2 and 3)
  • Race / ethnicity: Healthy European Caucasians. Where European data were too sparse to describe an organ across the full age range, North American and Australian data were added (Methods, Workflow for Elderly PBPK Model Development; Discussion)
  • Regions: Europe, with North American and Australian data added for sparsely reported organs and for blood flow / cardiac output

The physiology is assembled from a literature analysis, not from a single cohort: 19 studies carried over from the Thompson et al. database plus 97 studies added by this paper’s own two PubMed / Google Scholar / MedPilot searches (Methods). Inclusion required clear assignment of sex and race, a comprehensible statement of the method of analysis, and exclusion of medication or disease effects on physiology; longitudinal surveillance was preferred. Organ volumes came mostly from autopsy series, with in vivo measurements used only for muscle, fat and blood flow.

The GFR aging function of Equation 1 was fitted to body-surface-area-adjusted data from 1,213 females and 1,081 males, mostly prospective renal transplant donors assessed with exogenous markers (Results, Glomerular Filtration Rate).

For the physiological-consistency checks the paper created a virtual 30-year-old in PK-Sim v5.5 and generated a virtual population of N = 5,000 for each sex over 30 to 100 years.

The same information is available programmatically via readModelDb("Schlender_2016_aging_physiology")()$population.

Source trace

The per-parameter origin is recorded as an in-file comment next to each ini() entry in inst/modeldb/endogenous/Schlender_2016_aging_physiology.R. The table below collects the whole model in one place.

Model component Source location
bw, ht Table 2, rows “Body weight, kg” and “Height, cm” (female + male)
mass_blood, mass_bone, mass_brain, mass_fat, mass_gonads, mass_heart, mass_kidney, mass_liver, mass_lung, mass_muscle, mass_pancreas, mass_skin, mass_spleen Table 2, “Organ masses, g” (female + male)
mass_gi Table 2 footnote a: held constant at 1274.5 g (female) / 1304.8 g (male)
cardiac_index Table 3, row “Cardiac index, L/m2” (female + male)
flow_adipose, flow_cerebral, flow_gonads, flow_myocardial, flow_renal, flow_splanchnic, flow_hepatic, flow_muscle, flow_skeleton, flow_skin Table 3, “Organ blood flow, mL/min” (female + male)
Linear interpolation between decade knots Methods, “Workflow for Elderly PBPK Model Development”: data pooled into 10-year age bins “and interpolated linearly”
gfr_fpma = 26.6 mL/min/100 g Text below Equation 1 (Rhodin maturation function at full maturation, 40th gestational week)
gfr_vmax = 0.9 Text below Equation 1, “maximal decreasing rate factor”
gfr_ta50_female = 59 y, gfr_ta50_male = 54 y Text below Equation 1, “aging half-time”
gfr_hill = 1.5 Text below Equation 1, “The Hill coefficient was parameterized at 1.5”
gfr_specific, gfr Equation 1, page 1581

Equation 1 is an embedded image in the published PDF and is dropped by every text converter (it does not survive plain-text extraction of the PDF and is absent from pdftotext output). It was recovered from the publisher’s equation graphic (40262_2016_422_Article_Equ1.gif, deposited with the article, PMC5107207) and confirmed by rendering page 8 of the PDF at 400 dpi. As printed:

SpecificGFR30−100y=FPMA×(1−Vmax×(age−30years)HillTA50Hill+(age−30years)Hill) \mathrm{Specific\ GFR}_{30-100y} = F_{\mathrm{PMA}} \times \left( 1 - \frac{V_{\max} \times (\mathrm{age} - 30\,\mathrm{years})^{\mathrm{Hill}}} {\mathrm{TA}_{50}^{\mathrm{Hill}} + (\mathrm{age} - 30\,\mathrm{years})^{\mathrm{Hill}}} \right)

Units

The model has no ODEs, so the dimensional check is on the algebraic outputs.

Symbol Units Note
time / age year the rxode2 time variable is chronological age
bw kg
ht cm
mass_* g including mass_gi, which is age-invariant
cardiac_index L/min/m^2 Table 3 prints the header as “L/m2”; the cardiac index is a flow per unit BSA, so the per-minute is implicit
bsa m^2 Du Bois, from ht (cm) and bw (kg)
cardiac_output mL/min cardiac_index (L/min/m^2) x bsa (m^2) x 1000 (mL/L)
flow_* mL/min
gfr_fpma, gfr_specific mL/min per 100 g kidney
gfr_ta50_*, gfr_age year
gfr_vmax, gfr_hill unitless see Errata on the gfr_vmax unit tag
gfr mL/min gfr_specific (mL/min/100 g) x mass_kidney (g) / 100

Parameter table

Parameter Value Fixed in source Label
gfr_fpma 26.6 TRUE Specific GFR after full maturation F_PMA (mL/min per 100 g kidney weight)
gfr_vmax 0.9 TRUE Maximal fractional decrease in specific GFR with aging Vmax (fraction of F_PMA)
gfr_ta50_female 59.0 TRUE Aging half-time TA50 for females (years after the age of 30)
gfr_ta50_male 54.0 TRUE Aging half-time TA50 for males (years after the age of 30)
gfr_hill 1.5 TRUE Hill coefficient of the GFR aging function (unitless)

All five are published point estimates of a fitted function with no reported standard error, so all are fixed().

Simulation

The model is deterministic: no between-subject variability and no residual error are encoded (see Errata). One “subject” per sex is solved across the full published age range.

mod <- readModelDb("Schlender_2016_aging_physiology")

solve_sex <- function(sexf, ages) {
  ev <- as.data.frame(rxode2::et(ages))
  ev$SEXF <- sexf
  out <- rxode2::rxSolve(mod, ev, returnType = "data.frame")
  out$SEXF <- sexf
  out$sex <- ifelse(sexf == 1, "Female", "Male")
  out
}

knots <- seq(30, 100, by = 10)
fine <- seq(30, 100, by = 0.5)

sim_knot <- dplyr::bind_rows(solve_sex(1, knots), solve_sex(0, knots))
sim_fine <- dplyr::bind_rows(solve_sex(1, fine), solve_sex(0, fine))

nrow(sim_knot)
#> [1] 16
nrow(sim_fine)
#> [1] 282

Validation

1. Exact reproduction of Tables 2 and 3

The strongest available gate: the model must return every one of the 416 published cells of Tables 2 and 3 at the decade knots. The reference values below are transcribed independently from the article PDF.

# Schlender 2016 Table 2 (anthropometry + organ masses) and Table 3
# (cardiac index + organ blood flows). Order is age 30, 40, ..., 100.
ref <- tibble::tribble(
  ~var, ~sex, ~v30, ~v40, ~v50, ~v60, ~v70, ~v80, ~v90, ~v100,
  # ---- Table 2 ----
  "bw", "Female", 60.0, 63.3, 65.2, 65.5, 62.2, 56.1, 53.7, 45.2,
  "bw", "Male", 73.0, 74.4, 73.0, 72.0, 71.1, 68.1, 64.9, 55.6,
  "ht", "Female", 163.0, 160.9, 160.0, 158.3, 155.4, 151.7, 150.0, 147.9,
  "ht", "Male", 176.0, 175.1, 173.9, 171.2, 167.4, 165.5, 163.8, 157.6,
  "mass_blood", "Female", 1899.4, 1921.1, 1961.2, 1946.9, 1847.7, 1692.6, 1634.5, 1357.0,
  "mass_blood", "Male", 2264.6, 2269.5, 2234.9, 2180.2, 2111.3, 2042.5, 1976.5, 1739.5,
  "mass_bone", "Female", 9121.5, 8886.3, 8676.1, 8401.8, 7811.6, 6981.4, 6619.0, 6263.5,
  "mass_bone", "Male", 11817.8, 11848.0, 11481.6, 11091.6, 10355.0, 9637.1, 8969.0, 8300.9,
  "mass_brain", "Female", 1357.0, 1352.3, 1347.6, 1315.1, 1287.3, 1241.4, 1169.4, 1097.4,
  "mass_brain", "Male", 1508.8, 1506.9, 1502.9, 1463.3, 1434.3, 1400.1, 1372.1, 1344.2,
  "mass_fat", "Female", 19348.0, 20002.5, 23815.4, 26993.0, 27884.7, 25694.5, 25020.6, 20171.4,
  "mass_fat", "Male", 14868.0, 16309.7, 16676.5, 17920.7, 19447.4, 19947.7, 20691.0, 17082.3,
  "mass_gonads", "Female", 13.1, 13.0, 6.6, 5.3, 5.2, 5.1, 5.1, 4.9,
  "mass_gonads", "Male", 40.3, 40.8, 35.0, 33.5, 31.9, 30.6, 30.3, 29.5,
  "mass_heart", "Female", 328.4, 340.0, 355.6, 377.1, 399.1, 413.5, 431.2, 412.9,
  "mass_heart", "Male", 417.2, 434.3, 439.1, 454.6, 464.8, 441.4, 437.9, 425.1,
  "mass_kidney", "Female", 403.4, 401.7, 400.5, 383.4, 364.2, 325.2, 308.8, 295.8,
  "mass_kidney", "Male", 437.7, 475.9, 468.1, 455.6, 441.7, 395.7, 366.7, 355.9,
  "mass_liver", "Female", 1905.5, 1881.0, 1867.7, 1679.8, 1504.9, 1423.4, 1329.5, 1189.4,
  "mass_liver", "Male", 2357.8, 2324.5, 2206.0, 2041.3, 1714.6, 1417.3, 1324.0, 1202.0,
  "mass_lung", "Female", 1009.5, 1021.4, 1038.8, 1023.8, 1005.4, 849.1, 804.5, 637.3,
  "mass_lung", "Male", 1294.3, 1334.7, 1337.4, 1349.0, 1271.8, 1138.0, 1007.4, 978.0,
  "mass_muscle", "Female", 20276.2, 23058.3, 21300.1, 18971.2, 15746.4, 13367.8, 12394.3, 10060.6,
  "mass_muscle", "Male", 32338.6, 32318.2, 31180.6, 29600.0, 28562.1, 26524.8, 23657.0, 19534.0,
  "mass_pancreas", "Female", 169.5, 170.3, 163.8, 158.7, 148.5, 131.9, 128.0, 122.6,
  "mass_pancreas", "Male", 190.3, 190.8, 183.6, 178.2, 165.4, 149.9, 143.7, 139.5,
  "mass_skin", "Female", 2723.5, 2773.2, 2836.2, 2829.5, 2725.8, 2559.1, 2493.8, 2223.5,
  "mass_skin", "Male", 3760.9, 3790.2, 3745.1, 3692.0, 3635.2, 3537.0, 3439.7, 3122.6,
  "mass_spleen", "Female", 219.2, 197.7, 190.7, 182.4, 164.5, 149.7, 99.4, 78.7,
  "mass_spleen", "Male", 243.4, 221.9, 208.2, 197.0, 186.6, 160.4, 133.4, 108.4,
  # ---- Table 3 ----
  "cardiac_index", "Female", 3.34, 3.33, 3.08, 2.89, 2.75, 2.68, 2.52, 2.41,
  "cardiac_index", "Male", 3.23, 3.22, 3.09, 2.86, 2.58, 2.36, 2.18, 2.04,
  "flow_adipose", "Female", 501.1, 528.2, 578.0, 655.3, 684.7, 630.2, 607.0, 491.5,
  "flow_adipose", "Male", 324.7, 352.1, 357.3, 381.4, 408.0, 419.8, 429.2, 357.3,
  "flow_cerebral", "Female", 707.8, 686.9, 666.1, 632.1, 601.2, 562.8, 514.2, 467.6,
  "flow_cerebral", "Male", 779.7, 747.6, 727.9, 692.9, 650.6, 623.8, 594.4, 536.1,
  "flow_gonads", "Female", 1.2, 1.2, 0.6, 0.5, 0.5, 0.5, 0.5, 0.4,
  "flow_gonads", "Male", 3.2, 3.3, 2.8, 2.7, 2.6, 2.5, 2.4, 2.4,
  "flow_myocardial", "Female", 295.0, 311.2, 313.2, 363.1, 376.3, 445.8, 464.8, 445.2,
  "flow_myocardial", "Male", 260.1, 271.1, 278.0, 299.1, 308.7, 334.7, 332.1, 322.3,
  "flow_renal", "Female", 1121.0, 1061.8, 943.8, 825.8, 707.8, 589.8, 471.8, 353.8,
  "flow_renal", "Male", 1325.0, 1339.3, 1116.3, 913.3, 730.3, 567.3, 424.3, 301.3,
  "flow_splanchnic", "Female", 1239.0, 1254.3, 1187.2, 1015.3, 862.6, 771.5, 679.1, 570.5,
  "flow_splanchnic", "Male", 1235.0, 1241.9, 1237.7, 1056.7, 813.1, 610.6, 512.9, 413.5,
  "flow_hepatic", "Female", 383.4, 388.1, 367.3, 314.2, 266.9, 238.7, 210.1, 176.5,
  "flow_hepatic", "Male", 423.0, 425.4, 423.9, 361.9, 278.5, 209.1, 175.7, 141.6,
  "flow_muscle", "Female", 665.1, 774.8, 636.6, 568.4, 479.8, 405.6, 369.0, 298.8,
  "flow_muscle", "Male", 1105.7, 1086.8, 1036.8, 975.0, 922.3, 861.7, 751.9, 625.9,
  "flow_skeleton", "Female", 294.9, 287.3, 280.5, 271.6, 252.6, 225.7, 214.0, 202.5,
  "flow_skeleton", "Male", 324.9, 325.8, 315.7, 305.0, 284.7, 265.0, 246.6, 228.2,
  "flow_skin", "Female", 295.7, 301.1, 307.9, 307.2, 295.9, 277.8, 270.7, 241.4,
  "flow_skin", "Male", 325.1, 327.7, 323.8, 319.2, 314.3, 305.8, 297.4, 270.0
) |>
  tidyr::pivot_longer(
    # Name the age columns explicitly: starts_with("v") would also match
    # the `var` column and silently poison the pivot.
    cols = dplyr::all_of(paste0("v", seq(30, 100, by = 10))),
    names_to = "age", values_to = "published"
  ) |>
  dplyr::mutate(age = as.numeric(sub("^v", "", .data$age)))

nrow(ref)
#> [1] 416
got <- sim_knot |>
  dplyr::select(age = "time", "sex", dplyr::all_of(unique(ref$var))) |>
  tidyr::pivot_longer(
    cols = dplyr::all_of(unique(ref$var)),
    names_to = "var", values_to = "simulated"
  )

cmp <- ref |>
  dplyr::inner_join(got, by = c("var", "sex", "age")) |>
  dplyr::mutate(abs_diff = abs(.data$simulated - .data$published))

# The join must not silently drop rows -- a lost row would make the
# gate below pass vacuously.
stopifnot(
  nrow(cmp) == nrow(ref),
  nrow(cmp) == 416L,
  !anyNA(cmp$simulated)
)

# Deterministic algebra: the model must return the published cells to
# machine precision, not "close to" them.
stopifnot(max(cmp$abs_diff) < 1e-8)

c(cells = nrow(cmp), max_abs_diff = max(cmp$abs_diff))
#>        cells max_abs_diff 
#> 4.160000e+02 3.637979e-12

All 416 published cells are returned exactly.

The constant gastrointestinal mass of Table 2 footnote a is checked separately because it has no age dimension:

gi <- sim_fine |>
  dplyr::group_by(.data$sex) |>
  dplyr::summarise(lo = min(.data$mass_gi), hi = max(.data$mass_gi), .groups = "drop")

stopifnot(
  # Age-invariant within each sex.
  all(abs(gi$hi - gi$lo) < 1e-8),
  abs(gi$lo[gi$sex == "Female"] - 1274.5) < 1e-8,
  abs(gi$lo[gi$sex == "Male"] - 1304.8) < 1e-8
)
gi
#> # A tibble: 2 × 3
#>   sex       lo    hi
#>   <chr>  <dbl> <dbl>
#> 1 Female 1274. 1274.
#> 2 Male   1305. 1305.

2. Interpolation behaviour between and beyond the knots

The paper interpolates linearly between the ten-year bins. Between knots the model must therefore sit exactly on the chord, and outside 30 to 100 years the trajectory is held flat rather than extrapolating the last decade’s slope.

mid <- dplyr::bind_rows(solve_sex(1, c(35, 45, 95)), solve_sex(0, c(35, 45, 95)))
kn <- sim_knot

chord <- function(v, sex, a) {
  lo <- kn[[v]][kn$sex == sex & kn$time == a - 5]
  hi <- kn[[v]][kn$sex == sex & kn$time == a + 5]
  (lo + hi) / 2
}

midpoint_err <- vapply(
  c("mass_liver", "mass_kidney", "flow_renal", "bw"),
  function(v) {
    max(abs(vapply(
      c(35, 45, 95),
      function(a) {
        max(abs(c(
          mid[[v]][mid$sex == "Female" & mid$time == a] - chord(v, "Female", a),
          mid[[v]][mid$sex == "Male" & mid$time == a] - chord(v, "Male", a)
        )))
      },
      numeric(1)
    )))
  },
  numeric(1)
)

stopifnot(max(midpoint_err) < 1e-8)

# Flat outside the published 30-100 y range.
outside <- dplyr::bind_rows(solve_sex(1, c(10, 25, 30, 100, 110)), solve_sex(0, c(10, 25, 30, 100, 110)))
flat_lo <- outside |> dplyr::filter(.data$time %in% c(10, 25, 30))
flat_hi <- outside |> dplyr::filter(.data$time %in% c(100, 110))
stopifnot(
  dplyr::n_distinct(round(flat_lo$mass_liver[flat_lo$sex == "Male"], 8)) == 1L,
  dplyr::n_distinct(round(flat_hi$mass_liver[flat_hi$sex == "Male"], 8)) == 1L
)

midpoint_err
#>   mass_liver  mass_kidney   flow_renal           bw 
#> 0.000000e+00 5.684342e-14 0.000000e+00 0.000000e+00

3. Cardiac-output mass balance

The paper states plainly that “the sum of all blood flows should be equal to CO” (Methods), and that age-related changes in blood flow distribution were accounted for as changes in cardiac output. This is an internal-consistency gate on Table 3 that the paper never reports numerically.

flow_cols <- c(
  "flow_adipose", "flow_cerebral", "flow_gonads", "flow_myocardial",
  "flow_renal", "flow_splanchnic", "flow_hepatic", "flow_muscle",
  "flow_skeleton", "flow_skin"
)

balance <- sim_knot |>
  dplyr::mutate(
    sum_flows = rowSums(dplyr::across(dplyr::all_of(flow_cols))),
    ratio = .data$sum_flows / .data$cardiac_output
  ) |>
  dplyr::select(age = "time", "sex", "sum_flows", "cardiac_output", "ratio")

stopifnot(nrow(balance) == 16L)

# The residual absorbs the organs Schlender 2016 excludes from Table 3
# (the paper notes the modelled organ masses reach only 91-95 % of body
# weight), the 3-significant-figure rounding of the published cells, and
# the unstated choice of BSA formula. 5 % leaves room for all three and
# still goes red on a mis-transcribed flow, which would move a single
# organ's contribution by tens of percent.
stopifnot(max(abs(balance$ratio - 1)) < 0.05)

balance |>
  dplyr::mutate(dplyr::across(c("sum_flows", "cardiac_output"), ~ round(.x, 0)), ratio = round(.data$ratio, 4)) |>
  dplyr::rename(
    "Age (y)" = "age", "Sex" = "sex",
    "Sum of Table 3 flows (mL/min)" = "sum_flows",
    "Cardiac output (mL/min)" = "cardiac_output",
    "Ratio" = "ratio"
  ) |>
  knitr::kable()
Age (y) Sex Sum of Table 3 flows (mL/min) Cardiac output (mL/min) Ratio
30 Female 5504 5491 1.0024
40 Female 5595 5548 1.0084
50 Female 5281 5176 1.0204
60 Female 4954 4828 1.0259
70 Female 4528 4435 1.0211
80 Female 4148 4065 1.0206
90 Female 3801 3721 1.0215
100 Female 3248 3274 0.9922
30 Male 6106 6102 1.0007
40 Male 6121 6110 1.0018
50 Male 5820 5787 1.0057
60 Male 5307 5265 1.0080
70 Male 4713 4648 1.0140
80 Male 4200 4140 1.0146
90 Male 3767 3719 1.0129
100 Male 3199 3169 1.0094

The flows sum to cardiac output within 2.6 % at every one of the 16 age-by-sex cells, and within 0.2 % at the ICRP-derived 30-year reference. Table 3 is internally consistent.

4. The GFR aging function (Equation 1), replicating Figure 5

# At age 30 the aging term is exactly zero, so specific GFR must equal
# F_PMA and absolute GFR must equal F_PMA * kidney mass / 100.
at30 <- sim_knot |> dplyr::filter(.data$time == 30)
stopifnot(
  all(abs(at30$gfr_specific - 26.6) < 1e-10),
  all(abs(at30$gfr - 26.6 * at30$mass_kidney / 100) < 1e-10)
)

# Specific GFR is strictly decreasing across the published range, and the
# female half-time (59 y) is longer than the male (54 y), so at any age
# above 30 the female retains a larger FRACTION of F_PMA.
sf <- sim_fine |> dplyr::filter(.data$sex == "Female") |> dplyr::arrange(.data$time)
sm <- sim_fine |> dplyr::filter(.data$sex == "Male") |> dplyr::arrange(.data$time)
stopifnot(
  all(diff(sf$gfr_specific) < 0),
  all(diff(sm$gfr_specific) < 0),
  all(sf$gfr_specific[sf$time > 30] > sm$gfr_specific[sm$time > 30])
)

round(at30$gfr, 1)
#> [1] 107.3 116.4
ggplot2::ggplot(sim_fine, ggplot2::aes(.data$time, .data$gfr)) +
  ggplot2::geom_line(linewidth = 0.8) +
  ggplot2::facet_wrap(~sex) +
  ggplot2::scale_y_continuous(limits = c(0, NA)) +
  ggplot2::labs(
    x = "Age (years)",
    y = "Glomerular filtration rate (mL/min)"
  ) +
  ggplot2::theme_bw()
Replicates Figure 5 of Schlender 2016: absolute glomerular filtration rate over the course of aging, female (left) and male (right). The published panels show the fitted line falling from roughly 105 to 35 mL/min in females and from roughly 118 to 40 mL/min in males between 30 and 100 years.

Replicates Figure 5 of Schlender 2016: absolute glomerular filtration rate over the course of aging, female (left) and male (right). The published panels show the fitted line falling from roughly 105 to 35 mL/min in females and from roughly 118 to 40 mL/min in males between 30 and 100 years.

gfr_ends <- sim_knot |>
  dplyr::filter(.data$time %in% c(30, 100)) |>
  dplyr::select(age = "time", "sex", "gfr_specific", "gfr")

# Read off the published Fig. 5 panels: the fitted GFR line starts near
# 105 (female) / 118 (male) mL/min at age 30 and ends near 35 / 40 at age
# 100. The model is the paper's own equation applied to the paper's own
# kidney masses, so agreement should be within the precision of reading a
# published axis -- 8 mL/min is about one third of a minor gridline.
fig5 <- c("Female_30" = 105, "Male_30" = 118, "Female_100" = 35, "Male_100" = 40)
key <- paste(gfr_ends$sex, gfr_ends$age, sep = "_")
stopifnot(all(key %in% names(fig5)))
stopifnot(max(abs(gfr_ends$gfr - fig5[key])) < 8)

gfr_ends |>
  dplyr::mutate(
    read_from_fig5 = fig5[paste(.data$sex, .data$age, sep = "_")],
    dplyr::across(c("gfr_specific", "gfr"), ~ round(.x, 1))
  ) |>
  dplyr::rename(
    "Age (y)" = "age", "Sex" = "sex",
    "Specific GFR (mL/min/100 g)" = "gfr_specific",
    "GFR (mL/min)" = "gfr",
    "Read from published Fig. 5" = "read_from_fig5"
  ) |>
  knitr::kable()
Age (y) Sex Specific GFR (mL/min/100 g) GFR (mL/min) Read from published Fig. 5
30 Female 26.6 107.3 105
100 Female 13.1 38.8 35
30 Male 26.6 116.4 118
100 Male 12.3 43.9 40

5. Replicating Figure 3 (body height) and Figure 2 (organ masses)

ggplot2::ggplot(sim_fine, ggplot2::aes(.data$time, .data$ht, colour = .data$sex)) +
  ggplot2::geom_line(linewidth = 0.8) +
  ggplot2::labs(x = "Age (years)", y = "Body height (cm)", colour = "Sex") +
  ggplot2::theme_bw()
Replicates Figure 3 of Schlender 2016: age-dependent decrease in body height in females and males over 30 to 100 years.

Replicates Figure 3 of Schlender 2016: age-dependent decrease in body height in females and males over 30 to 100 years.

sim_fine |>
  dplyr::select(
    "time", "sex",
    "mass_fat", "mass_muscle", "mass_bone", "mass_skin",
    "mass_liver", "mass_brain", "mass_blood"
  ) |>
  tidyr::pivot_longer(dplyr::starts_with("mass_"), names_to = "tissue", values_to = "g") |>
  dplyr::mutate(tissue = sub("^mass_", "", .data$tissue)) |>
  ggplot2::ggplot(ggplot2::aes(.data$time, .data$g / 1000, colour = .data$tissue)) +
  ggplot2::geom_line(linewidth = 0.7) +
  ggplot2::facet_wrap(~sex) +
  ggplot2::labs(x = "Age (years)", y = "Mass (kg)", colour = "Tissue") +
  ggplot2::theme_bw()
Replicates the 30-100 y portion of Figure 2 of Schlender 2016: distribution of mean organ and tissue weights across the course of aging. Fat rises then falls while muscle and liver decline throughout.

Replicates the 30-100 y portion of Figure 2 of Schlender 2016: distribution of mean organ and tissue weights across the course of aging. Fat rises then falls while muscle and liver decline throughout.

6. The paper’s prose claims, checked against its own tables

Schlender 2016 states a number of quantitative aging rates in the Results narrative. None of them is a model parameter, so none is exercised by the checks above – but each is a claim about the very tables this model encodes, and a transcription error would break the agreement. Converting them into assertions is therefore a genuine gate, and it also surfaces several places where the prose and the tables disagree.

at <- function(v, sex, a) sim_knot[[v]][sim_knot$sex == sex & sim_knot$time == a]
per_decade_pct <- function(v, sex, a0, a1) {
  ((at(v, sex, a1) / at(v, sex, a0))^(10 / (a1 - a0)) - 1) * 100
}

claims <- tibble::tribble(
  ~Claim, ~Source, ~Published, ~Achieved, ~Tol, ~Deviation,
  "Male cardiac index falls 8.30 %/decade in the elderly (60-100 y)",
  "Results 3.1.6", -8.30, per_decade_pct("cardiac_index", "Male", 60, 100), 0.5, FALSE,

  "Splanchnic blood flow is 22.5 % of cardiac output in adults (female, 30 y)",
  "Results 3.1.8", 22.5,
  100 * at("flow_splanchnic", "Female", 30) / at("cardiac_output", "Female", 30), 0.5, FALSE,

  "Splanchnic blood flow falls to 17.5 % of cardiac output in the tenth decade (female, 100 y)",
  "Results 3.1.8", 17.5,
  100 * at("flow_splanchnic", "Female", 100) / at("cardiac_output", "Female", 100), 0.5, FALSE,

  "Male fat mass increases by 0.978 kg/decade, constantly (30-90 y)",
  "Results 3.1.3", 0.978,
  (at("mass_fat", "Male", 90) - at("mass_fat", "Male", 30)) / 1000 / 6, 0.05, FALSE,

  "Female fat mass gains 3.50 kg/decade over the first two decades after menopause (40-60 y)",
  "Results 3.1.3", 3.50,
  (at("mass_fat", "Female", 60) - at("mass_fat", "Female", 40)) / 1000 / 2, 0.05, FALSE,

  "Relative fat mass reaches 45.8 % in female centenarians",
  "Results 3.1.3", 45.8,
  100 * at("mass_fat", "Female", 100) / 1000 / at("bw", "Female", 100), 1.5, FALSE,

  "Relative fat mass reaches 31.5 % in male centenarians",
  "Results 3.1.3", 31.5,
  100 * at("mass_fat", "Male", 100) / 1000 / at("bw", "Male", 100), 1.5, FALSE,

  "Male skeletal muscle is 44 % of body weight in adults (30 y)",
  "Results 3.1.2", 44.0,
  100 * at("mass_muscle", "Male", 30) / 1000 / at("bw", "Male", 30), 1.0, FALSE,

  "Female skeletal muscle falls to 22 % of body weight in centenarians",
  "Results 3.1.2", 22.0,
  100 * at("mass_muscle", "Female", 100) / 1000 / at("bw", "Female", 100), 1.0, FALSE,

  "Male skeletal muscle falls to 34 % of body weight in the extremely old (100 y)",
  "Results 3.1.2", 34.0,
  100 * at("mass_muscle", "Male", 100) / 1000 / at("bw", "Male", 100), 1.5, FALSE,

  "Male body height loses about 3.5 %/decade in the tenth decade",
  "Results 3.1.1", -3.5, per_decade_pct("ht", "Male", 90, 100), 0.5, FALSE,

  "Male liver mass falls 11.4 %/decade after 60 y",
  "Results 3.1.5", -11.4, per_decade_pct("mass_liver", "Male", 60, 100), 1.2, FALSE,

  # ---- Documented deviations: prose vs the paper's own tables ----
  "Female liver mass falls 8.3 %/decade after 40 y",
  "Results 3.1.5", -8.3, per_decade_pct("mass_liver", "Female", 40, 100), 0.5, TRUE,

  "Male liver mass falls 6.1 %/decade up to 60 y",
  "Results 3.1.5", -6.1, per_decade_pct("mass_liver", "Male", 30, 60), 0.5, TRUE,

  "Female cardiac index falls almost linearly by 3.22 %/decade after 30 y",
  "Results 3.1.6", -3.22, per_decade_pct("cardiac_index", "Female", 30, 100), 0.5, TRUE,

  "Male cardiac index falls 1.46 %/decade during early adulthood (30-60 y)",
  "Results 3.1.6", -1.46, per_decade_pct("cardiac_index", "Male", 30, 60), 0.5, TRUE,

  "Female body height loses 4.2 %/decade in nonagenarians",
  "Results 3.1.1", -4.2, per_decade_pct("ht", "Female", 90, 100), 0.5, TRUE,

  "Body weight decline rises to nearly 10 %/decade between 90 and 100 y (female)",
  "Results 3.1.1", -10.0, per_decade_pct("bw", "Female", 90, 100), 1.5, TRUE
) |>
  dplyr::mutate(
    Achieved = round(.data$Achieved, 2),
    Pass = abs(.data$Achieved - .data$Published) <= .data$Tol
  )

# Every non-deviation claim must reproduce. The deviations are kept visible
# in the rendered table rather than having their tolerance widened until
# they disappear.
stopifnot(all(claims$Pass[!claims$Deviation]))
# ... and each recorded deviation must genuinely still be a deviation, so
# this gate goes red if a future edit silently fixes or breaks one.
stopifnot(!any(claims$Pass[claims$Deviation]))

claims |>
  dplyr::select("Claim", "Source", "Published", "Achieved", "Pass", "Deviation") |>
  knitr::kable()
Claim Source Published Achieved Pass Deviation
Male cardiac index falls 8.30 %/decade in the elderly (60-100 y) Results 3.1.6 -8.300 -8.10 TRUE FALSE
Splanchnic blood flow is 22.5 % of cardiac output in adults (female, 30 y) Results 3.1.8 22.500 22.56 TRUE FALSE
Splanchnic blood flow falls to 17.5 % of cardiac output in the tenth decade (female, 100 y) Results 3.1.8 17.500 17.43 TRUE FALSE
Male fat mass increases by 0.978 kg/decade, constantly (30-90 y) Results 3.1.3 0.978 0.97 TRUE FALSE
Female fat mass gains 3.50 kg/decade over the first two decades after menopause (40-60 y) Results 3.1.3 3.500 3.50 TRUE FALSE
Relative fat mass reaches 45.8 % in female centenarians Results 3.1.3 45.800 44.63 TRUE FALSE
Relative fat mass reaches 31.5 % in male centenarians Results 3.1.3 31.500 30.72 TRUE FALSE
Male skeletal muscle is 44 % of body weight in adults (30 y) Results 3.1.2 44.000 44.30 TRUE FALSE
Female skeletal muscle falls to 22 % of body weight in centenarians Results 3.1.2 22.000 22.26 TRUE FALSE
Male skeletal muscle falls to 34 % of body weight in the extremely old (100 y) Results 3.1.2 34.000 35.13 TRUE FALSE
Male body height loses about 3.5 %/decade in the tenth decade Results 3.1.1 -3.500 -3.79 TRUE FALSE
Male liver mass falls 11.4 %/decade after 60 y Results 3.1.5 -11.400 -12.40 TRUE FALSE
Female liver mass falls 8.3 %/decade after 40 y Results 3.1.5 -8.300 -7.35 FALSE TRUE
Male liver mass falls 6.1 %/decade up to 60 y Results 3.1.5 -6.100 -4.69 FALSE TRUE
Female cardiac index falls almost linearly by 3.22 %/decade after 30 y Results 3.1.6 -3.220 -4.56 FALSE TRUE
Male cardiac index falls 1.46 %/decade during early adulthood (30-60 y) Results 3.1.6 -1.460 -3.97 FALSE TRUE
Female body height loses 4.2 %/decade in nonagenarians Results 3.1.1 -4.200 -1.40 FALSE TRUE
Body weight decline rises to nearly 10 %/decade between 90 and 100 y (female) Results 3.1.1 -10.000 -15.83 FALSE TRUE

Twelve of the eighteen prose claims reproduce from the tables within tolerance. The six that do not are discussed in the Errata below.

Assumptions and deviations

Errata: prose that disagrees with the paper’s own Tables 2 and 3

These are disagreements within the source, not transcription choices. The tables are used, because the tables are what the PBPK model was actually parameterised with (“as used in the elderly PBPK models” – Table 2 caption).

  1. Female body height, “a 4.2 % decrease in body height per decade in female nonagenarians” (Results 3.1.1). Table 2 gives 150.0 cm at 90 y and 147.9 cm at 100 y, a 1.4 % decrease per decade – three-fold smaller. No decade in the female height row reaches 4.2 %; the largest is 2.4 % (70 to 80 y), and even the whole 70-to-100-year span averages 1.6 %/decade. The corresponding male claim (“about 3.5 %” in the tenth decade) does reproduce, at 3.8 %.

  2. Body weight, “rose to nearly 10 % per decade between 90 and 100 years” (Results 3.1.1). Table 2 gives 53.7 to 45.2 kg in females (15.8 %) and 64.9 to 55.6 kg in males (14.3 %). The prose understates the tabulated decline by roughly half.

  3. Female cardiac index, “an almost linear decrease of 3.22 % per decade after the age of 30 years” (Results 3.1.6). The Table 3 female row falls from 3.34 to 2.41 L/min/m^2 over 30 to 100 y, which is 4.56 %/decade, and it is visibly non-linear (essentially flat from 30 to 40 y, then steepening).

  4. Male cardiac index, “1.46 % per decade during early adulthood” (Results 3.1.6). Table 3 gives only 0.31 %/decade from 30 to 40 y and 3.97 %/decade averaged over 30 to 60 y; no window in the tabulated range yields 1.46 %. The companion elderly claim (8.30 %/decade) reproduces almost exactly, at 8.10 %.

  5. Liver mass rates (Results 3.1.5). The female “8.3 %/decade after 40 years” is 7.35 %/decade in Table 2, and the male “6.1 %/decade to the age of 60” is 4.69 %/decade. The male post-60 rate (“11.4 %/decade”) does reproduce, at 12.4 %.

  6. Male body weight peak. The prose says “both sexes showed an increase in body mass up to the age of 55 years”. The female row does peak between the 50- and 60-year knots, but the male row peaks at the 40-year knot (74.4 kg) and declines thereafter. The decade knots cannot resolve an age of 55 in any case.

Two further prose statements are reconciled rather than contradicted, and the window used in the assertion table above reflects the reconciliation:

  • “3.50 kg fat mass per decade in the first two decades after menopause” reproduces to three significant figures only over the 40-to-60-year window (3.495 kg/decade), not the 50-to-70-year window a literal reading of “after menopause” suggests (2.04 kg/decade).
  • “37 % of body weight in adults” for female skeletal muscle is the value at the 40-year knot (36.4 %), which is where the female muscle row peaks, not at 30 y (33.8 %).

Assumptions made by this extraction

  1. gfr_vmax is dimensionless. The prose tags the maximal decreasing rate factor as “0.9 mL/min/100 g kidney weight”, carrying over the unit of F_PMA from the preceding sentence. Equation 1 multiplies Vmax by a unitless Hill ratio inside (1 - ...), so Vmax must be a dimensionless fraction for the equation to be dimensionally consistent – and the resulting curve matches the published Figure 5. It is encoded as a fraction.

  2. Body surface area uses the Du Bois equation. Table 3 reports a cardiac index (per m^2 of BSA) but the paper never states which BSA formula it used, so recovering cardiac output requires a choice. Du Bois is used. The choice is not load-bearing: with the Mosteller equation instead, the flow-sum-to-cardiac-output ratios of validation section 3 move by less than 1.5 percentage points and every cell still balances, and cardiac_index itself – the published quantity – is unaffected.

  3. Flat extrapolation outside 30 to 100 years. Tables 2 and 3 are defined on the 30-to-100-year knots. Below 30 y and above 100 y the model holds the end knot rather than continuing the adjacent decade’s slope, which the paper does not support. Figure 2 of the paper does show organ weights from birth, but those come from the underlying PK-Sim paediatric model, not from this paper’s tables, and are not extracted.

  4. No variability is encoded. The paper assumed a 5 % coefficient of variation on every blood flow rate because “the general lack of perfusion data impeded computation of robust variability” (Results 3.1.8), and made GFR variability depend entirely on kidney-size variability – which the Discussion itself judges “too great”. Neither is a fitted between-subject variance, so neither is encoded as an eta; this model returns typical values. Organ-mass variability is likewise not reported per organ.

  5. The GFR function is evaluated against mean kidney mass. The paper parameterised specific GFR “using the observed GFR values and assuming the mean kidney weight at the respective age” (Results 3.1.10), which is exactly what gfr <- gfr_specific * mass_kidney / 100 does with the Table 2 row.

  6. SEXF is an indicator, not a proportion. Every trajectory is published as separate female and male rows and the model blends them linearly by SEXF. Values strictly between 0 and 1 produce a sex-averaged physiology that the paper never reports and that should not be interpreted as a real individual.

Not extracted from this paper

  • The morphine and furosemide PBPK drug models (see “What is deliberately not extracted” at the top), including Table 1’s compound cards and Table 5’s predictive-performance statistics.
  • Total body water (Figure 4). The paper plots extracellular and total body water as percentages of body weight against age but tabulates no values, and the Discussion notes that no body-water data were available for females above 80 years.
  • The per-organ literature review in the Electronic Supplementary Material, which is narrative plus Supplement Table 1 (a list of the contributing studies) and contains no additional parameter values.