ODE Model with Absorption Lag Time
This example demonstrates the LAGTIME keyword in [individual_parameters] for ODE models. The complete model file is examples/warfarin_ode_lagtime.ferx.
When to use
Use LAGTIME (ODE version) when: - The drug shows a measurable delay between dosing and the rise in concentration - You want IIV on the lag time itself (log-normal, like CL or V) - Your structural model is ODE-based and you cannot use the analytical solver’s lagtime= argument
For analytical models the lag time is passed directly to the solver:
pk one_cpt_oral(cl=CL, v=V, ka=KA, lagtime=TLAG)
For ODE models the equivalent is to assign LAGTIME in [individual_parameters] — the event handler delays every dose event for that subject by the individual lag time, without any change to the ODE equations.
Model file
This is the contents of examples/warfarin_ode_lagtime.ferx:
[parameters]
theta TVCL(0.134, 0.001, 10.0)
theta TVV(8.1, 0.1, 500.0)
theta TVKA(1.0, 0.01, 50.0)
theta TVLAG(0.5, 0.01, 5.0)
omega ETA_CL ~ 0.07
omega ETA_V ~ 0.02
omega ETA_KA ~ 0.40
omega ETA_LAG ~ 0.09
sigma PROP_ERR ~ 0.01 (sd)
[individual_parameters]
CL = TVCL * exp(ETA_CL)
V = TVV * exp(ETA_V)
KA = TVKA * exp(ETA_KA)
LAGTIME = TVLAG * exp(ETA_LAG)
[structural_model]
ode(obs_cmt=central, states=[depot, central])
[odes]
d/dt(depot) = -KA * depot
d/dt(central) = KA * depot - CL/V * central
[scaling]
obs_scale = V
[error_model]
DV ~ proportional(PROP_ERR)
[fit_options]
method = focei
maxiter = 500
covariance = true
Two features work together here:
LAGTIMEin[individual_parameters]: assigning to the reserved nameLAGTIMEdelays every dose event for that subject. No keyword in[structural_model]or[odes]is needed.obs_scale = V: the ODE statecentralis in amounts (mg), butDVis in concentrations (mg/L). Dividing by V converts amount to concentration before the residual is formed. This is equivalent to writingd/dt(central) = KA * depot / V - (CL/V) * centraland using a concentration-scale ODE state.
Running the fit
ferx examples/warfarin_ode_lagtime.ferx --data data/warfarin_ode_lagtime.csvOr via the Rust API:
let result = fit_from_files("examples/warfarin_ode_lagtime.ferx", "data/warfarin_ode_lagtime.csv")?;
println!("TVLAG = {:.3} h", result.theta["TVLAG"].estimate);
// Individual lag times are in result.individual_estimates["LAGTIME"]Interpreting output
The fit YAML includes TVLAG in the theta: section:
theta:
TVCL:
estimate: 0.134
...
TVLAG:
estimate: 0.489
se: 0.041
rse_pct: 8.4Individual LAGTIME values are returned in FitResult.individual_estimates["LAGTIME"] alongside CL, V, and KA.
Tips
- IIV on LAGTIME: log-normal IIV (
ETA_LAG ~ 0.09) is natural because lag times must be positive. Starting variance 0.09 corresponds to ~30% CV. - LAGTIME vs. transit compartments: a lag time is appropriate when there is an identifiable delay before absorption starts. Transit compartments (see Transit Absorption) are better when the delay distributes over time (smooth absorption rise). The two approaches are not equivalent.
- Negative DV at lag time: if DV observations fall before the estimated lag time for a subject, IPRED is zero at those times, which produces large IWRES. Consider filtering with
[data_selection]or re-examining the lag-time prior. - Amount vs. concentration ODE states: using
obs_scale = V(as in this example) keeps the ODE equations in amount units and is marginally faster than dividing every flux by V inside the ODE.