library(shiny) library(bslib) library(mrgsolve) library(dplyr) library(ggplot2) model_code <- ' $PARAM @annotated CL : 4.2 : Clearance (L/h) at 70 kg V1 : 61.9 : Central volume of distribution (L) at 70 kg FACT : 2.0 : Intercompartmental scaling factor (Q = CL*FACT, V2 = V1*FACT) INH : 1.0 : CYP3A4/5 inhibitor effect (1 = none, 0.8 = with inhibitor) WT : 70 : Body weight (kg) $CMT @annotated CENT : Central compartment (mcg) PERIPH : Peripheral compartment (mcg) $MAIN double CLi = CL * pow(WT/70.0, 0.75) * INH; double V1i = V1 * (WT/70.0); double Qi = CLi * FACT; double V2i = V1i * FACT; $ODE dxdt_CENT = -(CLi/V1i)*CENT - (Qi/V1i)*CENT + (Qi/V2i)*PERIPH; dxdt_PERIPH = (Qi/V1i)*CENT - (Qi/V2i)*PERIPH; $TABLE double CP = CENT / V1i; $CAPTURE CP ' mod <- mcode("tacrolimus_iv", model_code) 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-box { background: #f0f4ff; border-left: 4px solid #8b5cf6; padding: 12px 16px; border-radius: 4px; margin-top: 10px; font-size: 13px; } .ref-box a { color: #8b5cf6; } ") ui <- page_sidebar( title = "Tacrolimus IV Infusion PK Simulator — Pediatric HCT", theme = app_theme, sidebar = sidebar( title = "Simulation Settings", width = 340, h6("Dosing"), sliderInput("dose_rate", "Infusion Rate (mcg/kg/h)", min = 0.25, max = 2.5, value = 1.25, step = 0.05), numericInput("n_days", "Simulation Duration (days)", value = 7, min = 1, max = 14), hr(), h6("Patient Characteristics"), sliderInput("wt", "Body Weight (kg)", min = 5, max = 160, value = 24, step = 1), checkboxInput("cyp_inh", "CYP3A4/5 Inhibitor (voriconazole/posaconazole)", value = FALSE), hr(), checkboxInput("show_washout", "Show Post-Infusion Washout (24h)", value = FALSE), checkboxInput("log_scale", "Log Scale (Y-axis)", value = FALSE) ), navset_card_tab( title = "Tacrolimus PopPK Simulator", full_screen = TRUE, nav_panel("Simulation", layout_column_wrap( width = 1/4, fill = FALSE, div(class = "metric-card metric-success", div(class = "metric-value", textOutput("css")), div(class = "metric-label", "Css (ng/mL)")), div(class = "metric-card metric-warning", div(class = "metric-value", textOutput("time_ss")), div(class = "metric-label", "Time to 90% SS (h)")), div(class = "metric-card metric-primary", div(class = "metric-value", textOutput("thalf")), div(class = "metric-label", "t\u00BD\u03B2 (h)")), div(class = "metric-card metric-info", div(class = "metric-value", textOutput("total_rate")), div(class = "metric-label", "Total Rate (mcg/h)")) ), plotOutput("pkPlot", height = "500px") ), nav_panel("Model Information", markdown(" ## Tacrolimus — 2-Compartment IV Continuous Infusion PopPK Model **Population:** Pediatric and young adult patients (0.5–25 years) undergoing allogeneic hematopoietic cell transplantation (HCT) **Route:** IV continuous infusion **Structure:** Two-compartment linear model **Software:** NONMEM v7.4 **N:** 111 patients, 1,648 trough concentrations ### Parameter Estimates (referenced to 70 kg) | Parameter | Value | RSE | Unit | Notes | |-----------|-------|-----|------|-------| | CL | 4.2 | 2.95% | L/h | Allometric exponent: 0.75 | | V1 | 61.9 | 5.98% | L | Allometric exponent: 1.0 | | Q | CL × 2.0 | — | L/h | Fixed ratio to CL | | V2 | V1 × 2.0 | — | L | Fixed ratio to V1 | | CYP inhibitor effect | 0.80 | 6.97% | — | 20% reduction in CL | ### Random Effects - **IIV on CL:** 26.1% CV - **IOV on CL:** 28.7% CV - **Residual error (proportional):** 17.9% ### Key Equations - CL_i = 4.2 × (WT/70)^0.75 × 0.8^INH - V1_i = 61.9 × (WT/70)^1.0 - Q_i = CL_i × 2.0 - V2_i = V1_i × 2.0 ### Therapeutic Drug Monitoring - **Target trough range:** 7–10 ng/mL - **Recommended starting dose:** 1.25 mcg/kg/h IV continuous infusion - **TDM frequency:** Every 24–48h after initiation; daily after dose changes - **Narrow therapeutic index:** Supratherapeutic levels → nephrotoxicity, neurotoxicity; subtherapeutic → GVHD risk ### Covariates Evaluated - **Weight:** Significant — allometric scaling (CL: 0.75, V: 1.0) - **CYP3A4/5 inhibitors:** Significant — voriconazole/posaconazole reduce CL by ~20% - Age, sex, ancestry, diagnosis, donor source, HLA mismatch: Not significant ")), nav_panel("References", div(class = "ref-box", tags$h5("\U0001F4DA Key References"), tags$ol( tags$li("Brooks JT, Keizer RJ, Long-Boyle JR, Kharbanda S, Dvorak CC, Friend BD. (2021) Population Pharmacokinetic Model Development of Tacrolimus in Pediatric and Young Adult Patients Undergoing Hematopoietic Cell Transplantation. Front Pharmacol 12:750672. doi:10.3389/fphar.2021.750672"), tags$li("Andrews LM, et al. (2018) A Population Pharmacokinetic Model to Predict the Individual Starting Dose of Tacrolimus Following Pediatric Renal Transplantation. Clin Pharmacokinet 57:475\u2013489"), tags$li("Wallin JE, et al. (2009) Population Pharmacokinetics of Tacrolimus in Pediatric Hematopoietic Stem Cell Transplant Recipients. Ther Drug Monit 31:457\u2013466"), tags$li("Birdwell KA, et al. (2015) CPIC Guidelines for CYP3A5 Genotype and Tacrolimus Dosing. Clin Pharmacol Ther 98:19\u201324") ), tags$h5("\U0001F48A Therapeutic Context"), tags$ul( tags$li(tags$strong("Drug:"), " Tacrolimus (FK506, Prograf\u00AE)"), tags$li(tags$strong("Class:"), " Calcineurin inhibitor (immunosuppressant)"), tags$li(tags$strong("Indication:"), " GVHD prophylaxis in allogeneic HCT"), tags$li(tags$strong("Therapeutic window:"), " 7\u201310 ng/mL (narrow)"), tags$li(tags$strong("Metabolism:"), " CYP3A4/CYP3A5 (hepatic)"), tags$li(tags$strong("Key DDIs:"), " Azole antifungals (voriconazole, posaconazole) \u2193 CL ~20%"), tags$li(tags$strong("Administration:"), " IV continuous infusion; oral conversion post-HCT"), tags$li(tags$strong("Study population:"), " 111 pediatric/young adult HCT patients (UCSF Benioff Children\u2019s Hospital, 2016\u20132020)") ) )) ), 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"), " \u2022 Built by Sunny \u2600\uFE0F (Husain Attarwala\u2019s AI Assistant)", tags$br(), tags$span(style = "font-size: 10px;", "For research and educational purposes only. Not for clinical decision-making.")) ) server <- function(input, output, session) { pk_params <- reactive({ inh_val <- ifelse(input$cyp_inh, 0.8, 1.0) CLi <- 4.2 * (input$wt / 70)^0.75 * inh_val V1i <- 61.9 * (input$wt / 70) Qi <- CLi * 2.0 V2i <- V1i * 2.0 k10 <- CLi / V1i k12 <- Qi / V1i k21 <- Qi / V2i sum_k <- k10 + k12 + k21 prod_k <- k10 * k21 alpha <- (sum_k + sqrt(sum_k^2 - 4 * prod_k)) / 2 beta <- (sum_k - sqrt(sum_k^2 - 4 * prod_k)) / 2 thalf_beta <- log(2) / beta rate_total <- input$dose_rate * input$wt css <- rate_total / CLi # Coefficient B for approach to SS B <- (k21 - beta) / (alpha - beta) # Time to 90% SS (approximate: alpha phase gone) time_90 <- ifelse(B > 0.1, -log(0.1 / B) / beta, -log(0.1) / beta) time_90 <- max(time_90, 0) list(css = css, thalf = thalf_beta, time_90 = time_90, rate_total = rate_total, CLi = CLi, alpha = alpha, beta = beta) }) sim_data <- reactive({ inh_val <- ifelse(input$cyp_inh, 0.8, 1.0) infusion_hours <- input$n_days * 24 washout_hours <- ifelse(input$show_washout, 24, 0) total_hours <- infusion_hours + washout_hours rate_total <- input$dose_rate * input$wt ev1 <- ev(amt = rate_total * infusion_hours, cmt = 1, rate = rate_total) mod %>% param(WT = input$wt, INH = inh_val) %>% ev(ev1) %>% mrgsim(end = total_hours, delta = 0.5) %>% as.data.frame() %>% mutate(time_h = time, time_d = time / 24) }) output$css <- renderText({ sprintf("%.1f", pk_params()$css) }) output$time_ss <- renderText({ sprintf("%.0f", pk_params()$time_90) }) output$thalf <- renderText({ sprintf("%.1f", pk_params()$thalf) }) output$total_rate <- renderText({ sprintf("%.1f", pk_params()$rate_total) }) output$pkPlot <- renderPlot({ d <- sim_data() infusion_end <- input$n_days * 24 p <- ggplot(d, aes(x = time_h, y = CP)) + annotate("rect", xmin = -Inf, xmax = Inf, ymin = 7, ymax = 10, fill = "#10b981", alpha = 0.15) + geom_hline(yintercept = c(7, 10), linetype = "dashed", color = "#10b981", alpha = 0.6) + annotate("text", x = max(d$time_h) * 0.02, y = 10.5, label = "Therapeutic: 7\u201310 ng/mL", hjust = 0, size = 3.5, color = "#10b981") + geom_line(color = "#8b5cf6", linewidth = 0.8) + labs(x = "Time (hours)", y = "Concentration (ng/mL)", title = paste0("Tacrolimus IV Continuous Infusion \u2022 ", input$dose_rate, " mcg/kg/h \u2022 ", input$wt, " kg patient", ifelse(input$cyp_inh, " \u2022 + CYP inhibitor", ""))) + theme_minimal(base_size = 14) if (input$show_washout) { p <- p + geom_vline(xintercept = infusion_end, linetype = "dotted", color = "#e74c3c", alpha = 0.7) + annotate("text", x = infusion_end + 1, y = max(d$CP, na.rm = TRUE) * 0.95, label = "Infusion stopped", hjust = 0, size = 3, color = "#e74c3c") } if (input$log_scale) p <- p + scale_y_log10() p }) } shinyApp(ui = ui, server = server)