Skip to contents

Model and source

The packaged model implements the final joint model in Figure 1 and Table 3 of the paper: a mechanism-based system that couples (i) a two-compartment acetaminophen (paracetamol) PK model with first-order absorption from an upper-small-intestine compartment that shares its stomach-emptying rate KG with the nutrients, (ii) two precursor + plasma indirect-response models for the fast (CCKF) and late (CCKL) cholecystokinin secretion peaks, driven respectively by duodenal and jejunal nutrient signals, and (iii) a third indirect-response model for the gallbladder volume driven by a duodenal-nutrient Emax stimulus. Three parallel nutrient signal tracks (per-macronutrient stomach -> upper SI / duodenum / upper jejunum) carry the per-macronutrient gram amounts through their respective transit and saturable-MM-absorption kinetics; see the Errata section for the rationale of the parallel-track encoding.

Population

The model-development pool comprises 66 subjects (33 patients with type 2 diabetes and 33 nondiabetic controls matched on gender, age, and BMI) from three crossover postprandial-challenge studies conducted at Gentofte Hospital / University of Copenhagen (Study A: water, nasogastric infusion of 1.5 g acetaminophen, n=20; Study B: three glucose-only OGTT drinks at 25 / 75 / 125 g, n=16; Study C: 75 g OGTT plus three isocaloric low/medium/high-fat drinks, n=30). An external validation cohort (Study D, 10 nondiabetic controls, medium-high-fat drink) was used for predictive checks but is not pooled into the fit. Demographic distributions are reported in Table 1 of the paper; pooled medians used as reference values for the covariate effects in this extraction are WT = 88 kg (BASEBILE reference) and AGE = 58 y (S50_BILE reference).

The same metadata is available programmatically:

mod_meta <- rxode2::rxode2(readModelDb("Guiastrennec_2016_gastric_emptying"))
#>  parameter labels from comments will be replaced by 'label()'
str(mod_meta$population)
#> List of 12
#>  $ species       : chr "human"
#>  $ n_subjects    : int 66
#>  $ n_studies     : int 3
#>  $ age_range     : chr "Pooled across Studies A-C: 38-75 years (Bagger 38-75, Sonne 42-71, Hansen 44-74); medians ~57-62 y per study"
#>  $ age_median    : chr "58 years (pooled study-population median; reference for AGE-S50_BILE effect)"
#>  $ weight_range  : chr "63.6-122 kg pooled across Studies A-C"
#>  $ weight_median : chr "88 kg (pooled study-population median; reference for WT-BASEBILE effect)"
#>  $ sex_female_pct: num 39
#>  $ disease_state : chr "33 patients with type 2 diabetes and 33 matched (gender, age, BMI) nondiabetic controls. Cross-over test-drink "| __truncated__
#>  $ dose_range    : chr "Acetaminophen 1.5 g oral (1-min infusion into stomach in Study A via a nasogastric tube; bolus in Studies B / C"| __truncated__
#>  $ regions       : chr "Denmark (Studies A-D conducted in Copenhagen / Hellerup); fits performed in Sweden (Uppsala)."
#>  $ notes         : chr "Subject demographics, study design, and assessment times come from Table 1 of Guiastrennec 2016. Subject counts"| __truncated__

Source trace

The per-parameter origin is recorded in the model file’s ini() block. The table below collects them for quick review.

