library(shiny) library(bslib) library(mrgsolve) library(dplyr) library(ggplot2) # Rucaparib PopPK Model # Two-compartment with sequential zero-order release and first-order absorption # Source: Green et al. Cancer Chemother Pharmacol (2022) 89:671-682 # DOI: 10.1007/s00280-022-04413-7 model_code <- ' $PARAM @annotated CL : 10.3 : Clearance (L/h) VC : 16.9 : Central volume of distribution (L) Q : 17.4 : Intercompartmental clearance (L/h) VP : 166 : Peripheral volume of distribution (L) KA : 0.0718 : Absorption rate constant (1/h) D1 : 0.619 : Duration of zero-order release (h) F1 : 0.372 : Absolute oral bioavailability CLCR : 83 : Creatinine clearance (mL/min) ALB : 41 : Serum albumin (g/L) $CMT @annotated DEPOT : Oral depot ~ zero-order release (mg) CENT : Central compartment (mg) PERIPH : Peripheral compartment (mg) $MAIN double CLi = CL * pow(CLCR / 83.0, 0.313) * pow(ALB / 41.0, 0.720); double VCi = VC; double Qi = Q; double VPi = VP; F_DEPOT = F1; D_DEPOT = D1; $ODE dxdt_DEPOT = -KA * DEPOT; dxdt_CENT = KA * DEPOT - (CLi/VCi)*CENT - (Qi/VCi)*CENT + (Qi/VPi)*PERIPH; dxdt_PERIPH = (Qi/VCi)*CENT - (Qi/VPi)*PERIPH; $TABLE double CP = CENT / VCi * 1000.0; $CAPTURE CP ' mod <- mcode("rucaparib", 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 = "Rucaparib (Rubraca\u00AE) Population PK Simulator", theme = app_theme, sidebar = sidebar( title = "Simulation Settings", width = 340, h6("Dosing"), sliderInput("dose", "Dose (mg)", min = 200, max = 600, value = 600, step = 100), sliderInput("interval", "Dosing Interval (h)", min = 12, max = 24, value = 12, step = 12), numericInput("n_days", "Duration (days)", value = 14, min = 1, max = 30), hr(), h6("Patient Characteristics"), sliderInput("clcr", "Creatinine Clearance (mL/min)", min = 30, max = 180, value = 83, step = 5), sliderInput("alb", "Serum Albumin (g/L)", min = 25, max = 50, value = 41, step = 1), hr(), checkboxInput("log_scale", "Log Scale (Y-axis)", value = FALSE) ), navset_card_tab( title = "Rucaparib PK 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("cmax")), div(class = "metric-label", "Cmax,ss (ng/mL)")), div(class = "metric-card metric-warning", div(class = "metric-value", textOutput("ctrough")), div(class = "metric-label", "Ctrough,ss (ng/mL)")), div(class = "metric-card metric-primary", div(class = "metric-value", textOutput("auc")), div(class = "metric-label", HTML("AUC0-\u03C4 (ng\u00B7h/mL)"))), div(class = "metric-card metric-info", div(class = "metric-value", textOutput("thalf")), div(class = "metric-label", HTML("t\u00BD (h)"))) ), plotOutput("pkPlot", height = "500px") ), nav_panel("Model Information", markdown(" ## Rucaparib — Two-Compartment Population PK Model **Structure:** Two-compartment with sequential zero-order release and first-order absorption, first-order elimination **Estimation method:** NONMEM FOCEI **Patients:** 458 patients across 3 studies (Study 1014, Study 10, ARIEL2) **Observations:** 4,518 plasma concentrations ### Population PK Parameters | Parameter | Estimate | Description | |---|---|---| | CL | 10.3 L/h | Clearance | | Vc | 16.9 L | Central volume | | Q | 17.4 L/h | Intercompartmental clearance | | Vp | 166 L | Peripheral volume | | Ka | 0.0718 h\u207B\u00B9 | Absorption rate constant | | D1 | 0.619 h | Zero-order release duration | | F1 | 37.2% | Absolute oral bioavailability | | t\u00BD | ~25.9 h | Terminal half-life | ### Covariate Effects - **Creatinine clearance on CL:** Power model (exponent 0.313, reference 83 mL/min) - **Albumin on CL:** Power model (exponent 0.720, reference 41 g/L) - Mild renal impairment: ~15% higher AUC; moderate: ~33% higher AUC - CYP1A2/CYP2D6 polymorphisms: No clinically significant effect - Concomitant PPIs: No clinically significant effect on absorption ### Between-Subject Variability | Parameter | %CV | Shrinkage | |---|---|---| | CL | 48.8% | 8.8% | | Ka | 63.5% | 5.2% | | D1 | 111% | 11.8% | ")), nav_panel("References", div(class = "ref-box", tags$h5("\U0001F4DA Key References"), tags$ol( tags$li("Green ML et al. (2022) Cancer Chemother Pharmacol 89:671-682 — Population pharmacokinetics of rucaparib in patients with advanced ovarian cancer or other solid tumors"), tags$li("Shapiro GI et al. (2013) Clin Cancer Res 19:268-278 — Phase I dose-escalation study of rucaparib (Study 1014)"), tags$li("Kristeleit R et al. (2017) Clin Cancer Res 23:4095-4106 — Phase I-II study of oral rucaparib (Study 10)"), tags$li("Swisher EM et al. (2017) Lancet Oncol 18:75-87 — ARIEL2 rucaparib in platinum-sensitive recurrent ovarian cancer"), tags$li("Coleman RL et al. (2017) Lancet 376:2271-2281 — ARIEL3 rucaparib maintenance therapy") ), tags$h5("\U0001F48A Therapeutic Context"), tags$ul( tags$li(tags$strong("Drug:"), " Rucaparib camsylate (Rubraca\u00AE)"), tags$li(tags$strong("Class:"), " Poly(ADP-ribose) polymerase (PARP) inhibitor"), tags$li(tags$strong("Targets:"), " PARP1, PARP2, PARP3"), tags$li(tags$strong("Indications:"), " Maintenance treatment of recurrent ovarian cancer (platinum-sensitive); BRCA-mutated relapsed ovarian cancer; BRCA-mutated mCRPC"), tags$li(tags$strong("Approved dose:"), " 600 mg BID oral"), tags$li(tags$strong("Protein binding:"), " ~70%"), tags$li(tags$strong("Metabolism:"), " CYP2D6 and CYP1A2; major metabolite M324 (inactive)"), tags$li(tags$strong("Excretion:"), " Fecal 71.9%, Urinary 17.4%"), tags$li(tags$strong("Steady state:"), " Achieved after ~1 week BID dosing; ~4-fold accumulation") ) )) ), 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's AI Assistant)", br(), tags$span(style = "font-size: 10px;", "For research and educational purposes only. Not for clinical decision-making.")) ) server <- function(input, output, session) { sim_data <- reactive({ n_doses <- floor(input$n_days * 24 / input$interval) ev1 <- ev(amt = input$dose, cmt = 1, ii = input$interval, addl = n_doses - 1) mod %>% param(CLCR = input$clcr, ALB = input$alb) %>% ev(ev1) %>% mrgsim(end = input$n_days * 24, delta = 0.25) %>% as.data.frame() %>% mutate(time_h = time) }) output$cmax <- renderText({ d <- sim_data() last_start <- (input$n_days - 2) * 24 last <- d %>% filter(time >= last_start) sprintf("%.0f", max(last$CP, na.rm = TRUE)) }) output$ctrough <- renderText({ d <- sim_data() last_start <- (input$n_days - 2) * 24 last <- d %>% filter(time >= last_start) # Get trough: minimum in the last dosing intervals sprintf("%.0f", min(last$CP[last$CP > 0], na.rm = TRUE)) }) output$auc <- renderText({ d <- sim_data() tau <- input$interval last_dose_time <- (input$n_days - 1) * 24 last <- d %>% filter(time >= last_dose_time, time <= last_dose_time + tau) if (nrow(last) > 1) { auc <- sum(diff(last$time) * (head(last$CP, -1) + tail(last$CP, -1)) / 2) sprintf("%.0f", auc) } else { "—" } }) output$thalf <- renderText({ CLi <- 10.3 * (input$clcr / 83)^0.313 * (input$alb / 41)^0.720 Vss <- 16.9 + 166 sprintf("%.1f", log(2) * Vss / CLi) }) output$pkPlot <- renderPlot({ d <- sim_data() p <- ggplot(d, aes(x = time_h, y = CP)) + geom_line(color = "#8b5cf6", linewidth = 0.8) + labs( x = "Time (hours)", y = "Plasma Concentration (ng/mL)", title = paste0("Rucaparib ", input$dose, " mg Q", input$interval, "H — Population PK Simulation") ) + theme_minimal(base_size = 14) + theme( plot.title = element_text(face = "bold", size = 14), panel.grid.minor = element_blank() ) if (input$log_scale) p <- p + scale_y_log10() p }) } shinyApp(ui = ui, server = server)