library(shiny) library(bslib) library(mrgsolve) library(dplyr) library(ggplot2) # ── Betamethasone 2-Compartment PopPK Model ── # Source: Schoenmakers et al. (2025) Br J Clin Pharmacol 91:2327-2339 # Population PK of betamethasone in pre-eclampsia # 2-cpt model with first-order absorption, eoPE covariate on CL model_code <- ' $PARAM @annotated KA : 1.67 : Absorption rate constant (1/h) CL : 15.6 : Apparent clearance without eoPE (L/h) VC : 46.1 : Apparent central volume (L) VP : 109.0 : Apparent peripheral volume (L) QQ : 99.8 : Apparent intercompartmental clearance (L/h) EOPE : 0 : Early-onset pre-eclampsia (0=No, 1=Yes) THETA_PE : 0.606 : Pre-eclampsia effect on CL $CMT @annotated DEPOT : IM depot (mg) CENT : Central compartment (mg) PERIPH : Peripheral compartment (mg) $MAIN double CLi = CL; if (EOPE > 0.5) CLi = CL * THETA_PE; $ODE dxdt_DEPOT = -KA * DEPOT; dxdt_CENT = KA * DEPOT - (CLi/VC + QQ/VC) * CENT + (QQ/VP) * PERIPH; dxdt_PERIPH = (QQ/VC) * CENT - (QQ/VP) * PERIPH; $TABLE double CP = CENT / VC; double CP_ugL = CP * 1000.0; CP_ugL = CP_ugL > 0 ? CP_ugL : 0; $CAPTURE @annotated CP_ugL : Plasma concentration (ug/L) CLi : Individual clearance (L/h) ' mod <- mcode("betamethasone", model_code) # ── Theme ── app_theme <- bs_theme( version = 5, bootswatch = "flatly", primary = "#8b5cf6" ) |> bs_add_rules(" .metric-card { background: #f8f9fa; border-radius: 8px; padding: 15px; margin: 5px; text-align: center; border: 1px solid #dee2e6; } .metric-value { font-size: 24px; font-weight: bold; color: #2c3e50; } .metric-label { font-size: 12px; color: #7f8c8d; } .metric-success .metric-value { color: #10b981; } .metric-warning .metric-value { color: #f59e0b; } .metric-primary .metric-value { color: #8b5cf6; } .metric-info .metric-value { color: #0dcaf0; } .ref-card { font-size: 13px; } .ref-card a { color: #8b5cf6; } ") # ── UI ── ui <- page_sidebar( title = "Betamethasone PK Simulator — Pre-eclampsia", theme = app_theme, sidebar = sidebar( title = "Simulation Settings", width = 340, h6("Clinical Scenario"), radioButtons("eope", "Pre-eclampsia Status", choices = c("No Pre-eclampsia" = "0", "Early-Onset Pre-eclampsia" = "1"), selected = "0" ), hr(), h6("Dosing Regimen"), numericInput("dose", "Dose per administration (mg)", value = 11.4, min = 1, max = 30, step = 0.1), numericInput("n_doses", "Number of doses", value = 2, min = 1, max = 4), numericInput("interval", "Dosing interval (h)", value = 24, min = 6, max = 48), hr(), h6("Simulation"), numericInput("sim_hours", "Simulation duration (h)", value = 96, min = 24, max = 240), checkboxInput("log_scale", "Log Scale (Y-axis)", value = FALSE), checkboxInput("show_threshold", "Show fetal target (1 ng/mL maternal ≈ 1.3 µg/L)", value = TRUE), hr(), card( card_header("Model Parameters", class = "bg-light"), p(strong("CL/F:"), "15.6 L/h (no eoPE)"), p(strong("CL/F:"), "9.35 L/h (eoPE)"), p(strong("Vc/F:"), "46.1 L"), p(strong("Vp/F:"), "109 L"), p(strong("Q/F:"), "99.8 L/h"), p(strong("Ka:"), "1.67 h⁻¹"), p(strong("t½:"), "~7.4 h"), p(em("Dose: 11.4 mg IM (6 mg phosphate + 5.4 mg acetate)")) ) ), # ── Metrics Row ── layout_column_wrap( width = 1/4, fill = FALSE, div(class = "metric-card metric-success", div(class = "metric-value", textOutput("cmax_val")), div(class = "metric-label", "Cmax (µg/L)") ), div(class = "metric-card metric-warning", div(class = "metric-value", textOutput("auc_val")), div(class = "metric-label", "AUC₀₋₂₄h (ng·h/mL)") ), div(class = "metric-card metric-primary", div(class = "metric-value", textOutput("cl_val")), div(class = "metric-label", "CL/F (L/h)") ), div(class = "metric-card metric-info", div(class = "metric-value", textOutput("thalf_val")), div(class = "metric-label", "t½ (h)") ) ), # ── PK Plot ── card( card_header("Maternal Betamethasone Concentration-Time Profile"), full_screen = TRUE, plotOutput("pkPlot", height = "450px") ), # ── Comparison & Info ── navset_card_tab( title = "Analysis", nav_panel("eoPE vs Non-eoPE Comparison", plotOutput("comparePlot", height = "400px") ), nav_panel("NCA — Ex Vivo Perfusion (Fig 4A)", layout_columns( col_widths = c(6, 6), card( card_header("Maternal Betamethasone — Placental Perfusion"), plotOutput("ncaPlot", height = "380px") ), card( card_header("NCA Summary (Maternal Circulation)"), tableOutput("ncaTable"), hr(), markdown(" **Data Source:** Figure 4A, Schoenmakers et al. (2025) *Ex vivo dual-sided placental perfusion, starting conc 1336 nmol/L.* **Notes:** - Rapid initial distribution (0–15 min): maternal conc drops from ~800 to ~60 nmol/L - System reaches equilibrium (~260–300 nmol/L) by 60 min - Terminal λz estimated from 60–180 min; near-flat profile → very long apparent t½ - AUC extrapolation unreliable (equilibrium, not elimination) - **Reported foetal:maternal ratio = 0.76 ± 0.05 at 180 min** ") ) ), card( card_header("Perfusion Data (Digitized from Figure 4A)"), tableOutput("perfusionDataTable") ) ), nav_panel("Clinical Context", markdown(" ## Antenatal Betamethasone in Pre-eclampsia **Standard Regimen:** 11.4 mg IM (betamethasone sodium phosphate 6 mg + betamethasone acetate 5.4 mg) × 2 doses, 24 h apart. **Key Finding:** Women with early-onset pre-eclampsia (eoPE) exhibit **40% lower betamethasone clearance** (9.35 vs 15.6 L/h), resulting in **~40% higher maternal exposure** (AUC₀₋₂₄h: 709 vs 584 ng·h/mL). ### Implications - Current one-dose-fits-all approach may lead to **overexposure** in eoPE patients - Fetal target: >1 ng/mL betamethasone for lung maturation — far exceeded with current dosing - Foetal-to-maternal ratio ≈ 0.76 (from ex vivo perfusion) - Main placental metabolite: 11-keto-betamethasone (via 11β-HSD) ### Mechanisms of Altered PK in eoPE - **Decreased 11β-HSD1 mRNA expression** in eoPE placentas (P = 0.015) - Reduced estrogen levels → impaired CYP3A4-mediated hepatic metabolism - Elevated hepatic artery resistance → reduced liver blood flow *Source: Schoenmakers et al. (2025) Br J Clin Pharmacol 91:2327-2339* ") ), nav_panel("References", div(class = "ref-card", markdown(" ## References 1. **Schoenmakers S, Li L, Kluivers ACM, et al.** Pharmacokinetics of betamethasone in pre-eclampsia: An in vivo and ex vivo study. *Br J Clin Pharmacol.* 2025;91:2327-2339. [DOI: 10.1002/bcp.70035](https://doi.org/10.1002/bcp.70035) 2. **Foissac F, Zheng Y, Hirt D, et al.** Maternal betamethasone for prevention of respiratory distress syndrome in neonates: population PK/PD approach. *Clin Pharmacol Ther.* 2020;108(5):1026-1035. 3. **Della Torre M, Hibbard JU, Jeong H, Fischer JH.** Betamethasone in pregnancy: influence of maternal body weight and multiple gestation on pharmacokinetics. *Am J Obstet Gynecol.* 2010;203:254.e1-e12. 4. **Krzyzanski W, Milad MA, Jobe AH, et al.** Population PK modeling of IM and oral dexamethasone and betamethasone in Indian women. *J Pharmacokinet Pharmacodyn.* 2021;48(2):261-272. ### FDA Label - [Betamethasone Prescribing Information (Drugs.com)](https://www.drugs.com/pro/betamethasone.html) - [FDA Label (PDF)](https://www.accessdata.fda.gov/drugsatfda_docs/label/2019/018741s040lbl.pdf) ") ) ) ), # ── PKPDBuilder Branding Footer ── div(style = "text-align: center; padding: 20px; margin-top: 30px; border-top: 1px solid #e9ecef; color: #6c757d; font-size: 12px;", "Powered by ", tags$a(href = "https://www.pkpdbuilder.com", target = "_blank", style = "color: #8b5cf6; font-weight: 500;", "PKPDBuilder.com"), " • Built by Sunny ☀️ (Husain Attarwala's AI Assistant)", br(), tags$span(style = "font-size: 10px;", "For research and educational purposes only. Not for clinical decision-making.") ) ) # ── Server ── server <- function(input, output, session) { sim_data <- reactive({ eope_flag <- as.numeric(input$eope) dose_mg <- input$dose n <- input$n_doses ii <- input$interval end_h <- input$sim_hours ev_obj <- ev(amt = dose_mg, cmt = 1, ii = ii, addl = n - 1) out <- mod %>% param(EOPE = eope_flag) %>% ev(ev_obj) %>% mrgsim(end = end_h, delta = 0.25) %>% as.data.frame() out }) output$cmax_val <- renderText({ d <- sim_data() sprintf("%.1f", max(d$CP_ugL, na.rm = TRUE)) }) output$auc_val <- renderText({ d <- sim_data() d24 <- d %>% dplyr::filter(time <= 24) # Trapezoidal AUC in ng*h/mL (CP_ugL is ug/L = ng/mL) auc <- sum(diff(d24$time) * (head(d24$CP_ugL, -1) + tail(d24$CP_ugL, -1)) / 2) sprintf("%.0f", auc) }) output$cl_val <- renderText({ d <- sim_data() sprintf("%.1f", d$CLi[1]) }) output$thalf_val <- renderText({ # Approximate terminal half-life sprintf("%.1f", 7.44) }) output$pkPlot <- renderPlot({ d <- sim_data() d_plot <- if (input$log_scale) d %>% dplyr::filter(CP_ugL > 0.001) else d p <- ggplot(d_plot, aes(x = time, y = CP_ugL)) + geom_line(color = "#8b5cf6", linewidth = 1.2) + labs( x = "Time after first dose (h)", y = "Betamethasone concentration (µg/L)", caption = "Schoenmakers et al. (2025) Br J Clin Pharmacol" ) + theme_minimal(base_size = 14) + theme( plot.caption = element_text(color = "grey50", size = 10), panel.grid.minor = element_blank() ) if (input$log_scale) { p <- p + scale_y_log10() } if (input$show_threshold) { # ~1.3 µg/L maternal ≈ 1 ng/mL fetal (ratio 0.76) p <- p + geom_hline(yintercept = 1.3, linetype = "dashed", color = "#e74c3c", linewidth = 0.7) + annotate("text", x = max(d_plot$time) * 0.85, y = 1.6, label = "Fetal target (~1 ng/mL)", color = "#e74c3c", size = 3.5) } p }) # ── NCA from Figure 4A (Ex Vivo Perfusion) ── perfusion_data <- data.frame( Time_min = c(0, 15, 30, 60, 90, 120, 150, 180), Maternal = c(800, 60, 200, 280, 300, 280, 280, 260), Foetal = c(0, 50, 170, 270, 300, 280, 270, 250) ) output$ncaPlot <- renderPlot({ d_long <- data.frame( Time = rep(perfusion_data$Time_min, 2), Conc = c(perfusion_data$Maternal, perfusion_data$Foetal), Side = rep(c("Maternal", "Foetal"), each = nrow(perfusion_data)) ) ggplot(d_long, aes(x = Time, y = Conc, color = Side, shape = Side)) + geom_point(size = 3) + geom_line(linewidth = 1) + scale_color_manual(values = c("Maternal" = "#e74c3c", "Foetal" = "#3498db")) + scale_shape_manual(values = c("Maternal" = 16, "Foetal" = 1)) + geom_hline(yintercept = 0, color = "grey80") + labs( x = "Time (min)", y = "Betamethasone (nmol/L)", color = "", shape = "", title = "Ex Vivo Placental Perfusion — Figure 4A", subtitle = "Starting concentration: 1336 nmol/L (maternal reservoir)", caption = "Data digitized from Schoenmakers et al. (2025)" ) + theme_minimal(base_size = 14) + theme(legend.position = "top", panel.grid.minor = element_blank()) }) output$ncaTable <- renderTable({ mat <- perfusion_data$Maternal t <- perfusion_data$Time_min # AUC trapezoidal auc_0_180 <- sum(diff(t) * (head(mat, -1) + tail(mat, -1)) / 2) # Terminal slope from 60-180 min idx <- which(t >= 60) fit <- lm(log(mat[idx]) ~ t[idx]) lz <- -coef(fit)[2] thalf <- ifelse(lz > 0, log(2) / lz, NA) data.frame( Parameter = c( "Cmax", "Tmax", "C_last (180 min)", "AUC(0–180 min)", "AUC(0–180 min)", "λz (terminal)", "t½ (terminal)*", "Foetal:Maternal (180 min)" ), Value = c( "800 nmol/L", "0 min (start of perfusion)", "260 nmol/L", sprintf("%.0f nmol·min/L", auc_0_180), sprintf("%.0f nmol·h/L", auc_0_180 / 60), sprintf("%.5f 1/min", lz), sprintf("%.0f min (%.1f h)*", thalf, thalf / 60), sprintf("%.2f (published: 0.76 ± 0.05)", 250 / 260) ) ) }, striped = TRUE, hover = TRUE, width = "100%") output$perfusionDataTable <- renderTable({ df <- perfusion_data df$FM_Ratio <- round(df$Foetal / df$Maternal, 2) df$FM_Ratio[1] <- NA # 0/800 not meaningful names(df) <- c("Time (min)", "Maternal (nmol/L)", "Foetal (nmol/L)", "F:M Ratio") df }, striped = TRUE, hover = TRUE, digits = 0, width = "100%", na = "—") output$comparePlot <- renderPlot({ dose_mg <- input$dose n <- input$n_doses ii <- input$interval end_h <- input$sim_hours ev_obj <- ev(amt = dose_mg, cmt = 1, ii = ii, addl = n - 1) out_no <- mod %>% param(EOPE = 0) %>% ev(ev_obj) %>% mrgsim(end = end_h, delta = 0.25) %>% as.data.frame() %>% mutate(Group = "No Pre-eclampsia") out_pe <- mod %>% param(EOPE = 1) %>% ev(ev_obj) %>% mrgsim(end = end_h, delta = 0.25) %>% as.data.frame() %>% mutate(Group = "Early-Onset Pre-eclampsia") d <- bind_rows(out_no, out_pe) ggplot(d, aes(x = time, y = CP_ugL, color = Group)) + geom_line(linewidth = 1.1) + scale_color_manual(values = c("No Pre-eclampsia" = "#3498db", "Early-Onset Pre-eclampsia" = "#e74c3c")) + geom_hline(yintercept = 1.3, linetype = "dashed", color = "grey50", linewidth = 0.5) + annotate("text", x = end_h * 0.85, y = 1.6, label = "Fetal target", color = "grey50", size = 3.5) + labs( x = "Time after first dose (h)", y = "Betamethasone concentration (µg/L)", color = "", title = "Impact of Pre-eclampsia on Betamethasone Exposure", subtitle = sprintf("Dose: %.1f mg IM × %d doses, %dh apart", dose_mg, n, ii), caption = "eoPE: CL reduced by 40% → higher maternal exposure" ) + theme_minimal(base_size = 14) + theme( legend.position = "top", plot.caption = element_text(color = "grey50", size = 10), panel.grid.minor = element_blank() ) }) } shinyApp(ui = ui, server = server)