library(shiny) library(bslib) library(mrgsolve) library(dplyr) library(ggplot2) library(tidyr) # ── mrgsolve model: 2-compartment PK for IN nalmefene & IN naloxone ── # Source: Laffont CM et al. (2024) Front. Psychiatry 15:1399803 # PopPK parameters from Tables 1 & 2 # Mixed zero-order + first-order absorption with lag time model_code <- ' $PARAM @annotated NMF_CLF : 63.7 : Nalmefene CL/F (L/h) NMF_VCF : 15.2 : Nalmefene Vc/F (L) NMF_QF : 81.3 : Nalmefene Q/F (L/h) NMF_VPF : 522 : Nalmefene Vp/F (L) NMF_KA : 0.497 : Nalmefene IN Ka (1/h) NLX_CLF : 396 : Naloxone CL/F (L/h) NLX_VCF : 65.7 : Naloxone Vc/F (L) NLX_QF : 284 : Naloxone Q/F (L/h) NLX_VPF : 102 : Naloxone Vp/F (L) NLX_KA : 0.998 : Naloxone IN Ka (1/h) WT : 74.7 : Body weight (kg) $CMT @annotated NMF_DEPOT : Nalmefene first-order depot (mg) NMF_CENT : Nalmefene central (mg) NMF_PERI : Nalmefene peripheral (mg) NLX_DEPOT : Naloxone first-order depot (mg) NLX_CENT : Naloxone central (mg) NLX_PERI : Naloxone peripheral (mg) $MAIN double NMF_CL = NMF_CLF * pow(WT / 74.7, 0.572); $ODE dxdt_NMF_DEPOT = -NMF_KA * NMF_DEPOT; dxdt_NMF_CENT = NMF_KA * NMF_DEPOT - (NMF_CL / NMF_VCF) * NMF_CENT - (NMF_QF / NMF_VCF) * NMF_CENT + (NMF_QF / NMF_VPF) * NMF_PERI; dxdt_NMF_PERI = (NMF_QF / NMF_VCF) * NMF_CENT - (NMF_QF / NMF_VPF) * NMF_PERI; dxdt_NLX_DEPOT = -NLX_KA * NLX_DEPOT; dxdt_NLX_CENT = NLX_KA * NLX_DEPOT - (NLX_CLF / NLX_VCF) * NLX_CENT - (NLX_QF / NLX_VCF) * NLX_CENT + (NLX_QF / NLX_VPF) * NLX_PERI; dxdt_NLX_PERI = (NLX_QF / NLX_VCF) * NLX_CENT - (NLX_QF / NLX_VPF) * NLX_PERI; $TABLE double CP_NMF = NMF_CENT / NMF_VCF * 1000.0; double CP_NLX = NLX_CENT / NLX_VCF * 1000.0; $CAPTURE CP_NMF CP_NLX ' mod <- mcode("antag_pk", model_code) # Mixed absorption constants (from Tables 1 & 2) NMF_FK0 <- 0.0485 # Fraction zero-order absorption (nalmefene) NMF_D2 <- 0.302 # Zero-order duration (h) NMF_ALAG <- 0.0615 # Lag time first-order (h) NLX_FK0 <- 0.183 # Fraction zero-order absorption (naloxone) NLX_D2 <- 0.689 # Zero-order duration (h) NLX_ALAG <- 0.0717 # Lag time first-order (h) 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; } .metric-label { font-size: 12px; color: #7f8c8d; } .nmf .metric-value { color: #2563eb; } .nlx .metric-value { color: #dc2626; } .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; } .highlight-box { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; padding: 16px; margin: 12px 0; } .danger-box { background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 16px; margin: 12px 0; } ") ui <- page_sidebar( title = "IN Nalmefene vs IN Naloxone — Opioid Overdose PK Simulator", theme = app_theme, sidebar = sidebar( title = "Simulation Settings", width = 340, h6(tags$span(style = "color:#2563eb;", "\u25A0"), " Nalmefene (OPVEE\u00AE)"), sliderInput("nmf_dose", "Dose per spray (mg HCl)", min = 1, max = 6, value = 3, step = 1), sliderInput("nmf_n", "Number of sprays", min = 1, max = 4, value = 1, step = 1), hr(), h6(tags$span(style = "color:#dc2626;", "\u25A0"), " Naloxone (NARCAN\u00AE)"), sliderInput("nlx_dose", "Dose per spray (mg HCl)", min = 2, max = 8, value = 4, step = 2), sliderInput("nlx_n", "Number of sprays", min = 1, max = 4, value = 1, step = 1), hr(), h6("Patient / Display"), sliderInput("wt", "Weight (kg)", min = 50, max = 120, value = 75, step = 5), sliderInput("sim_hours", "Simulation Duration (h)", min = 4, max = 24, value = 12, step = 2), checkboxInput("log_scale", "Log Scale (Y-axis)", value = FALSE) ), navset_card_tab( title = "Nalmefene vs Naloxone PK Comparison", full_screen = TRUE, nav_panel("Simulation", layout_column_wrap( width = 1/4, fill = FALSE, div(class = "metric-card nmf", div(class = "metric-value", textOutput("nmf_cmax")), div(class = "metric-label", "Nalmefene Cmax (ng/mL)")), div(class = "metric-card nmf", div(class = "metric-value", textOutput("nmf_auc")), div(class = "metric-label", "Nalmefene AUC (ng\u00B7h/mL)")), div(class = "metric-card nlx", div(class = "metric-value", textOutput("nlx_cmax")), div(class = "metric-label", "Naloxone Cmax (ng/mL)")), div(class = "metric-card nlx", div(class = "metric-value", textOutput("nlx_auc")), div(class = "metric-label", "Naloxone AUC (ng\u00B7h/mL)")) ), plotOutput("pkPlot", height = "500px") ), nav_panel("Cardiac Arrest Outcomes", div(class = "highlight-box", tags$h5("Key Finding"), tags$p("A single dose of 3 mg IN nalmefene produced cardiac arrest reductions ", "comparable to 4 simultaneous doses (16 mg total) of IN naloxone across ", "all fentanyl and carfentanil overdose scenarios.") ), tags$h5("Chronic Opioid Users \u2014 Fentanyl Overdose"), tags$p("Simulated incidence of cardiac arrest (%) in 2000 virtual patients. ", "Median (95% CI) shown."), tableOutput("ca_chronic"), tags$h5("Opioid Na\u00EFve Individuals \u2014 Fentanyl Overdose"), tableOutput("ca_naive"), div(class = "danger-box", tags$h5("Clinical Implications"), tags$ul( tags$li(">90% of opioid overdose deaths are linked to synthetic opioids (fentanyl)"), tags$li("Standard 4 mg IN naloxone may be insufficient for potent synthetic opioid overdoses"), tags$li("Nalmefene offers faster onset, higher potency, and longer duration (t\u00BD 7\u201311h vs ~2h)"), tags$li("Multiple naloxone doses increase risk of sustained precipitated withdrawal without", " proportional improvement in rescue rate (Strauss et al. 2024)") ) ) ), nav_panel("Model Information", markdown(" ## Translational PK/PD Model for Opioid Overdose Rescue ### Source Laffont CM et al. (2024) *Frontiers in Psychiatry* 15:1399803. Expansion of the Mann et al. (2022) translational model originally developed for IM naloxone. ### Nalmefene PK Model (IN, 2-Compartment) | Parameter | Value | Description | |-----------|-------|-------------| | CL/F | 63.7 L/h | Apparent clearance (weight-scaled) | | Vc/F | 15.2 L | Apparent central volume | | Q/F | 81.3 L/h | Intercompartmental clearance | | Vp/F | 522 L | Apparent peripheral volume | | Ka | 0.497 h\u207B\u00B9 | First-order absorption rate | | FK0 | 4.85% | Fraction zero-order absorption | | D2 | 0.302 h | Zero-order duration | | ALAG | 0.0615 h | Absorption lag time | | F_rel (IN vs IM) | 83.4% | Relative bioavailability | | **t\u00BD** | **7.1\u201311 h** | **Terminal half-life** | **Weight covariate:** CL/F = 63.7 \u00D7 (WT/74.7)^0.572 ### Naloxone PK Model (IN, 2-Compartment) | Parameter | Value | Description | |-----------|-------|-------------| | CL/F | 396 L/h | Apparent clearance | | Vc/F | 65.7 L | Apparent central volume | | Q/F | 284 L/h (fixed) | Intercompartmental clearance | | Vp/F | 102 L (fixed) | Apparent peripheral volume | | Ka | 0.998 h\u207B\u00B9 | First-order absorption rate | | FK0 | 18.3% | Fraction zero-order absorption | | D2 | 0.689 h | Zero-order duration | | ALAG | 0.0717 h | Absorption lag time | | **t\u00BD** | **~2 h** | **Terminal half-life** | ### Absorption Model Both drugs use **parallel zero-order and first-order absorption** with a lag time on the first-order component. The zero-order fraction is delivered as a constant-rate input into the central compartment, while the first-order fraction enters via a depot compartment after the lag time. ### Key Pharmacological Differences - **Nalmefene** has higher \u03BC-opioid receptor affinity than naloxone - **Nalmefene** achieves higher plasma concentrations in the critical early minutes after dosing - **Nalmefene** has a significantly longer duration of action (5\u20136\u00D7 longer t\u00BD) - Both are FDA-approved for intranasal treatment of opioid overdose ### Translational Model Components 1. **PK component:** Compartmental PK for opioids and antagonists 2. **Receptor binding:** Competitive binding at \u03BC-opioid receptor (kon, koff, steepness n) 3. **PD component:** Opioid-bound receptors reduce ventilatory drives 4. **Physiological component:** Gas exchange, metabolism, blood flow control ")), nav_panel("References", div(class = "ref-box", tags$h5("Key References"), tags$ol( tags$li("Laffont CM, Purohit P, Delcamp N, Gonzalez-Garcia I, Skolnick P. (2024) ", "Comparison of intranasal naloxone and intranasal nalmefene in a translational ", "model assessing the impact of synthetic opioid overdose on respiratory depression ", "and cardiac arrest. ", tags$em("Front. Psychiatry"), " 15:1399803. ", tags$a(href = "https://doi.org/10.3389/fpsyt.2024.1399803", target = "_blank", "DOI")), tags$li("Mann J et al. (2022) Development of a translational model to assess the impact ", "of opioid overdose and naloxone dosing on respiratory depression and cardiac arrest. ", tags$em("Clin Pharmacol Ther"), " 112:1020-1032. ", tags$a(href = "https://doi.org/10.1002/cpt.2696", target = "_blank", "DOI")), tags$li("Crystal R, Ellison M, Purdon C, Skolnick P. (2024) Pharmacokinetic properties ", "of an FDA-approved intranasal nalmefene formulation for the treatment of opioid ", "overdose. ", tags$em("Clin Pharmacol Drug Dev"), " 13:58-69. ", tags$a(href = "https://doi.org/10.1002/cpdd.1312", target = "_blank", "DOI")), tags$li("Ellison M, Hutton E, Webster L, Skolnick P. (2024) Reversal of opioid-induced ", "respiratory depression in healthy volunteers: comparison of intranasal nalmefene ", "and intranasal naloxone. ", tags$em("J Clin Pharmacol"), ". ", tags$a(href = "https://doi.org/10.1002/jcph.2421", target = "_blank", "DOI")), tags$li("Krieter P et al. (2016) Pharmacokinetic properties and human use characteristics ", "of an FDA-approved intranasal naloxone product for the treatment of opioid overdose. ", tags$em("J Clin Pharmacol"), " 56:1243-1253. ", tags$a(href = "https://doi.org/10.1002/jcph.759", target = "_blank", "DOI")), tags$li("Strauss DG et al. (2024) Intranasal naloxone repeat dosing strategies and fentanyl ", "overdose: A simulation-based randomized clinical trial. ", tags$em("JAMA Netw Open"), " 7:e2351839. ", tags$a(href = "https://doi.org/10.1001/jamanetworkopen.2023.51839", target = "_blank", "DOI")) ), tags$h5("Clinical Context"), tags$ul( tags$li(tags$strong("Nalmefene (OPVEE\u00AE):"), " 3 mg IN, FDA-approved May 2023 (Indivior)"), tags$li(tags$strong("Naloxone (NARCAN\u00AE):"), " 4 mg IN, FDA-approved Nov 2015; OTC March 2023"), tags$li(tags$strong("Class:"), " \u03BC-Opioid Receptor Antagonists"), tags$li(tags$strong("Therapeutic Area:"), " Emergency Medicine / Opioid Overdose Rescue"), tags$li(tags$strong("Epidemic context:"), " >80,000 opioid overdose deaths/year in the US; ", ">90% linked to synthetic opioids like fentanyl") ) )) ), # ── 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"), " \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({ # Total doses (simultaneous administration) nmf_total <- input$nmf_dose * input$nmf_n nlx_total <- input$nlx_dose * input$nlx_n # Nalmefene events: zero-order into CENT (cmt 2) + first-order into DEPOT (cmt 1) nmf_zo_amt <- NMF_FK0 * nmf_total nmf_fo_amt <- (1 - NMF_FK0) * nmf_total ev_nmf <- ev(amt = nmf_zo_amt, cmt = 2, rate = nmf_zo_amt / NMF_D2, time = 0) + ev(amt = nmf_fo_amt, cmt = 1, time = NMF_ALAG) # Naloxone events: zero-order into CENT (cmt 5) + first-order into DEPOT (cmt 4) nlx_zo_amt <- NLX_FK0 * nlx_total nlx_fo_amt <- (1 - NLX_FK0) * nlx_total ev_nlx <- ev(amt = nlx_zo_amt, cmt = 5, rate = nlx_zo_amt / NLX_D2, time = 0) + ev(amt = nlx_fo_amt, cmt = 4, time = NLX_ALAG) ev_all <- ev_nmf + ev_nlx mod %>% param(WT = input$wt) %>% ev(ev_all) %>% mrgsim(end = input$sim_hours, delta = 0.05) %>% as.data.frame() }) # ── Metric outputs ── output$nmf_cmax <- renderText({ d <- sim_data() sprintf("%.2f", max(d$CP_NMF, na.rm = TRUE)) }) output$nmf_auc <- renderText({ d <- sim_data() auc <- sum(diff(d$time) * (head(d$CP_NMF, -1) + tail(d$CP_NMF, -1)) / 2) sprintf("%.1f", auc) }) output$nlx_cmax <- renderText({ d <- sim_data() sprintf("%.2f", max(d$CP_NLX, na.rm = TRUE)) }) output$nlx_auc <- renderText({ d <- sim_data() auc <- sum(diff(d$time) * (head(d$CP_NLX, -1) + tail(d$CP_NLX, -1)) / 2) sprintf("%.1f", auc) }) # ── PK overlay plot ── output$pkPlot <- renderPlot({ d <- sim_data() %>% select(time, Nalmefene = CP_NMF, Naloxone = CP_NLX) %>% pivot_longer(-time, names_to = "Drug", values_to = "Concentration") nmf_total <- input$nmf_dose * input$nmf_n nlx_total <- input$nlx_dose * input$nlx_n p <- ggplot(d, aes(x = time, y = Concentration, color = Drug)) + geom_line(linewidth = 1.0) + scale_color_manual(values = c("Nalmefene" = "#2563eb", "Naloxone" = "#dc2626")) + labs( x = "Time (hours)", y = "Plasma Concentration (ng/mL)", title = paste0("Nalmefene ", nmf_total, " mg vs Naloxone ", nlx_total, " mg (Intranasal)"), subtitle = paste0("Weight: ", input$wt, " kg | Simultaneous administration | ", "Nalmefene t\u00BD \u22487\u201311h, Naloxone t\u00BD \u22482h") ) + theme_minimal(base_size = 14) + theme( legend.position = "top", legend.text = element_text(size = 12), plot.subtitle = element_text(size = 11, color = "#6b7280") ) if (input$log_scale) p <- p + scale_y_log10() p }) # ── Cardiac arrest outcome tables ── output$ca_chronic <- renderTable({ data.frame( Treatment = c( "No antagonist", "2 mg IM naloxone", "4 mg IN naloxone", "2\u00D74 mg IN naloxone (8 mg)", "3\u00D74 mg IN naloxone (12 mg)", "4\u00D74 mg IN naloxone (16 mg)", "3 mg IN nalmefene", "2\u00D73 mg IN nalmefene (6 mg)" ), `Fentanyl 1.63 mg` = c( "52.1% (47.3\u201356.8)", "29.5% (25.3\u201334.0)", "19.2% (15.5\u201323.3)", "10.5% (7.5\u201313.5)", "6.6% (4.3\u20139.3)", "3.8% (2.0\u20135.8)", "2.2% (1.0\u20133.8)", "0.35% (0\u20131.0)" ), `Fentanyl 2.97 mg` = c( "77.9% (73.8\u201381.8)", "54.2% (49.5\u201359.0)", "47.1% (42.0\u201352.3)", "31.8% (27.3\u201336.5)", "22.6% (18.4\u201326.8)", "17.0% (13.5\u201320.8)", "11.6% (8.5\u201314.8)", "3.8% (2.0\u20135.5)" ), check.names = FALSE ) }, striped = TRUE, bordered = TRUE, hover = TRUE, width = "100%") output$ca_naive <- renderTable({ data.frame( Treatment = c( "No antagonist", "2 mg IM naloxone", "4 mg IN naloxone", "2\u00D74 mg IN naloxone (8 mg)", "3\u00D74 mg IN naloxone (12 mg)", "4\u00D74 mg IN naloxone (16 mg)", "3 mg IN nalmefene", "2\u00D73 mg IN nalmefene (6 mg)" ), `Fentanyl 1.63 mg` = c( "74.7% (70.3\u201378.8)", "48.3% (43.5\u201353.3)", "39.1% (34.3\u201343.8)", "23.8% (19.6\u201327.8)", "16.3% (12.5\u201319.8)", "11.8% (8.8\u201314.8)", "7.6% (5.0\u201310.3)", "1.7% (0.5\u20133.0)" ), `Fentanyl 2.97 mg` = c( "90.1% (87.0\u201393.0)", "71.8% (67.3\u201376.0)", "67.6% (63.0\u201372.0)", "53.0% (48.3\u201357.5)", "42.4% (37.8\u201347.3)", "33.4% (28.8\u201337.8)", "25.7% (21.5\u201330.0)", "10.1% (7.3\u201313.0)" ), check.names = FALSE ) }, striped = TRUE, bordered = TRUE, hover = TRUE, width = "100%") } shinyApp(ui = ui, server = server)