Equation / parameter Value Source location
lcl (paracetamol CL/F) log(0.441) Table 3, Gastric-emptying column
lvc (paracetamol VC/F) log(19.5) Table 3, Gastric-emptying column
lq (paracetamol Q/F) log(1.56) Table 3, Gastric-emptying column
lvp (paracetamol VP/F) log(48.1) Table 3, Gastric-emptying column
lfdepot (paracetamol F1, FIXED) log(1) Table 3 footnote: F1 = 1 fixed
lhl_ka (HL-KA, frequentist prior Hens 2008) log(8.19) Table 3 footnote a
lkg0 (baseline GE rate KG0) log(1.06) Table 3, Gastric-emptying column
lkul (upper- to lower-SI calorie rate KUL) log(0.0266) Table 3, Gastric-emptying column
ramax (saturable nutrient absorption Vmax, FIXED) 2.292 Table 3, FIXED from Hens 2008 (glucose)
km (saturable nutrient absorption Km, FIXED) 25.12 Table 3, FIXED from Hens 2008 (glucose)
slpcal (caloric feedback, sign negative) -0.0173 Table 3, Gastric-emptying column (Eq. 2)
sig (GE-onset Hill sigmoidicity) 0.285 Table 3, Gastric-emptying column (Eq. 3)
lt50ogtt (T50 for OGTT drinks) log(15.7) Table 3, Gastric-emptying column
lt50fat (T50 for fat-containing drinks) log(23.1) Table 3, Gastric-emptying column
e_sexf_slpcal (sex strengthening of SLPCAL) 0.407 Table 3 and Eq. 4 (+40.7% for females)
lbase_cckf log(0.506) Table 3, CCK column
lbase_cckl log(0.0882) Table 3, CCK column
lpool_cckf log(17.1) Table 3, CCK column
lpool_cckl log(1.95) Table 3, CCK column
lkoutf log(0.355) Table 3, CCK column
lkoutl log(0.00546) Table 3, CCK column
smax_cckf 0.193 Table 3, CCK column
ls50_cckf log(0.592) Table 3, CCK column
slp_cckl 0.0377 Table 3, CCK column
lkdj (FIXED from Hens 2008) log(0.0833) Table 3 fixed value
lkji (FIXED from Hens 2008) log(0.0111) Table 3 fixed value
potfatc (FIXED reference) 100 Table 3, CCK column
potprotc (FIXED at 0, not supported) 0 Table 3, CCK column
potcarbc 10.1 Table 3, CCK column
e_t2dm_potcarbc (T2D depression) -0.811 Table 3, CCK column (Eq. 5)
lbase_bile log(36.3) Table 3, GBE column
lkrb log(0.0618) Table 3, GBE column
smax_bile 6.62 Table 3, GBE column (Eq. 6 framework)
ls50_bile log(5.52) Table 3, GBE column
potfatb (FIXED reference) 100 Table 3, GBE column
potprotb 67.9 Table 3, GBE column
potcarbb 2.25 Table 3, GBE column
e_wt_base_bile (WT-deviation, ref 88 kg) 0.0119 /kg Table 3, GBE column (Eq. 7)
e_age_s50_bile (AGE-deviation, ref 58 y) 0.0215 /yr Table 3, GBE column (Eq. 8)
propSd (paracetamol Cc residual) 0.148 Table 3, Gastric-emptying column
propSd_TCCK (total CCK residual) 0.295 Table 3, CCK column
addSd_GVol / propSd_GVol (combined GBE residual) 2.33 / 0.0766 Table 3, GBE column
ODE form (paracetamol stomach -> upper_si -> central) n/a Methods, “GE model” paragraph; Figure 1 schematic
ODE form (per-nutrient parallel tracks) n/a Figure 1 schematic; see Errata
CCK precursor + plasma indirect response n/a Methods, “CCK model” paragraph
GBE indirect response, no recirculation n/a Methods, “GBE model” paragraph

Virtual cohort

Original observed data are not publicly available. The figures below simulate three contrasting test-drink occasions from the paper (water from Study A, 75 g OGTT from Study B, and a 40 g high-fat isocaloric drink from Study C; demographics fixed at the pooled medians for a nondiabetic adult male). All three occasions co-administer 1.5 g of acetaminophen as the gastric-emptying tracer.

set.seed(2026)

build_events <- function(label, dose_acet, dose_fat, dose_prot, dose_carb,
                         drink_ogtt, drink_fat, t2dm = 0, wt = 88, age = 58,
                         sexf = 0, id = 1L, times = seq(0, 240, by = 5)) {
  ev <- rxode2::et() |>
    rxode2::et(amt = dose_acet, cmt = "stomach",   time = 0) |>
    rxode2::et(amt = dose_fat,  cmt = "stom_fat",  time = 0) |>
    rxode2::et(amt = dose_prot, cmt = "stom_prot", time = 0) |>
    rxode2::et(amt = dose_carb, cmt = "stom_carb", time = 0) |>
    rxode2::et(times,           cmt = "Cc")
  ev <- as.data.frame(ev)
  ev$id         <- id
  ev$WT         <- wt
  ev$AGE        <- age
  ev$SEXF       <- sexf
  ev$T2DM       <- t2dm
  ev$DRINK_OGTT <- drink_ogtt
  ev$DRINK_FAT  <- drink_fat
  ev$cohort     <- label
  ev
}

