Example: Transit absorption + two-compartment ODE

This example fits an ODE-based two-compartment model with a three-transit absorption chain and allometric scaling. It demonstrates how to combine several features in one model: transit compartments, a lag time, allometric covariate scaling, and a multi-state ODE system.

The transit chain is the standard Savic (2007) parameterization: KTR = (n + 1) / MTT where n = 3 transit compartments, so KTR = 4 / MTT.

Model structure

State CMT Role
transit1 1 Oral dose lands here; lag time applied
transit2 2 Transit
transit3 3 Transit
central 4 Observation compartment (concentration, mg/L)
peripheral 5 Peripheral distribution

ODEs:

d/dt(transit1)   = -KTR * transit1
d/dt(transit2)   =  KTR * transit1 - KTR * transit2
d/dt(transit3)   =  KTR * transit2 - KTR * transit3
d/dt(central)    =  KA  * transit3 / V1 - (CL/V1 + Q/V1) * central + Q/V2 * peripheral
d/dt(peripheral) =  Q   * central  / V1 - Q/V2 * peripheral

Allometric scaling uses a reference body weight of 70 kg:

CL = TVCL * (WT / 70)^0.75 * exp(ETA_CL)
V1 = TVV1 * (WT / 70)^1.00 * exp(ETA_V1)
V2 = TVV2 * (WT / 70)^1.00

Model file

[parameters]
  theta TVCL(5.0,    0.1,  100.0)
  theta TVV1(50.0,   5.0,  500.0)
  theta TVQ(10.0,    0.1,  200.0)
  theta TVV2(100.0,  5.0, 1000.0)
  theta TVMTT(1.0,   0.1,   10.0)

  omega ETA_CL ~ 0.09
  omega ETA_V1 ~ 0.09
  omega ETA_KA ~ 0.09

  sigma PROP_ERR ~ 0.30 (sd)

[individual_parameters]
  CL      = TVCL * (WT / 70)^0.75 * exp(ETA_CL)
  V1      = TVV1 * (WT / 70)^1.00 * exp(ETA_V1)
  Q       = TVQ
  V2      = TVV2 * (WT / 70)^1.00
  KTR     = 4.0 / TVMTT
  KA      = KTR * exp(ETA_KA)

[structural_model]
  ode(obs_cmt=central, states=[transit1, transit2, transit3, central, peripheral])

[odes]
  d/dt(transit1)   = -KTR * transit1
  d/dt(transit2)   =  KTR * transit1 - KTR * transit2
  d/dt(transit3)   =  KTR * transit2 - KTR * transit3
  d/dt(central)    =  KA  * transit3 / V1 - (CL / V1 + Q / V1) * central + Q / V2 * peripheral
  d/dt(peripheral) =  Q   * central  / V1 - Q / V2 * peripheral

[error_model]
  DV ~ proportional(PROP_ERR)

[fit_options]
  method     = focei
  maxiter    = 500
  covariance = true

The ODE states are listed in [structural_model] in the order they receive doses (CMT 1 = transit1 is the dosing compartment). The obs_cmt=central argument tells ferx which state to predict against DV.

Running

library(ferx)

ex  <- ferx_example("transit_2cpt")
fit <- ferx_fit(ex$model, ex$data)
fit

# Inspect the parsed structure
ferx_model_inspect(fit)

Dataset requirements

The data file must include a WT covariate column (body weight in kg); it is referenced directly in [individual_parameters]. Dose rows target CMT=1 (transit1).

The transit chain provides the absorption delay via MTT — no separate lag time parameter is needed in this model. To add a true pre-transit lag on top of the transit delay, declare a theta TVLAG and wire it in:

LAGTIME = TVLAG

[structural_model]
  ode(obs_cmt=central, states=[transit1, ...], lagtime=LAGTIME)

Tips

  • Number of transit compartments: the model is easy to extend. Change the states=[...] list, add corresponding ODE equations, and update KTR = (n+1) / TVMTT with the new n.
  • Allometric exponents: 0.75 on CL (three-quarter power law) and 1.0 on V are the standard physiological allometry values. Override with estimated thetas if the dataset supports it.
  • BSV on Q/V2: Q and V2 carry no BSV here. Add ETAs if peripheral samples are available and residuals show systematic subject-level bias.

See also