flowchart LR
A["Single dose<br/>profile (0→∞)"] --> B["Shift by τ,<br/>shift by 2τ, ..."]
B --> C["Sum contributions<br/>at each time t"]
C --> D["Predicted SS<br/>profile (0→τ)"]
9 Superposition
9.1 What superposition does
Superposition predicts steady-state concentration-time profiles from a single-dose profile by linearly summing lagged copies of the single-dose curve. This is the NCA equivalent of multi-dose simulation, and it requires no compartmental model. Observed steady-state parameters (Cav, PTR, fluctuation) are covered in Multiple-Dose and Steady-State; estimating when steady state is reached is covered in Time to Steady State.
The core assumption is linear, time-invariant pharmacokinetics: the drug does not accumulate non-linearly, and clearance and volume do not change over time. See also the package vignette Superposition of Pharmacokinetic Data.
9.2 Basic superposition
Theoph records dose in mg/kg; converting it to each subject’s total dose in mg gives the value that dose.input expects:
d_conc <- as.data.frame(Theoph) |> rename(time = Time, subject = Subject)
# Theoph dose is mg/kg — multiply by body weight for the total mg dose
d_dose <- Theoph |> as.data.frame() |>
group_by(Subject) |>
summarise(dose = Dose[1] * Wt[1], .groups = "drop") |>
rename(subject = Subject) |>
mutate(time = 0)# Use Theoph subject 1 as single-dose reference
subj1 <- d_conc |> filter(subject == "1")
actual_dose <- d_dose |> filter(subject == "1") |> pull(dose)
o_conc_1 <- PKNCAconc(subj1, conc ~ time | subject)
# Profile during the dosing interval after the 3rd dose (τ = 24 h)
ss_profile <- superposition(
o_conc_1,
tau = 24, # dosing interval (h)
dose.input = actual_dose, # dose used to generate the single-dose data
dose.amount = actual_dose, # dose to simulate at SS (same here)
n.tau = 3, # simulate 3 intervals
check.blq = FALSE # Theoph has non-zero first sample
)
head(ss_profile, 12)# A tibble: 12 × 3
subject conc time
<ord> <dbl> <dbl>
1 1 5.12 0
2 1 7.17 0.25
3 1 8.54 0.370
4 1 10.8 0.57
5 1 14.7 1.12
6 1 13.6 2.02
7 1 12.2 3.82
8 1 11.8 5.1
9 1 10.6 7.03
10 1 9.72 9.05
11 1 8.38 12.1
12 1 4.71 24
9.3 Visualizing the approach to steady state
With a finite n.tau, superposition() returns the concentration profile over the dosing interval after that many doses; with n.tau = Inf it accumulates doses until the profile converges to steady state (within steady.state.tol). Overlaying the profiles after 1, 2, and 3 doses on the converged profile makes the approach to steady state visible.
approach <- lapply(1:3, function(k) {
superposition(o_conc_1, tau = 24, dose.input = actual_dose,
dose.amount = actual_dose, n.tau = k, check.blq = FALSE) |>
mutate(profile = paste("After dose", k))
}) |> bind_rows()
ss_converged <- superposition(
o_conc_1,
tau = 24,
dose.input = actual_dose,
dose.amount = actual_dose,
n.tau = Inf,
check.blq = FALSE
)
bind_rows(approach, ss_converged |> mutate(profile = "Steady state")) |>
mutate(profile = factor(profile, levels = c(paste("After dose", 1:3), "Steady state"))) |>
ggplot(aes(x = time, y = conc, colour = profile)) +
geom_line() +
geom_point(size = 1.5) +
labs(title = "Superposition: approach to steady state (τ = 24 h)",
x = "Time within interval (h)", y = "Predicted concentration (mg/L)",
colour = NULL) +
theme_minimal()
9.4 Changing dose at steady state
To predict the effect of a dose change, set dose.amount to the new dose while keeping dose.input as the original dose. PKNCA scales the concentrations proportionally.
ss_half_dose <- superposition(
o_conc_1,
tau = 24,
dose.input = actual_dose,
dose.amount = actual_dose / 2, # half dose
n.tau = Inf,
check.blq = FALSE
)
bind_rows(
ss_converged |> mutate(regimen = "Full dose"),
ss_half_dose |> mutate(regimen = "Half dose")
) |>
ggplot(aes(x = time, y = conc, colour = regimen)) +
geom_line() + geom_point(size = 2) +
labs(title = "Dose scaling via superposition",
x = "Time (h)", y = "Predicted SS concentration (mg/L)") +
scale_colour_manual(values = c("Full dose" = "steelblue", "Half dose" = "coral")) +
theme_minimal()
9.5 Changing the dosing interval (τ)
Shortening τ to 8 h raises troughs and reduces peak-to-trough fluctuation:
ss_8h <- superposition(
o_conc_1,
tau = 8,
dose.input = actual_dose,
dose.amount = actual_dose,
n.tau = Inf,
check.blq = FALSE
)
ggplot(ss_8h, aes(x = time, y = conc)) +
geom_line(colour = "firebrick") +
geom_point(size = 2, colour = "firebrick") +
labs(title = "Superposition with τ = 8 h (more frequent dosing)",
x = "Time within interval (h)", y = "Predicted SS concentration (mg/L)") +
theme_minimal()
9.6 Multiple doses within an interval
dose.times (default 0) sets the dose time(s) within each interval, and a vector places several doses inside one τ: tau = 24 with dose.times = c(0, 12) is twice-daily dosing described as a single 24 h interval. dose.amount applies to each administration, so halving it here keeps the total daily dose equal to the once-daily regimen above:
ss_bid <- superposition(
o_conc_1,
tau = 24,
dose.input = actual_dose,
dose.amount = actual_dose / 2, # half dose per administration
dose.times = c(0, 12), # doses at 0 h and 12 h in each interval
n.tau = Inf,
check.blq = FALSE
)
bind_rows(
ss_converged |> mutate(regimen = "Once daily, full dose"),
ss_bid |> mutate(regimen = "Twice daily, half dose")
) |>
ggplot(aes(x = time, y = conc, colour = regimen)) +
geom_line() + geom_point(size = 2) +
labs(title = "Two doses per interval: dose.times = c(0, 12), τ = 24 h",
x = "Time within interval (h)", y = "Predicted SS concentration (mg/L)",
colour = NULL) +
theme_minimal()
At the same total daily dose, splitting the dose lowers Cmax and raises the trough. The dose times need not be evenly spaced: dose.times = c(0, 8) with tau = 24 describes a staggered morning/evening regimen (doses 8 h apart, then a 16 h overnight gap).
9.7 Extracting accumulation ratio
One common definition of the accumulation ratio (Rac) is Cmax at steady state divided by Cmax after the first dose (an AUC-based Rac, AUCτ,ss / AUCτ,first, is equally common):
cmax_sd <- max(subj1$conc, na.rm = TRUE)
cmax_ss <- max(ss_converged$conc, na.rm = TRUE)
rac <- cmax_ss / cmax_sd
cat("Accumulation ratio (Rac):", round(rac, 2), "\n")Accumulation ratio (Rac): 1.44
cat("Single-dose Cmax:", round(cmax_sd, 2), "mg/L\n")Single-dose Cmax: 10.5 mg/L
cat("Steady-state Cmax:", round(cmax_ss, 2), "mg/L\n")Steady-state Cmax: 15.1 mg/L
9.8 Key arguments reference
| Argument | Required | Meaning |
|---|---|---|
tau |
Yes | Dosing interval (same time units as data) |
dose.input |
No | Dose that generated the observed profile; supply together with dose.amount to dose-scale |
dose.amount |
No | Dose to simulate; if omitted, the output dose equals the input dose |
dose.times |
No (default 0) |
Dose time(s) within the interval; a vector gives multiple/staggered doses per τ |
n.tau |
No (default Inf) |
Number of intervals to simulate before stopping |
steady.state.tol |
No (default 0.001) | Convergence tolerance for n.tau = Inf |
check.blq |
No (default TRUE) |
Require first concentration = 0; set FALSE if first sample is non-zero |
auc.type |
No (default "AUCinf") |
How to extrapolate the single-dose tail: "AUCinf", "AUClast", or "AUCall" |
additional.times |
No | Extra timepoints to include in the output |
Interpolation and tail extrapolation follow the dose-aware interpolation methods.
Limitation: Superposition assumes linear PK. It should not be used for drugs with non-linear kinetics (e.g. Michaelis-Menten elimination, time-varying clearance, or saturable protein binding).