events <- dplyr::bind_rows(
  build_events("Water (Study A)",
               dose_acet = 1500, dose_fat = 0, dose_prot = 0, dose_carb = 0,
               drink_ogtt = 0, drink_fat = 0, id = 1L),
  build_events("OGTT 75 g (Study B)",
               dose_acet = 1500, dose_fat = 0, dose_prot = 0, dose_carb = 75,
               drink_ogtt = 1, drink_fat = 0, id = 2L),
  build_events("High-fat 40 g (Study C)",
               dose_acet = 1500, dose_fat = 40, dose_prot = 3, dose_carb = 32,
               drink_ogtt = 0, drink_fat = 1, id = 3L)
)
stopifnot(!anyDuplicated(unique(events[, c("id", "time", "evid")])))

Simulation

Typical-value simulation (between-subject variability zeroed out) reproduces the median responses for each test drink.

mod         <- rxode2::rxode2(readModelDb("Guiastrennec_2016_gastric_emptying"))
#>  parameter labels from comments will be replaced by 'label()'
mod_typical <- rxode2::zeroRe(mod)
sim <- rxode2::rxSolve(mod_typical, events,
                       keep = c("cohort"),
                       returnType = "tibble")
#>  omega/sigma items treated as zero: 'etalvc', 'etalfdepot', 'etalkg0', 'etalkul', 'etalslpcal_mag', 'etalbase_cckf', 'etalpool_cckl', 'etalkoutf', 'etalbase_bile', 'etalkrb', 'etals50_bile'
#> Warning: multi-subject simulation without without 'omega'
sim$cohort <- factor(sim$cohort,
                     levels = c("Water (Study A)",
                                "OGTT 75 g (Study B)",
                                "High-fat 40 g (Study C)"))

Replicate published figures

# Replicates Figure 2 (top): acetaminophen plasma concentration time
# course, stratified by test drink. The published median curves rise
# fastest for water, more slowly for OGTT, and slowest for high-fat
# drinks - matching the GE-feedback structure.
ggplot(sim, aes(time, Cc, colour = cohort)) +
  geom_line(linewidth = 1) +
  labs(x = "Time (min)",
       y = "Acetaminophen Cc (umol/L)",
       colour = "Test drink",
       title = "Figure 2 (top) - acetaminophen Cc by test drink",
       caption = "Typical-value (zeroRe) simulation; matches the qualitative ordering of the paper's Figure 2 (top).") +
  theme_minimal()

# Replicates Figure 2 (bottom): gallbladder volume time course. The
# fat-containing drink drives a substantial gallbladder ejection; the
# OGTT drink drives a small ejection; water leaves the volume at
# baseline.
ggplot(sim, aes(time, GVol, colour = cohort)) +
  geom_line(linewidth = 1) +
  labs(x = "Time (min)",
       y = "Gallbladder volume (mL)",
       colour = "Test drink",
       title = "Figure 2 (bottom) - gallbladder volume by test drink",
       caption = "Typical-value (zeroRe) simulation.") +
  theme_minimal()

# Total plasma CCK time course (paper Supplementary Material S3). The
# high-fat drink drives the largest postprandial CCK rise via the
# duodenal Emax stimulus; the OGTT drink drives a small rise; water
# stays at baseline.
ggplot(sim, aes(time, TCCK, colour = cohort)) +
  geom_line(linewidth = 1) +
  labs(x = "Time (min)",
       y = "Total plasma CCK (pmol/L)",
       colour = "Test drink",
       title = "Postprandial total CCK by test drink",
       caption = "Typical-value (zeroRe) simulation.") +
  theme_minimal()

PKNCA validation

