library(shiny) library(bslib) library(mrgsolve) library(dplyr) library(ggplot2) library(tidyr) # --------------------------------------------------------------------------- # mrgsolve PBPK-derived parent–metabolite model # Based on: Sharma et al. (2020) Environ Res — PBPK model for flutamide # Parameters calibrated to reproduce Radwanski et al. (1989) human PK data # --------------------------------------------------------------------------- model_code <- ' $PARAM @annotated CL : 260 : Apparent clearance - flutamide (L/h) Vc : 2200 : Volume of distribution - flutamide (L) KA : 0.80 : Absorption rate constant (1/h) F1 : 0.90 : Oral bioavailability (fraction) Fm : 0.85 : Fraction metabolised to 2-OH-flutamide via CYP1A2 CLm : 12.5 : Apparent clearance - 2-OH-flutamide (L/h) Vm : 174 : Volume of distribution - 2-OH-flutamide (L) WT : 70 : Body weight (kg) CYP1A2 : 1.0 : CYP1A2 activity (fraction of normal) $CMT @annotated DEPOT : Oral depot (mg) FLU : Flutamide central compartment (mg) OH : 2-OH-Flutamide central compartment (mg) $MAIN F_DEPOT = F1; $ODE double ke = CL * pow(WT / 70.0, 0.75) * CYP1A2 / (Vc * (WT / 70.0)); double kem = CLm * pow(WT / 70.0, 0.75) / (Vm * (WT / 70.0)); dxdt_DEPOT = -KA * DEPOT; dxdt_FLU = KA * DEPOT - ke * FLU; dxdt_OH = Fm * ke * FLU - kem * OH; $TABLE double Vci = Vc * (WT / 70.0); double Vmi = Vm * (WT / 70.0); double CP_flu = (FLU / Vci) * 1000.0; // ng/mL double CP_oh = (OH / Vmi) * 1000.0; // ng/mL $CAPTURE CP_flu CP_oh ' mod <- mcode("flutamide_pbpk", model_code, quiet = TRUE) # --------------------------------------------------------------------------- # Theme # --------------------------------------------------------------------------- app_theme <- bs_theme( version = 5, bootswatch = "flatly", primary = "#8b5cf6" ) |> bs_add_rules(" .metric-card { background: #f8f9fa; border-radius: 8px; padding: 15px; margin: 4px; text-align: center; border: 1px solid #dee2e6; } .metric-value { font-size: 22px; font-weight: bold; color: #2c3e50; } .metric-label { font-size: 12px; color: #7f8c8d; } .metric-unit { font-size: 10px; color: #adb5bd; } .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; } .kp-table { font-size: 12px; } ") # --------------------------------------------------------------------------- # UI # --------------------------------------------------------------------------- ui <- page_sidebar( title = "Flutamide PBPK Simulator", theme = app_theme, sidebar = sidebar( title = "Simulation Settings", width = 330, h6("Dosing"), sliderInput("dose", "Dose (mg)", min = 125, max = 500, value = 250, step = 125), helpText("Standard clinical dose: 250 mg q8h (TID)"), sliderInput("interval", "Dosing Interval (h)", min = 6, max = 12, value = 8, step = 1), numericInput("n_days", "Duration (days)", value = 5, min = 1, max = 14), hr(), h6("Patient Characteristics"), sliderInput("wt", "Weight (kg)", min = 40, max = 130, value = 70, step = 5), hr(), h6("Pharmacogenomics / Drug Interactions"), sliderInput("cyp1a2", "CYP1A2 Activity (fraction)", min = 0.1, max = 2.5, value = 1.0, step = 0.1), helpText("Normal = 1.0 | Inhibited (fluvoxamine, cipro) = 0.2–0.5 |", "Induced (smoking, omeprazole) = 1.5–2.5"), hr(), checkboxInput("log_scale", "Log Scale (Y-axis)", value = FALSE) ), # ---- Metric cards ------------------------------------------------------- layout_column_wrap( width = 1/4, fill = FALSE, div(class = "metric-card metric-success", div(class = "metric-value", textOutput("cmax_flu")), div(class = "metric-label", "Flutamide Cmax"), div(class = "metric-unit", "ng/mL — at steady state")), div(class = "metric-card metric-warning", div(class = "metric-value", textOutput("cmax_oh")), div(class = "metric-label", "2-OH-Flutamide Cmax"), div(class = "metric-unit", "ng/mL — active metabolite")), div(class = "metric-card metric-primary", div(class = "metric-value", textOutput("thalf_flu")), div(class = "metric-label", "Flutamide t\u00BD"), div(class = "metric-unit", "hours")), div(class = "metric-card metric-info", div(class = "metric-value", textOutput("thalf_oh")), div(class = "metric-label", "2-OH-Flutamide t\u00BD"), div(class = "metric-unit", "hours")) ), # ---- Main plot card ----------------------------------------------------- card( full_screen = TRUE, min_height = "500px", plotOutput("pkPlot", height = "460px") ), # ---- Information tabs --------------------------------------------------- navset_card_underline( nav_panel("Model Information", div(style = "padding: 16px;", markdown(" ## Flutamide PBPK-Derived PK Simulator **Model Type:** PBPK-derived parent–metabolite (1-compartment per species) **Structure:** Oral depot → Flutamide (central) → 2-OH-Flutamide (central) **Source:** Sharma RP et al. (2020) *Environmental Research* 183:108948 ### PBPK Model Parameters (Human, 70 kg Reference) | Parameter | Symbol | Value | Units | Source | |-----------|--------|-------|-------|--------| | Absorption rate | Ka | 0.80 | h⁻¹ | PBPK (kgut) | | Oral bioavailability | F | 0.90 | fraction | Estimated | | Unbound fraction (flutamide) | fu | 0.09 | — | Asakawa et al. (1995) | | Unbound fraction (Flu-OH) | fu₁ | 0.05 | — | Sjögren et al. (2014) | | CYP1A2 → Flu-OH Km | Km₁ | 1.113 | mg/L | Sjo et al. (2009) | | CYP1A2 → Flu-OH Vmax | Vmax₁ | 0.079 | µg/min/mg MSP | Sjo et al. (2009) | | FLU-6 pathway Km | Km₂ | 23.754 | mg/L | Wen et al. (2008) | | FLU-1 pathway Km | Km₃ | 82.863 | mg/L | Kobayashi et al. (2012) | | Flutamide apparent t½ | — | ~5.9 | h | PBPK-derived | | 2-OH-Flutamide t½ | — | ~9.6 | h | Radwanski (1989) | ### Tissue Partition Coefficients (Kp = tissue/plasma) | Tissue | Kp | Implication | |--------|----|-------------| | Liver | 5.57 | High liver accumulation — hepatotoxicity risk | | Prostate | 2.17 | 2× plasma — target tissue enrichment | | Kidney | 2.63 | Renal distribution | | Fat | 2.78 | Lipophilic storage — prolonged exposure | | Gonads | 1.70 | Off-target endocrine effects | | Lung | 1.65 | Modest pulmonary distribution | ### Allometric Scaling - CL and CLm scaled as BW^0.75 - Vc and Vm scaled linearly with BW ") ) ), nav_panel("References", div(class = "ref-box", style = "margin: 16px;", tags$h5("\U0001F4DA Key References"), tags$ol( tags$li("Sharma RP, Kumar V, Schuhmacher M et al. (2020) Development and evaluation of a harmonized whole body PBPK model for flutamide in rats and its extrapolation to humans. ", tags$em("Environ Res"), " 183:108948."), tags$li("Radwanski E, Perentesis G, Symchowicz S, Zampaglione N. (1989) Single and multiple dose pharmacokinetic evaluation of flutamide in normal geriatric volunteers. ", tags$em("J Clin Pharmacol"), " 29:554–558."), tags$li("Sjögren E, Tammela TL, Lennernäs B et al. (2014) Pharmacokinetics of injectable modified-release 2-hydroxyflutamide using a semi-physiologically based biopharmaceutical model. ", tags$em("Mol Pharm"), " 11:3097–3111."), tags$li("Kobayashi Y et al. (2012) In vitro metabolic data used for IVIVE scaling. ", tags$em("Drug Metab Dispos")), tags$li("Asakawa N et al. (1995) Tissue distribution and partition coefficients from in vivo rat studies.") ), tags$h5("\U0001F48A Therapeutic Context"), tags$ul( tags$li(tags$strong("Drug class:"), " Non-steroidal antiandrogen (androgen receptor antagonist)"), tags$li(tags$strong("Brand:"), " Eulexin® (125 mg capsules, TID = 750 mg/day)"), tags$li(tags$strong("Mechanism:"), " Competitive AR blockade — active form is 2-OH-flutamide (Flu-OH)"), tags$li(tags$strong("Indication:"), " Stage D2 metastatic prostate cancer (combined with LHRH agonist)"), tags$li(tags$strong("Metabolism:"), " CYP1A2 (primary → Flu-OH), nitro-reduction (FLU-6, FLU-1)"), tags$li(tags$strong("Black box warning:"), " Hepatotoxicity — monitor LFTs monthly for first 4 months"), tags$li(tags$strong("Prostate enrichment:"), " Kp(prostate/plasma) = 2.17 → ~2× higher than plasma") ) ) ) ), # ---- Footer ------------------------------------------------------------- div( style = "text-align: center; padding: 20px; margin-top: 20px; 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 # --------------------------------------------------------------------------- server <- function(input, output, session) { tryCatch(thematic::thematic_shiny(), error = function(e) NULL) # Reactive simulation sim_data <- reactive({ shiny::req(input$dose, input$interval, input$n_days, input$wt, input$cyp1a2) n_doses <- max(1L, floor(input$n_days * 24 / input$interval)) ev1 <- ev(amt = input$dose, cmt = 1, ii = input$interval, addl = n_doses - 1L) mod |> param(WT = input$wt, CYP1A2 = input$cyp1a2) |> ev(ev1) |> mrgsim(end = input$n_days * 24, delta = 0.1) |> as.data.frame() }) # Helper: last-interval window last_interval_data <- reactive({ d <- sim_data() t0 <- max(0, (input$n_days - 1) * 24) dplyr::filter(d, time >= t0) }) # Metric: Flutamide Cmax output$cmax_flu <- renderText({ sprintf("%.1f", max(last_interval_data()$CP_flu, na.rm = TRUE)) }) # Metric: 2-OH-Flu Cmax output$cmax_oh <- renderText({ sprintf("%.1f", max(last_interval_data()$CP_oh, na.rm = TRUE)) }) # Metric: Flutamide t½ output$thalf_flu <- renderText({ shiny::req(input$wt, input$cyp1a2) ke <- 260 * (input$wt / 70)^0.75 * input$cyp1a2 / (2200 * (input$wt / 70)) sprintf("%.1f", log(2) / ke) }) # Metric: 2-OH-Flu t½ output$thalf_oh <- renderText({ shiny::req(input$wt) kem <- 12.5 * (input$wt / 70)^0.75 / (174 * (input$wt / 70)) sprintf("%.1f", log(2) / kem) }) # PK plot output$pkPlot <- renderPlot({ d <- sim_data() # Long format for both species d_long <- tidyr::pivot_longer( d, cols = c("CP_flu", "CP_oh"), names_to = "species", values_to = "conc" ) |> dplyr::mutate(species = dplyr::case_when( species == "CP_flu" ~ "Flutamide", species == "CP_oh" ~ "2-OH-Flutamide (active)" )) # Filter near-zero values when log scale requested if (input$log_scale) { d_long <- dplyr::filter(d_long, conc > 0.01) } p <- ggplot2::ggplot(d_long, ggplot2::aes(x = time, y = conc, color = species)) + ggplot2::geom_line(linewidth = 0.9) + ggplot2::scale_color_manual( values = c( "Flutamide" = "#8b5cf6", "2-OH-Flutamide (active)" = "#f59e0b" ) ) + ggplot2::labs( x = "Time (hours)", y = "Plasma Concentration (ng/mL)", title = paste0( "Flutamide ", input$dose, " mg Q", input$interval, "H — ", input$n_days, "-day PK Profile" ), subtitle = paste0( "Weight: ", input$wt, " kg | CYP1A2 activity: ", input$cyp1a2, "\u00D7 | Kp(prostate/plasma) = 2.17" ), color = NULL, caption = paste0( "PBPK-derived model: Sharma et al. (2020) Environ Res | ", "Calibrated to Radwanski et al. (1989) J Clin Pharmacol" ) ) + ggplot2::theme_minimal(base_size = 13) + ggplot2::theme( legend.position = "top", plot.title = ggplot2::element_text(face = "bold"), plot.subtitle = ggplot2::element_text(color = "#6c757d", size = 10), plot.caption = ggplot2::element_text(color = "#adb5bd", size = 9), panel.grid.minor = ggplot2::element_blank() ) if (input$log_scale) { p <- p + ggplot2::scale_y_log10( labels = scales::label_comma() ) } p }) } shinyApp(ui = ui, server = server)