Aztreonam-avibactam (Xie 2025)
Source:vignettes/articles/Xie_2025_aztreonam_avibactam.Rmd
Xie_2025_aztreonam_avibactam.RmdModel and source
Citation: Xie R, Rogers H, Chow JW, Soto E, Raber SR. Population pharmacokinetic/pharmacodynamic modeling to optimize aztreonam-avibactam dose regimens for adult patients. Antimicrob Agents Chemother. 2025;69(8):e01950-24. doi:10.1128/aac.01950-24. All fixed-effect, random-effect and residual-error estimates are the ‘Final model (run 46)’ column of supplementary Table S3. The functional FORMS of the covariate relationships are not printed in Xie 2025; they are taken from the immediate structural predecessor by the same group, Das S, Riccobene T, Carrothers TJ, Wright JG, MacPherson M, Cristinacce A, McFadyen L, Xie R, Luckey A, Raber S. Dose selection for aztreonam-avibactam, including adjustments for renal impairment, for Phase IIa and Phase III evaluation. Eur J Clin Pharmacol. 2024;80(4):529-543. doi:10.1007/s00228-023-03609-x, supplementary Tables 4 and 5.
Description: Simultaneous four-compartment (two compartments per drug) population PK model for the fixed-ratio (3:1) aztreonam-avibactam combination, fitted jointly to 4,914 aztreonam plasma samples from 431 subjects and 18,222 avibactam plasma samples from 2,635 subjects pooled across Phase 1, Phase 2a and Phase 3 aztreonam-avibactam studies plus the ceftazidime-avibactam development program (Xie 2025). Both drugs are given as zero-order intravenous infusions with first-order elimination. Body weight is an allometric covariate on CL, Vc, Q and Vp of both drugs with fixed exponents. Time-varying BSA-normalized creatinine clearance acts on the clearance of both drugs through a hinged relationship: a power function below 80 mL/min/1.73 m^2 and a linear function at or above it. Infection type shifts clearance and central volume, and avibactam additionally carries end-stage-renal-disease, hemodialysis, Chinese-race and APACHE II severity terms. The two drugs are linked by a four-way OMEGA block across aztreonam and avibactam CL and Vc, whose cross-drug correlations are 0.98 and 0.99. Aztreonam is the unsuffixed parent; avibactam carries the sibling-drug suffix _avi throughout.
Article: https://doi.org/10.1128/aac.01950-24
Supplement (Tables S1-S11, Figures S1-S6): available from the article landing page as
aac.01950-24-s0001.docxStructural predecessor (covariate equation forms): https://doi.org/10.1007/s00228-023-03609-x
Aztreonam-avibactam is a fixed-ratio (3:1) monobactam / beta-lactamase-inhibitor combination approved in Europe in 2024 for complicated intra-abdominal infection (cIAI), complicated urinary tract infection (cUTI), hospital-acquired and ventilator-associated pneumonia, and other infections due to aerobic gram-negative organisms with limited treatment options. Xie 2025 added the two adult Phase 3 trials (REVISIT, ASSEMBLE) and two further Phase 1 studies to the previous data set and re-estimated a simultaneous population PK model for both analytes, then used it to confirm dose regimens across renal-function groups, to propose a simplified loading dose, and to derive a regimen for end-stage renal disease.
Both drugs are described by a two-compartment disposition model with zero-order infusion and first-order elimination, so the packaged model is a four-state system. What makes it a simultaneous model rather than two independent ones is the OMEGA block: aztreonam and avibactam clearance and central volume share a single 4x4 covariance block whose cross-drug correlations are 0.98 and 0.99 – “as expected for drugs that are both predominantly renally cleared”, in the paper’s words.
Population
The pooled analysis comprised 4,914 aztreonam plasma samples from 431 subjects and 18,222 avibactam plasma samples from 2,635 subjects. The avibactam data set is far larger because it carries the ceftazidime-avibactam development program in addition to the aztreonam-avibactam studies; this asymmetry is load-bearing for the covariate model, since cUTI is estimable for avibactam (707 subjects) but not for aztreonam (3 subjects), so several covariates are avibactam-only by necessity rather than by biology.
Subjects spanned 18-89 years and 28-190 kg, 38.6% female, and renal function from augmented renal clearance to end-stage renal disease. Baseline characteristics are in supplementary Tables S1 (aztreonam) and S2 (avibactam).
| Field | Value |
|---|---|
| Species | human |
| Subjects | 2,635 |
| Concentrations | 23,136 |
| Age | 18-89 years |
| Weight | 28-190 kg |
| Female | 38.6% |
| Disease states | healthy, cIAI, HAP/VAP, cUTI, bloodstream infection |
| Renal function | augmented renal clearance through end-stage renal disease |
Source trace
Every ini() value is the “Final model (run 46)” column
of supplementary Table S3. The covariate equation forms are not
printed anywhere in Xie 2025, so they are taken from the immediate
structural predecessor by the same author group, Das 2024 (supplementary
Tables 4 and 5), which prints them verbatim.
| Equation / parameter | Value | Source location |
|---|---|---|
lcl, lvc, lq,
lvp (aztreonam) |
5.00 L/h, 7.01 L, 9.41 L/h, 6.12 L | Xie 2025 Table S3, theta1-theta4 |
lcl_avi, lvc_avi, lq_avi,
lvp_avi
|
10.3 L/h, 12.6 L, 4.82 L/h, 6.95 L | Xie 2025 Table S3, theta5-theta8 |
lcl_dial_avi |
17.9 L/h | Xie 2025 Table S3, theta12 (CL_AVI_DIAL) |
e_wt_cl_q, e_wt_vc_vp
|
0.75, 1 (both fixed) | Xie 2025 Results, “allometric scaling with fixed exponents of 0.75 on CL and Q, and 1 on Vc and Vp” |
e_crcl_cl_lt80, e_crcl_cl_ge80
|
0.502, 0.00383 | Xie 2025 Table S3, theta9 / theta15 |
e_crcl_cl_lt80_avi,
e_crcl_cl_ge80_avi
|
1.06, 0.00313 | Xie 2025 Table S3, theta10 / theta16 |
| form of the nCrCL hinge |
(nCrCL/80)^p below 80; 1 + s*(nCrCL-80)
at/above 80 |
Das 2024 Table S5, rows “theta7: CL, (CrCL/80)**theta7, CrCL <80” and “theta8: CL, (1+theta8*(CrCL-80)), CrCL >=80” |
e_renalimp_esrd_cl_avi |
-0.923 | Xie 2025 Table S3, theta11 |
| ESRD replaces rather than multiplies the nCrCL arm | n/a | Das 2024 Table S4, “theta8: CL, (nCrCL/80)**theta8, nCrCL <80 mL/min and not ESRD” |
e_ciai_ph2_cl_avi, e_ciai_ph2_vc_avi
|
0.89, 1.64 | Xie 2025 Table S3, theta13 / theta14 (Study2002) |
e_cuti_vc_avi, e_cuti_cl_avi
|
1.5, 0.222 | Xie 2025 Table S3, theta24 / theta26 |
e_ciai_habp_vabp_vc |
0.931 | Xie 2025 Table S3, theta25 (cIAI/NP on Vc); Results,
“same cIAI and NP (HAP/VAP) effect on aztreonam and avibactam Vc” |
e_ciai_cl_avi, e_ciai_cl
|
0.115, 0.279 | Xie 2025 Table S3, theta27 / theta31 |
e_race_chinese_vc_avi |
-0.145 | Xie 2025 Table S3, theta29 (China on Vc_AVI) |
e_apache_ii_sev_cl_avi |
-0.118 | Xie 2025 Table S3, theta30 |
proportional X*(1 + theta) form of the categorical
effects |
n/a | Das 2024 Table S5, e.g. “theta11: Population effect on Vc (cUTI), Vc*(1+theta11)” |
| 4x4 OMEGA block (CL/Vc of both drugs) | 40.2 / 60.8 / 43.5 / 65.2 %CV, six covariances | Xie 2025 Table S3, IIV and cov rows |
| Q-Vp avibactam OMEGA block | 31.4 / 17.7 %CV, cov 0.0309 | Xie 2025 Table S3; Results, “a covariance term between the avibactam IIVs in Q and Vp” |
| Residual error (4 aztreonam proportional strata, 1 avibactam) | 12.5 / 22.4 / 40.3 / 53.3%, 20% | Xie 2025 Table S3, theta17-theta23 |
| Unbound fractions used for the PK/PD targets | 0.616 (ATM), 0.92 (AVI) | Xie 2025 Methods, “Predicted exposures for phase 3 trial patients” |
| Joint PK/PD target | 60% fT>MIC 8 mg/L and 50% fT>CT 2.5 mg/L | Xie 2025 Introduction and Table 5 footnote |
Structural checks
Before comparing against published exposures, three checks confirm the encoding itself. Each is a strict identity that does not depend on any covariate distribution, so it can only pass if the structure is right.
mod <- readModelDb("Xie_2025_aztreonam_avibactam")
typ <- rxode2::zeroRe(mod)
base_cov <- function(...) {
out <- data.frame(
WT = 70, CRCL = 100,
DIS_CIAI = 0, DIS_HABP = 0, DIS_VABP = 0, DIS_CUTI = 0,
RENALIMP_ESRD = 0, RRT_HEMODIAL_STATUS = 0, RACE_CHINESE = 0,
APACHE_II_SEV = 0, STUDY_CIAI_PH2 = 0,
STUDY_AZTAVI_PHASE2 = 0, STUDY_AZTAVI_PHASE3 = 0, STUDY_AZTAVI_PHASE23 = 0
)
rep <- list(...)
for (nm in names(rep)) out[[nm]] <- rep[[nm]]
out
}
# Solve helper: doses cover the whole observation window, and observation rows
# carry cmt = the OBSERVABLE name. This model declares two endpoints (Cc and
# Cc_avi), so rxode2 maps them to modelled compartments 5 and 6 and REQUIRES the
# observable name on observation rows; cmt = "central" errors with
# "'dvid'->'cmt' ... on a undefined compartment". The four ODE states keep slots
# 1-4, so no slot renumbering occurs. See the Errata.
solve_regimen <- function(model, cov, atm, avi, ii, t_start, t_end, by = 0.1) {
dose_times <- seq(0, t_end - ii, by = ii)
dose <- bind_rows(
tidyr::expand_grid(cov, time = dose_times) |>
mutate(amt = atm, cmt = "central", evid = 1L, dur = 3),
tidyr::expand_grid(cov, time = dose_times) |>
mutate(amt = avi, cmt = "central_avi", evid = 1L, dur = 3)
)
obs <- bind_rows(
tidyr::expand_grid(cov, time = seq(t_start, t_end, by = by)) |>
mutate(amt = NA_real_, cmt = "Cc", evid = 0L, dur = NA_real_),
tidyr::expand_grid(cov, time = seq(t_start, t_end, by = by)) |>
mutate(amt = NA_real_, cmt = "Cc_avi", evid = 0L, dur = NA_real_)
)
ev <- bind_rows(dose, obs) |> arrange(id, time, desc(evid))
out <- rxode2::rxSolve(model, ev,
keep = intersect(c("grp", "WT", "CRCL"), names(cov)),
returnType = "data.frame")
# rxSolve() omits the `id` column when the event table holds exactly ONE
# subject, so restore it before the per-subject grouping below. Guarded by
# the single-subject precondition that causes the omission in the first
# place, so a genuinely missing `id` on a multi-subject solve still errors.
if (!"id" %in% names(out)) {
stopifnot(dplyr::n_distinct(cov$id) == 1L)
out$id <- cov$id[1]
}
out |> distinct(id, time, .keep_all = TRUE)
}
trapz <- function(t, y) sum(diff(t) * (head(y, -1) + tail(y, -1)) / 2)Check 1 – the nCrCL hinge is continuous. The power
arm and the linear arm must agree exactly at nCrCL = 80, which is what
makes the ini() clearances interpretable as the typical
values at nCrCL = 80.
# Probe symmetrically about the hinge. The test must measure the JUMP between
# the two arms, not the legitimate smooth variation of a continuous function,
# so it is expressed as a RELATIVE difference over a step small enough that
# smooth variation is negligible: the steeper of the two drugs (avibactam)
# has slope theta5 * theta10 / 80 = 0.136 L/h per mL/min at the hinge, so an
# absolute tolerance would have to be re-derived per drug, whereas a relative
# one is scale-free and applies to both.
eps <- 1e-6
hinge <- lapply(c(80 - eps, 80, 80 + eps), function(cr) {
s <- rxode2::rxSolve(typ, cbind(base_cov(CRCL = cr), id = 1L) |>
mutate(time = 0, amt = 0, evid = 0L, cmt = "Cc"),
returnType = "data.frame")
c(cl = s$cl[1], cl_avi = s$cl_avi[1])
})
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
hinge <- do.call(rbind, hinge)
rownames(hinge) <- c("nCrCL 80 - 1e-6", "nCrCL 80", "nCrCL 80 + 1e-6")
knitr::kable(signif(hinge, 12),
caption = "Clearance across the nCrCL = 80 hinge (L/h).")| cl | cl_avi | |
|---|---|---|
| nCrCL 80 - 1e-6 | 5 | 10.3 |
| nCrCL 80 | 5 | 10.3 |
| nCrCL 80 + 1e-6 | 5 | 10.3 |
rel_jump <- c(
cl = max(abs(hinge[, "cl"] / hinge["nCrCL 80", "cl"] - 1)),
cl_avi = max(abs(hinge[, "cl_avi"] / hinge["nCrCL 80", "cl_avi"] - 1))
)
stopifnot(
# A genuine failure of the two arms to meet would be a relative jump of
# order 1e-2 or larger; smooth variation across this window is ~1e-8.
rel_jump[["cl"]] < 1e-6,
rel_jump[["cl_avi"]] < 1e-6,
abs(hinge["nCrCL 80", "cl"] - 5.00) < 1e-8,
abs(hinge["nCrCL 80", "cl_avi"] - 10.3) < 1e-8
)The relative jump across the hinge is below 1e-6 for both drugs –
four orders of magnitude smaller than any real failure of the arms to
meet – and at the hinge the clearances equal the published theta1 = 5.00
and theta5 = 10.3 exactly, confirming that the ini()
clearances are the typical values at nCrCL = 80.
Check 2 – steady-state AUC equals dose over clearance, per subject. For a linear disposition model this identity holds exactly, so it tests the ODE system, the infusion handling, and the observation scaling at once.
idcov <- bind_rows(lapply(seq_len(6), function(i) {
cbind(base_cov(WT = c(50, 60, 70, 85, 100, 120)[i],
CRCL = c(20, 45, 65, 95, 130, 190)[i],
DIS_CIAI = 1), id = i)
}))
ss <- solve_regimen(typ, idcov, atm = 1500, avi = 500, ii = 6,
t_start = 216, t_end = 240)
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
#> Warning: multi-subject simulation without without 'omega'
ident <- ss |>
group_by(id) |>
summarise(
auc_atm = trapz(time, Cc), auc_avi = trapz(time, Cc_avi),
cl = first(cl), cl_avi = first(cl_avi), .groups = "drop"
) |>
mutate(
pred_atm = 6000 / cl, pred_avi = 2000 / cl_avi,
err_atm = 100 * (auc_atm / pred_atm - 1),
err_avi = 100 * (auc_avi / pred_avi - 1)
)
knitr::kable(
ident |>
transmute(ID = id,
"CL (L/h)" = round(cl, 2), "AUC24 sim" = round(auc_atm),
"Dose24/CL" = round(pred_atm), "ATM % diff" = round(err_atm, 3),
"AVI % diff" = round(err_avi, 3)),
caption = "Steady-state AUC24 vs the Dose24/CL identity, per subject."
)| ID | CL (L/h) | AUC24 sim | Dose24/CL | ATM % diff | AVI % diff |
|---|---|---|---|---|---|
| 1 | 2.48 | 2422 | 2422 | 0 | 0 |
| 2 | 4.27 | 1406 | 1406 | 0 | 0 |
| 3 | 5.76 | 1041 | 1041 | 0 | 0 |
| 4 | 7.82 | 767 | 767 | 0 | 0 |
| 5 | 9.96 | 603 | 603 | 0 | 0 |
| 6 | 13.62 | 441 | 441 | 0 | 0 |
# Observed worst case is ~1.5e-5 %, so this bound sits ~70x above the
# trapezoidal-integration noise floor while still being tight enough that any
# real error in the ODE system, the infusion handling, or the observation
# scaling would breach it by orders of magnitude.
stopifnot(max(abs(ident$err_atm)) < 1e-3, max(abs(ident$err_avi)) < 1e-3)Every subject reproduces the identity to better than 0.001% for both drugs – in fact to about 1e-5% – the residual being trapezoidal-integration error on the 0.1 h output grid rather than any error in the model.
Check 3 – the OMEGA block reproduces the paper’s stated correlations. Table S3 reports the IIV diagonals as “%CV” and the off-diagonals as bare covariances, which are only mutually consistent under one reading of the %CV column. The Discussion supplies the answer key: the aztreonam-avibactam correlations “were high at 0.976 and 0.986”. Taking omega^2 = (CV%/100)^2 reproduces both; the log-normal reading omega^2 = log(CV^2 + 1) does not.
om <- ui$omega
pick <- c("etalcl", "etalvc", "etalcl_avi", "etalvc_avi")
corr <- cov2cor(om[pick, pick])
knitr::kable(
tibble::tibble(
Pair = c("CL aztreonam - CL avibactam", "Vc aztreonam - Vc avibactam"),
Model = round(c(corr["etalcl", "etalcl_avi"], corr["etalvc", "etalvc_avi"]), 3),
Published = c(0.976, 0.986)
),
caption = "Cross-drug IIV correlations vs Xie 2025 Discussion."
)| Pair | Model | Published |
|---|---|---|
| CL aztreonam - CL avibactam | 0.978 | 0.976 |
| Vc aztreonam - Vc avibactam | 0.986 | 0.986 |
stopifnot(
abs(corr["etalcl", "etalcl_avi"] - 0.976) < 0.005,
abs(corr["etalvc", "etalvc_avi"] - 0.986) < 0.005,
min(eigen(om, only.values = TRUE)$values) > 0
)Both correlations land within 0.005 of the published values, and the
full OMEGA matrix is positive definite (so rxSolve can
sample from it).
Renal-function ladder: reproducing Table S9
This is the sharpest available test of the covariate model. For each renal function group, supplementary Table S9 reports the steady-state geometric-mean Cmax and AUC24 of both drugs in simulated cIAI patients receiving the approved regimen. The paper’s own covariate sampling cannot be reproduced (it drew weight, BSA and CrCL from a multivariate normal whose covariance matrix is not published), so instead a single number per group is taken from the paper – the aztreonam AUC24 – and inverted through the aztreonam clearance model to recover the nCrCL of a 70 kg cIAI patient who would produce it. The other three quantities per group are then predictions.
ladder <- tibble::tribble(
~grp, ~atm, ~avi, ~ii, ~esrd, ~pATMcmax, ~pATMauc, ~pAVIcmax, ~pAVIauc,
"ARC", 1500, 500, 6, 0, 42.4, 666, 8.5, 128,
"Normal", 1500, 500, 6, 0, 51.9, 838, 10.2, 158,
"Mild", 1500, 500, 6, 0, 65.0, 1094, 13.8, 229,
"Moderate", 750, 250, 6, 0, 39.7, 699, 10.3, 188,
"Severe", 675, 225, 8, 0, 40.3, 646, 13.0, 237,
"ESRD", 675, 225, 12, 1, 45.1, 661, 19.8, 378
)
# Invert the aztreonam renal factor for a 70 kg cIAI patient.
implied_ncrcl <- function(cl) {
f <- cl / (5.00 * (1 + 0.279))
if (f >= 1) 80 + (f - 1) / 0.00383 else 80 * f^(1 / 0.502)
}
ladder_res <- lapply(seq_len(nrow(ladder)), function(i) {
r <- ladder[i, ]
cl_atm <- (r$atm * 24 / r$ii) / r$pATMauc
ncrcl <- implied_ncrcl(cl_atm)
cov <- cbind(base_cov(WT = 70, CRCL = ncrcl, DIS_CIAI = 1,
RENALIMP_ESRD = r$esrd), id = 1L)
s <- solve_regimen(typ, cov, r$atm, r$avi, r$ii, 216, 240)
tibble::tibble(
Group = r$grp, nCrCL = round(ncrcl, 1),
ATMcmax = max(s$Cc), pATMcmax = r$pATMcmax,
AVIauc = trapz(s$time, s$Cc_avi), pAVIauc = r$pAVIauc,
AVIcmax = max(s$Cc_avi), pAVIcmax = r$pAVIcmax
)
}) |> bind_rows() |>
mutate(
"ATM Cmax % diff" = round(100 * (ATMcmax / pATMcmax - 1), 1),
"AVI AUC24 % diff" = round(100 * (AVIauc / pAVIauc - 1), 1),
"AVI Cmax % diff" = round(100 * (AVIcmax / pAVIcmax - 1), 1)
)
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
#> ℹ omega/sigma items treated as zero: 'etalcl', 'etalvc', 'etalcl_avi', 'etalvc_avi', 'etalq_avi', 'etalvp_avi'
ladder_res |>
transmute(
Group, "Implied nCrCL" = nCrCL,
"ATM Cmax sim" = round(ATMcmax, 1), "ATM Cmax pub" = pATMcmax,
`ATM Cmax % diff`,
"AVI AUC24 sim" = round(AVIauc), "AVI AUC24 pub" = pAVIauc,
`AVI AUC24 % diff`, `AVI Cmax % diff`
) |>
knitr::kable(
caption = paste(
"Table S9 ladder. Only the aztreonam AUC24 was used as input (to recover",
"the implied nCrCL); the aztreonam Cmax and both avibactam quantities are",
"predictions. Units mg/L and mg*h/L."
)
)| Group | Implied nCrCL | ATM Cmax sim | ATM Cmax pub | ATM Cmax % diff | AVI AUC24 sim | AVI AUC24 pub | AVI AUC24 % diff | AVI Cmax % diff |
|---|---|---|---|---|---|---|---|---|
| ARC | 186.7 | 44.5 | 42.4 | 5.0 | 131 | 128 | 2.0 | 5.9 |
| Normal | 111.2 | 52.9 | 51.9 | 1.8 | 159 | 158 | 0.4 | 2.2 |
| Mild | 58.9 | 64.6 | 65.0 | -0.7 | 241 | 229 | 5.2 | 3.2 |
| Moderate | 36.1 | 39.0 | 39.7 | -1.8 | 202 | 188 | 7.5 | 3.5 |
| Severe | 19.3 | 38.7 | 40.3 | -3.9 | 265 | 237 | 11.8 | 5.4 |
| ESRD | 8.2 | 42.6 | 45.1 | -5.4 | 508 | 378 | 34.4 | 23.5 |
For the five non-ESRD groups, all three predicted quantities land within 12% of the published values – mostly within 6% – from a single input per group. The implied nCrCL values (roughly 190, 111, 59, 36 and 19 mL/min/1.73 m^2) also fall inside each group’s published raw-CrCL band, which they were in no way constrained to do. Taken together with the fact that aztreonam and avibactam have different renal exponents (0.502 power / 0.00383 slope versus 1.06 / 0.00313), different infection-type coefficients and different volumes, this is strong evidence that the hinged renal model, the allometry, and the proportional infection-type effects are all encoded as the authors intended.
non_esrd <- ladder_res |> filter(Group != "ESRD")
stopifnot(
max(abs(non_esrd$`ATM Cmax % diff`)) < 12,
max(abs(non_esrd$`AVI AUC24 % diff`)) < 12,
max(abs(non_esrd$`AVI Cmax % diff`)) < 12,
# ESRD avibactam is the documented outlier; assert it stays an outlier so a
# future change that silently "fixes" it is noticed.
ladder_res$`AVI AUC24 % diff`[ladder_res$Group == "ESRD"] > 20
)The ESRD row is the exception: avibactam AUC24 comes out 34% above the published value and Cmax 24% above, while aztreonam matches. That localisation is itself informative – aztreonam carries no ESRD or dialysis term, so the discrepancy lies entirely in the avibactam ESRD / dialysis pathway. See the Errata.
Virtual cohort and PKNCA validation
The cohort below reproduces the paper’s simulation design as far as it is published: cIAI patients (the population Xie 2025 used for dose selection, being “the most conservative”), stratified by renal function, receiving the approved maintenance regimen to steady state.
set.seed(20250618)
N_PER_ARM <- 200L # cap is 200 per arm
arms <- tibble::tribble(
~grp, ~lo, ~hi, ~atm, ~avi, ~ii,
"Normal", 80, 150, 1500, 500, 6,
"Mild", 50, 80, 1500, 500, 6,
"Severe", 15, 30, 675, 225, 8
)
make_arm <- function(i, id_offset) {
a <- arms[i, ]
tibble::tibble(
id = id_offset + seq_len(N_PER_ARM),
grp = a$grp,
WT = pmin(pmax(rlnorm(N_PER_ARM, log(74), 0.25), 33), 130),
CRCL = runif(N_PER_ARM, a$lo, a$hi)
) |>
bind_cols(base_cov(DIS_CIAI = 1)[rep(1, N_PER_ARM), setdiff(
names(base_cov()), c("WT", "CRCL"))])
}
cohort <- bind_rows(lapply(seq_len(nrow(arms)), function(i)
make_arm(i, (i - 1L) * N_PER_ARM)))
stopifnot(!anyDuplicated(cohort$id), nrow(cohort) == N_PER_ARM * nrow(arms))
sim <- bind_rows(lapply(seq_len(nrow(arms)), function(i) {
a <- arms[i, ]
solve_regimen(mod, cohort |> filter(grp == a$grp),
a$atm, a$avi, a$ii, t_start = 216, t_end = 240)
}))
stopifnot(n_distinct(sim$id) == N_PER_ARM * nrow(arms))
thresholds <- tibble::tibble(
analyte = c("Aztreonam", "Avibactam"),
thr = c(8 / 0.616, 2.5 / 0.92)
)
sim |>
select(grp, id, time, Aztreonam = Cc, Avibactam = Cc_avi) |>
tidyr::pivot_longer(c(Aztreonam, Avibactam),
names_to = "analyte", values_to = "conc") |>
mutate(tad = time - 216) |>
group_by(grp, analyte, tad) |>
summarise(Q05 = quantile(conc, 0.05), Q50 = median(conc),
Q95 = quantile(conc, 0.95), .groups = "drop") |>
ggplot(aes(tad, Q50)) +
geom_ribbon(aes(ymin = Q05, ymax = Q95), alpha = 0.25) +
geom_line() +
geom_hline(data = thresholds, aes(yintercept = thr),
linetype = "dashed", colour = "firebrick") +
facet_grid(analyte ~ grp, scales = "free_y") +
scale_y_log10() +
labs(x = "Time after the start of the steady-state window (h)",
y = "Total plasma concentration (mg/L)")
Simulated steady-state total-drug concentration-time profiles over one 24 h window by renal function group (median and 5th-95th percentiles), with the free-drug PK/PD thresholds shown on the total-drug scale.
nca_for <- function(conc_col, analyte_dose) {
sim_nca <- sim |>
filter(!is.na(.data[[conc_col]])) |>
transmute(id, time, grp, Cc = .data[[conc_col]])
dose_df <- cohort |>
select(id, grp) |>
left_join(arms |> select(grp, amt = all_of(analyte_dose), ii), by = "grp") |>
tidyr::uncount(24 / ii, .id = "k") |>
mutate(time = 216 + (k - 1) * ii) |>
select(id, time, amt, grp)
conc_obj <- PKNCA::PKNCAconc(sim_nca, Cc ~ time | grp + id,
concu = "mg/L", timeu = "h")
dose_obj <- PKNCA::PKNCAdose(dose_df, amt ~ time | grp + id, doseu = "mg")
intervals <- data.frame(start = 216, end = 240,
cmax = TRUE, tmax = TRUE, auclast = TRUE, cmin = TRUE)
PKNCA::pk.nca(PKNCA::PKNCAdata(conc_obj, dose_obj, intervals = intervals))
}
nca_atm <- nca_for("Cc", "atm")
nca_avi <- nca_for("Cc_avi", "avi")
published_atm <- tibble::tribble(
~grp, ~cmax, ~auclast,
"Normal", 51.9, 838,
"Mild", 65.0, 1094,
"Severe", 40.3, 646
)
nlmixr2lib::ncaComparisonTable(
simulated = nca_atm, reference = published_atm, by = "grp",
units = c(cmax = "mg/L", auclast = "mg*h/L"), tolerance_pct = 20
) |>
knitr::kable(
caption = paste(
"Aztreonam steady-state NCA vs Xie 2025 Table S9.",
"* differs from reference by >20%."
)
)| NCA parameter | grp | Reference | Simulated | % diff |
|---|---|---|---|---|
| Cmax (mg/L) | Normal | 51.9 | 50.6 | -2.5% |
| Cmax (mg/L) | Mild | 65 | 58.8 | -9.5% |
| Cmax (mg/L) | Severe | 40.3 | 36.6 | -9.2% |
| AUClast (mg*h/L) | Normal | 838 | 818 | -2.3% |
| AUClast (mg*h/L) | Mild | 1090 | 990 | -9.5% |
| AUClast (mg*h/L) | Severe | 646 | 591 | -8.4% |
published_avi <- tibble::tribble(
~grp, ~cmax, ~auclast,
"Normal", 10.2, 158,
"Mild", 13.8, 229,
"Severe", 13.0, 237
)
nlmixr2lib::ncaComparisonTable(
simulated = nca_avi, reference = published_avi, by = "grp",
units = c(cmax = "mg/L", auclast = "mg*h/L"), tolerance_pct = 20
) |>
knitr::kable(
caption = paste(
"Avibactam steady-state NCA vs Xie 2025 Table S9.",
"* differs from reference by >20%."
)
)| NCA parameter | grp | Reference | Simulated | % diff |
|---|---|---|---|---|
| Cmax (mg/L) | Normal | 10.2 | 9.93 | -2.6% |
| Cmax (mg/L) | Mild | 13.8 | 12.4 | -9.9% |
| Cmax (mg/L) | Severe | 13 | 12.3 | -5.7% |
| AUClast (mg*h/L) | Normal | 158 | 152 | -3.8% |
| AUClast (mg*h/L) | Mild | 229 | 207 | -9.5% |
| AUClast (mg*h/L) | Severe | 237 | 225 | -5.3% |
No row exceeds the 20% tolerance. Two aggregation caveats are worth
stating explicitly rather than leaving implicit:
ncaComparisonTable() summarises the simulated side by the
median whereas Table S9 reports geometric
means (for a log-normal quantity these coincide, so the effect
here is small), and the cohort’s covariate distribution is an
approximation of the paper’s unpublished multivariate-normal sampling.
The severe-impairment arm is the weakest of the three for exactly that
reason – the nCrCL band 15-30 is wide relative to the steep power arm
that governs clearance there.
Joint probability of target attainment
The paper’s decision criterion is the joint target: free aztreonam above 8 mg/L for at least 60% of the dosing interval and free avibactam above 2.5 mg/L for at least 50%, achieved simultaneously in the same patient.
FU_ATM <- 0.616; FU_AVI <- 0.92
pta <- sim |>
group_by(grp, id) |>
summarise(
fT_atm = 100 * mean(Cc * FU_ATM > 8),
fT_avi = 100 * mean(Cc_avi * FU_AVI > 2.5),
.groups = "drop"
) |>
group_by(grp) |>
summarise(
"Median %fT>8 (ATM)" = round(median(fT_atm), 1),
"Median %fT>2.5 (AVI)" = round(median(fT_avi), 1),
"Joint PTA % (simulated)" = round(100 * mean(fT_atm >= 60 & fT_avi >= 50), 1),
.groups = "drop"
) |>
left_join(
tibble::tibble(grp = c("Normal", "Mild", "Severe"),
"Joint PTA % (Xie 2025 Table 5)" = c(96.7, 99.3, 91.2)),
by = "grp"
) |>
rename("Renal function" = grp)
knitr::kable(pta, caption = "Joint PTA at steady state in simulated cIAI patients.")| Renal function | Median %fT>8 (ATM) | Median %fT>2.5 (AVI) | Joint PTA % (simulated) | Joint PTA % (Xie 2025 Table 5) |
|---|---|---|---|---|
| Mild | 100 | 100 | 99.5 | 99.3 |
| Normal | 100 | 100 | 96.0 | 96.7 |
| Severe | 100 | 100 | 89.5 | 91.2 |
The normal-renal-function and mild-impairment arms reproduce the published joint PTA to within about one percentage point. The severe-impairment arm comes out several points low, consistent with the same covariate-sampling limitation seen in its NCA row; the published median %fT values of 100% for both drugs are reproduced in every arm.
Assumptions and deviations
Covariate equation forms come from the predecessor
paper. Xie 2025 prints no equations. Every functional form –
the hinged nCrCL relationship, the proportional
X*(1 + theta) categorical effects, the ESRD term replacing
rather than multiplying the nCrCL arm, and the dialysis clearance being
an absolute value rather than a multiplier – is taken from Das 2024
supplementary Tables 4 and 5, which is the same author group’s
immediately preceding model on the same data lineage and prints them
verbatim. Each is cross-checked against Xie 2025’s own numbers in the
Source trace table above, and the renal-function ladder is an end-to-end
test of all of them at once.
The APACHE II covariate is encoded as a binary stratum, and
the threshold is unknown. Neither Xie 2025 nor Das 2024 writes
the equation. Das 2024 prints the row as CL*(1 + theta16),
which is the grammar it uses for every binary population effect and not
the grammar it uses for continuous covariates (which always carry the
covariate symbol, as in AGE/35**theta14); a raw continuous
score is also arithmetically impossible, since at a typical ICU APACHE
II of 10 the factor 1 + (-0.118)*10 is negative. The model
therefore carries APACHE_II_SEV as a binary indicator.
The score threshold that sets the flag is not stated in any
source on disk and has not been invented here. Every simulation
in this vignette holds it at the 0 reference, so no reproduced value
depends on the inference.
The pooled phase-2/3 residual stratum is undefined.
Table S3 reports four aztreonam proportional residual magnitudes –
unqualified, phase 2, phase 3, and “phase 2/3” – without saying which
records constitute the fourth stratum as distinct from the other two.
All four are carried so the published parameter set is complete,
selected by STUDY_AZTAVI_PHASE2 / _PHASE3 /
_PHASE23, with all three zero selecting the unqualified
magnitude as the Phase 1 stratum (which matches Das 2024, where the
strata are phase I / II / III with an additive term on phase I only).
Residual error affects only the simulated observation, not the
individual predictions used throughout this vignette.
ESRD avibactam exposures are ~30% below what the encoded
model predicts. The renal-function ladder reproduces every
non-ESRD group within 12% but overpredicts avibactam AUC24 by 34% and
Cmax by 24% in ESRD, while aztreonam matches. Since aztreonam carries no
ESRD or dialysis term, the gap is isolated to the avibactam ESRD /
dialysis pathway. Quantitatively, the published ESRD AUC24 of 378 mg*h/L
on 225 mg q12h implies an avibactam clearance of 1.19 L/h, against the
0.88 L/h the ESRD arm produces for a 70 kg cIAI patient
(10.3 * (1 - 0.923) * (1 + 0.115)) – a 35% clearance
difference.
It is worth being precise about what this does not show. The
obvious candidate explanation – that the published ESRD simulation
applies the separate dialysis clearance of 17.9 L/h, since Xie 2025 says
the approved ESRD regimen “requires patients to be receiving
hemodialysis, with aztreonam-avibactam to be administered after
dialysis” – cannot account for it as stated: that clearance is roughly
17 times the published-implied value and would drive AUC24 to about 23
mgh/L, far below the published 378 rather than above it. Whatever
the paper did, it is much closer to the ESRD arm than to the dialysis
arm, and the residual 35% must come from something intermediate that is
not published – a time-weighted contribution of intermittent dialysis
sessions, residual renal function in the simulated ESRD cohort, or a
covariate distribution differing from the 70 kg reference patient used
here. Neither the dialysis schedule nor the session duration is
reported, so the sub-model is encoded as a subject-level flag
(RRT_HEMODIAL_STATUS) rather than a time-varying
per-session gate, and the intradialytic / interdialytic sawtooth cannot
be reproduced. Nothing was tuned to close this gap, and the
ladder-assert chunk asserts the ESRD row stays* an
outlier so that a future change which silently closes it is noticed.
Body-weight allometry is applied to the dialysis
clearance. Xie 2025 states that body weight is a structural
covariate on “aztreonam and avibactam CL, Vc, Vp, and Q” without
excepting the dialysis clearance, so the allometric term is applied
uniformly. If the authors instead held CL_AVI_DIAL at an
absolute value, predictions for dialysis patients away from 70 kg would
differ.
Covariate distributions are approximations. Xie 2025 sampled weight, BSA and CrCL from a multivariate normal with a covariance matrix estimated from the analysis data set and truncated to observed ranges; that matrix is not published. This vignette samples body weight log-normally about the pooled median of 74 kg (CV 25%, truncated to the observed 33-130 kg) and nCrCL uniformly within each renal-function band, independently. The bands themselves are the paper’s raw-CrCL group boundaries used directly as nCrCL bands, i.e. assuming a body surface area of 1.73 m^2; the renal-function ladder above avoids this assumption entirely by inverting the model for the implied nCrCL instead.
Infection-type indicators are set to cIAI
throughout. This follows the paper, whose dose-selection
simulations were all run in cIAI patients as the most conservative case.
STUDY_CIAI_PH2 is zero everywhere, as it is for every
aztreonam-avibactam subject.
Observation rows use the observable name in
cmt. This model declares two endpoints, so rxode2
maps Cc and Cc_avi to modelled compartments 5
and 6 and requires those names on observation records;
cmt = "central" fails with
'dvid'->'cmt' ... on a undefined compartment regardless
of useLinCmt. The four ODE states retain slots 1-4, so this
does not trigger the slot-renumbering problem that the same syntax
causes in single-endpoint models.
Free concentrations use fixed unbound fractions. The
0.616 (aztreonam) and 0.92 (avibactam) unbound fractions are conversion
constants applied downstream of the PK model, not fitted parameters, so
they live in the model’s population metadata rather than in
ini() and are applied in this vignette.