PKNCA on the acetaminophen Cc, stratified by test drink. The CCK and gallbladder volume outputs are not standard NCA targets (they describe mechanistic responses rather than drug PK), and the paper’s preferred summaries for them - time-to-50%-gastric-emptying TGE50 and gallbladder ejection fraction EF% - are reported separately in the next section.

nca_data <- sim |>
  dplyr::filter(time > 0, !is.na(Cc)) |>
  dplyr::transmute(id = cohort, time, Cc)
dose_data <- sim |>
  dplyr::filter(time == 0) |>
  dplyr::distinct(cohort, .keep_all = TRUE) |>
  dplyr::transmute(id = cohort, time = 0, amt = 1500)

conc_obj <- PKNCA::PKNCAconc(nca_data, Cc ~ time | id)
dose_obj <- PKNCA::PKNCAdose(dose_data, amt ~ time | id)
intervals <- data.frame(start = 0, end = 240,
                        cmax = TRUE, tmax = TRUE,
                        auclast = TRUE, half.life = TRUE)
nca_res <- PKNCA::pk.nca(PKNCA::PKNCAdata(conc_obj, dose_obj, intervals = intervals))
#> Warning: Requesting an AUC range starting (0) before the first measurement (5) is not allowed
#> Requesting an AUC range starting (0) before the first measurement (5) is not allowed
#> Requesting an AUC range starting (0) before the first measurement (5) is not allowed
knitr::kable(summary(nca_res),
             caption = "Acetaminophen NCA (simulated typical-value, by test drink).")
Acetaminophen NCA (simulated typical-value, by test drink).
start end N auclast cmax tmax half.life
0 240 3 NC 132 [27.7] 55.0 [15.0, 80.0] 121 [0.112]

Derived summaries: TGE50 and gallbladder ejection fraction

The paper’s preferred mechanistic summaries are the time to empty 50% of the stomach content (TGE50) and the gallbladder ejection fraction (EF%, Eq. 1 of the paper). These quantities derive from the simulated trajectories rather than from NCA.

# TGE50 derived from the stomach acetaminophen amount (the per-cohort
# initial dose is 1500 mg into the stomach; the trajectory falls
# monotonically until KG drains it past 50% of the initial value).
tge50 <- sim |>
  group_by(cohort) |>
  summarise(
    initial_stomach = max(stomach, na.rm = TRUE),
    tge50_min       = approx(stomach / max(stomach), time,
                             xout = 0.5)$y,
    .groups         = "drop"
  )
knitr::kable(tge50,
             caption = "Time to empty 50% of the stomach content (TGE50, min).")
Time to empty 50% of the stomach content (TGE50, min).
cohort initial_stomach tge50_min
Water (Study A) 1500 2.512542
OGTT 75 g (Study B) 1500 20.941369
High-fat 40 g (Study C) 1500 33.733577

ef_pct <- sim |>
  group_by(cohort) |>
  summarise(
    base_volume = first(GVol),
    min_volume  = min(GVol, na.rm = TRUE),
    ef_pct      = 100 * (first(GVol) - min(GVol, na.rm = TRUE)) /
                  first(GVol),
    .groups     = "drop"
  )
knitr::kable(ef_pct,
             caption = "Gallbladder ejection fraction (EF%, Eq. 1 of the paper).")
Gallbladder ejection fraction (EF%, Eq. 1 of the paper).
cohort base_volume min_volume ef_pct
Water (Study A) 36.3 36.30000 0.00000
OGTT 75 g (Study B) 36.3 30.44866 16.11939
High-fat 40 g (Study C) 36.3 10.81284 70.21256

The paper reports a simulation-based TGE50 of 3.8 min (PI95 0.2-15) for water and up to 96 min (PI95 64-133) for a high-fat drink, with gallbladder ejection fraction up to 65% (PI95 37-82) for the high-fat drink (Results). The typical-value simulation above falls within these prediction-interval envelopes for the three test drinks shown.

Assumptions and deviations

  • Joint-model topology - parallel nutrient tracks. Figure 1 of the paper shows three separate first-order rate constants for nutrient disappearance: KUL (from the “upper SI”, driving the GE feedback), KDJ (from the “duodenum”, driving CCKF and GBE), and KJI (from the “upper jejunum”, driving CCKL). All three are reported as separate parameters in Table 3 (KUL estimated in the GE column; KDJ and KJI fixed from Hens 2008 and shared between the CCK and GBE columns). The literal reading of Figure 1 + Table 3 is therefore that the joint model carries three parallel nutrient signal tracks downstream of the stomach - an upper-SI track (drains via KUL) for the GE caloric feedback, and a duodenum-then-upper-jejunum chain (drains via KDJ then KJI) for the CCK and GBE signals. This is what the packaged model implements: each per-macronutrient stomach state feeds both downstream tracks simultaneously, with the same KG rate exit. Mass is not strictly conserved across the parallel tracks (each track is a delayed signal representation of the stomach content rather than a physical mass-balance compartment). The alternative interpretation - a single chain stomach -> duodenum -> upper jejunum with the GE feedback reading the lumped duodenum + upper jejunum content - cannot be ruled out without the Supplementary Material S1 (NONMEM control code), which is not on disk in this worktree and not retrievable from open-access mirrors. Operators who can access S1 are encouraged to confirm the topology choice and (if warranted) refit the model into a single-chain form; the parameter estimates from Table 3 carry over directly under either topology.
  • Covariate-effect reference values. The WT-BASEBILE (+1.19%/kg) and AGE-S50_BILE (+2.15%/yr) effects are linear-deviation forms that require a reference value (WT_ref, AGE_ref) for the deviation to be zero. The paper reports the effect coefficients in Table 3 but does not state the reference values explicitly; the natural NONMEM default is the population mean / median, which from Table 1 of the paper is approximately WT = 88 kg and AGE = 58 y across the pooled Studies A-C. The Supplementary Material S1 control stream would disambiguate the reference values; if S1 is later inspected, re-fitting the typical-value BASEBILE and S50_BILE may be required to keep the cohort-mean predictions aligned with the paper.
  • SLPCAL parameterisation. SLPCAL is a negative slope on the linear-scale gastric-emptying feedback (-0.0173 /kcal in Table 3). The packaged model encodes |SLPCAL| as exp(lslpcal_mag) so the reported 19% CV between-subject variability can be carried as a standard log-normal eta on the magnitude (with the sign fixed negative in model()). This is equivalent to the NONMEM idiom SLPCAL_i = -THETA(SLPCAL) * exp(ETA(SLPCAL)) and preserves the paper’s interpretation that the caloric feedback is always inhibitory of GE.
  • GE-onset gating for water. The paper reports that no onset delay was needed for the Study A water arm. The packaged model implements this as onset = 1 for DRINK_OGTT = DRINK_FAT = 0 (a branch in the model onset expression that adds back to 1 when both binary indicators are zero), so simulating water reproduces the paper’s immediate-onset behaviour without further parameter changes.
  • TAD via raw model time. The Hill onset function uses time after dose. Because the study protocol co-administers the test drink and acetaminophen at t = 0 (an oral dose for Studies B-D, a 1-min nasogastric infusion for Study A), the packaged model uses the raw model time variable t as a stand-in for TAD. Downstream users dosing at a nonzero time should shift the data so t = 0 corresponds to the dose time, or modify the model to use the rxode2 tad() function with a registered dose compartment.
  • IPPSE coupling not simulated. The paper used an individual pharmacokinetic-parameters-with-standard-errors (IPPSE) approach in which the per-subject KG estimates from the GE submodel fit were carried into the CCK and GBE submodel fits. In simulation from the packaged model, KG is sampled from the population distribution (BSV 155% CV via etalkg0) and is therefore not perfectly individualised to the simulated subject’s nutrient amounts. This is a standard simplification when a single joint model is simulated forward; the typical-value predictions are unaffected.
  • Supplementary Material S2 (parameter shrinkages) not on disk. The paper’s S2 reports shrinkage diagnostics for the etas; these are not parameter values that drive the simulation but help interpret the realism of the BSV distributions. They are not embedded in the packaged model.
  • Acetaminophen molecular weight 151.17 g/mol. The paracetamol-amount-to-uM (= umol/L) conversion uses the standard IUPAC molecular weight; the paper does not report the conversion constant explicitly.