library(shiny) library(bslib) library(mrgsolve) library(dplyr) library(ggplot2) calc_auc <- function(time, conc) { if (length(time) < 2 || length(conc) < 2) { return(NA_real_) } sum(diff(time) * (head(conc, -1) + tail(conc, -1)) / 2, na.rm = TRUE) } terminal_half_life_2c <- function(cl, vc, q, vp) { k10 <- cl / vc k12 <- q / vc k21 <- q / vp disc <- pmax((k10 + k12 + k21)^2 - 4 * k10 * k21, 0) beta <- 0.5 * ((k10 + k12 + k21) - sqrt(disc)) if (!is.finite(beta) || beta <= 0) { return(NA_real_) } log(2) / beta } get_analyte_meta <- function(analyte_key) { switch( analyte_key, parent = list(col = "CP_parent_ngml", label = "Ziftomenib", color = "#8b5cf6"), ko739 = list(col = "CP_ko739_ngml", label = "KO-739", color = "#10b981"), ko516 = list(col = "CP_ko516_ngml", label = "KO-516", color = "#f59e0b") ) } model_code <- ' $PARAM @annotated F1BASE : 0.129 : Baseline oral bioavailability fraction F1_FED : 6.09 : Food effect multiplier on F1 F1_PPI : 0.627 : PPI effect multiplier on F1 KABASE : 0.0928: Baseline absorption rate constant (1/h) KA_PPI : 0.616 : PPI effect multiplier on Ka LAGBASE : 0.325 : Baseline absorption lag time (h) LAG_FED : 1.38 : Food effect multiplier on lag time CLBASE : 11.6 : Parent clearance (L/h) CL_HV : 2.59 : Healthy volunteer effect on parent CL CL_CYP : 0.459 : Strong CYP3A4 inhibitor effect on parent CL VCBASE : 54.6 : Parent central volume (L) QBASE : 27.7 : Parent intercompartmental clearance (L/h) VPBASE : 1106 : Parent peripheral volume (L) FMBASE : 0.535 : Fraction of parent clearance to active metabolites FM_HV : 0.348 : Healthy volunteer effect on total metabolite formation FM516BASE : 0.5 : Fraction of metabolite formation to KO-516 CL739BASE : 8.50 : KO-739 clearance (L/h) CL739_CYP : 0.195 : Strong CYP3A4 inhibitor effect on KO-739 CL Q739BASE : 4.13 : KO-739 intercompartmental clearance (L/h) VC739BASE : 8.20 : KO-739 central volume (L) VC739_HV : 0.197 : Healthy volunteer effect on KO-739 central volume VP739BASE : 240 : KO-739 peripheral volume (L) CL516BASE : 21.7 : KO-516 clearance (L/h) CL516_CYP : 0.449 : Strong CYP3A4 inhibitor effect on KO-516 CL Q516BASE : 9.55 : KO-516 intercompartmental clearance (L/h) VC516BASE : 11.8 : KO-516 central volume (L) VC516_HV : 0.154 : Healthy volunteer effect on KO-516 central volume VP516BASE : 604 : KO-516 peripheral volume (L) FED : 0 : 1 = fed, 0 = fasted PPI : 0 : 1 = concomitant proton pump inhibitor CYP3A_STRONG: 0 : 1 = concomitant strong CYP3A4 inhibitor HV : 0 : 1 = healthy volunteer, 0 = R/R AML patient $CMT @annotated DEPOT : Oral depot (mg) CENT : Parent central compartment (mg) PERIPH : Parent peripheral compartment (mg) MET739C : KO-739 central compartment (mg-equivalent) MET739P : KO-739 peripheral compartment (mg-equivalent) MET516C : KO-516 central compartment (mg-equivalent) MET516P : KO-516 peripheral compartment (mg-equivalent) $MAIN bool fed_on = FED > 0.5; bool ppi_on = PPI > 0.5; bool cyp_on = CYP3A_STRONG > 0.5; bool hv_on = HV > 0.5; double F1i = F1BASE * (fed_on ? F1_FED : 1.0) * (ppi_on ? F1_PPI : 1.0); if (F1i < 0.001) F1i = 0.001; if (F1i > 0.99) F1i = 0.99; double KAi = KABASE * (ppi_on ? KA_PPI : 1.0); double LAGi = LAGBASE * (fed_on ? LAG_FED : 1.0); double CLi = CLBASE * (hv_on ? CL_HV : 1.0) * (cyp_on ? CL_CYP : 1.0); double VCi = VCBASE; double Qi = QBASE; double VPi = VPBASE; double FMi = FMBASE * (hv_on ? FM_HV : 1.0); if (FMi < 0.001) FMi = 0.001; if (FMi > 0.95) FMi = 0.95; double FM516i = FM516BASE; double CL739i = CL739BASE * (cyp_on ? CL739_CYP : 1.0); double Q739i = Q739BASE; double VC739i = VC739BASE * (hv_on ? VC739_HV : 1.0); double VP739i = VP739BASE; double CL516i = CL516BASE * (cyp_on ? CL516_CYP : 1.0); double Q516i = Q516BASE; double VC516i = VC516BASE * (hv_on ? VC516_HV : 1.0); double VP516i = VP516BASE; F_DEPOT = F1i; ALAG_DEPOT = LAGi; $ODE double parent_to_739 = (CLi / VCi) * FMi * (1.0 - FM516i); double parent_to_516 = (CLi / VCi) * FMi * FM516i; double parent_nonmet = (CLi / VCi) * (1.0 - FMi); dxdt_DEPOT = -KAi * DEPOT; dxdt_CENT = KAi * DEPOT - (parent_nonmet + parent_to_739 + parent_to_516 + Qi / VCi) * CENT + (Qi / VPi) * PERIPH; dxdt_PERIPH = (Qi / VCi) * CENT - (Qi / VPi) * PERIPH; dxdt_MET739C = parent_to_739 * CENT - (CL739i / VC739i + Q739i / VC739i) * MET739C + (Q739i / VP739i) * MET739P; dxdt_MET739P = (Q739i / VC739i) * MET739C - (Q739i / VP739i) * MET739P; dxdt_MET516C = parent_to_516 * CENT - (CL516i / VC516i + Q516i / VC516i) * MET516C + (Q516i / VP516i) * MET516P; dxdt_MET516P = (Q516i / VC516i) * MET516C - (Q516i / VP516i) * MET516P; $TABLE double CP_parent_ngml = (CENT / VCi) * 1000.0; double CP_ko739_ngml = (MET739C / VC739i) * 1000.0; double CP_ko516_ngml = (MET516C / VC516i) * 1000.0; double PARENT_CL = CLi; double PARENT_VC = VCi; double PARENT_Q = Qi; double PARENT_VP = VPi; double KO739_CL = CL739i; double KO739_VC = VC739i; double KO739_Q = Q739i; double KO739_VP = VP739i; double KO516_CL = CL516i; double KO516_VC = VC516i; double KO516_Q = Q516i; double KO516_VP = VP516i; double F1_OUT = F1i; double KA_OUT = KAi; double LAG_OUT = LAGi; double FM_OUT = FMi; $CAPTURE @annotated CP_parent_ngml : Ziftomenib plasma concentration (ng/mL) CP_ko739_ngml : KO-739 plasma concentration (ng/mL) CP_ko516_ngml : KO-516 plasma concentration (ng/mL) PARENT_CL : Parent clearance (L/h) PARENT_VC : Parent central volume (L) PARENT_Q : Parent intercompartmental clearance (L/h) PARENT_VP : Parent peripheral volume (L) KO739_CL : KO-739 clearance (L/h) KO739_VC : KO-739 central volume (L) KO739_Q : KO-739 intercompartmental clearance (L/h) KO739_VP : KO-739 peripheral volume (L) KO516_CL : KO-516 clearance (L/h) KO516_VC : KO-516 central volume (L) KO516_Q : KO-516 intercompartmental clearance (L/h) KO516_VP : KO-516 peripheral volume (L) F1_OUT : Individualized bioavailability KA_OUT : Individualized absorption rate constant (1/h) LAG_OUT : Individualized lag time (h) FM_OUT : Individualized metabolite formation fraction ' mod <- mcode("ziftomenib_pkpd_320", model_code, quiet = TRUE) app_theme <- bs_theme( version = 5, bootswatch = "flatly", primary = "#8b5cf6" ) |> bs_add_rules(" .metric-card { background: linear-gradient(135deg, #eef2ff 0%, #ffffff 100%); border-radius: 12px; padding: 16px; margin: 5px; text-align: center; border: 1px solid #c7d2fe; border-left: 6px solid #8b5cf6; box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06); } .metric-success { background: linear-gradient(135deg, #ecfdf5 0%, #ffffff 100%); border-color: #a7f3d0; border-left-color: #10b981; } .metric-warning { background: linear-gradient(135deg, #fffbeb 0%, #ffffff 100%); border-color: #fde68a; border-left-color: #f59e0b; } .metric-primary { background: linear-gradient(135deg, #f5f3ff 0%, #ffffff 100%); border-color: #ddd6fe; border-left-color: #8b5cf6; } .metric-info { background: linear-gradient(135deg, #ecfeff 0%, #ffffff 100%); border-color: #a5f3fc; border-left-color: #0dcaf0; } .main-output-note { font-size: 11px; color: #6c757d; margin: 4px 0 12px 6px; } .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 = "Ziftomenib Population PK Simulator", theme = app_theme, sidebar = sidebar( title = "Simulation Settings", width = 340, accordion( open = c("Dosing", "Clinical Context"), accordion_panel( "Dosing", icon = bsicons::bs_icon("capsule"), sliderInput("dose", "Dose (mg)", min = 100, max = 1000, value = 600, step = 50), sliderInput("interval", "Dosing interval (h)", min = 12, max = 48, value = 24, step = 4), numericInput("n_doses", "Number of doses", value = 28, min = 1, max = 120) ), accordion_panel( "Clinical Context", icon = bsicons::bs_icon("person"), selectInput( "analyte", "Analyte", choices = c("Ziftomenib (parent)" = "parent", "KO-739" = "ko739", "KO-516" = "ko516"), selected = "parent" ), radioButtons( "population", "Population", choices = c("R/R AML patient" = "aml", "Healthy volunteer" = "hv"), selected = "aml" ), checkboxInput("fed", "Fed state", value = FALSE), checkboxInput("ppi", "Concomitant PPI", value = FALSE), checkboxInput("cyp3a", "Concomitant strong CYP3A4 inhibitor", value = FALSE), tags$p( style = "font-size:12px; color:#6c757d; margin-top:8px;", "Paper default: 600 mg once daily, fasted, R/R AML population. Food increased exposure ~6-fold; PPIs reduced F and Ka; strong CYP3A4 inhibitors reduced CL but exposure-response remained flat in the paper." ) ), accordion_panel( "Display", icon = bsicons::bs_icon("gear"), checkboxInput("show_reference", "Show parent steady-state exposure range from paper", value = TRUE), checkboxInput("log_scale", "Log scale (Y-axis)", value = FALSE) ) ), tags$hr(), actionLink("show_model_info", "Model information", icon = bsicons::bs_icon("info-circle")), tags$br(), actionLink("show_references", "References", icon = bsicons::bs_icon("journal-text")) ), tags$p( style = "font-size: 11px; color: #6c757d; margin: 4px 0 0 6px;", "This app reproduces the typical deterministic ziftomenib population PK model from Mitra et al. (2026). Inter-individual and inter-occasion variability are not simulated." ), layout_column_wrap( width = 1 / 4, fill = FALSE, div( class = "metric-card metric-success", div(class = "metric-value", textOutput("cmax")), div(class = "metric-label", textOutput("cmax_label")) ), div( class = "metric-card metric-warning", div(class = "metric-value", textOutput("ctrough")), div(class = "metric-label", textOutput("ctrough_label")) ), div( class = "metric-card metric-primary", div(class = "metric-value", textOutput("auc")), div(class = "metric-label", textOutput("auc_label")) ), div( class = "metric-card metric-info", div(class = "metric-value", textOutput("thalf")), div(class = "metric-label", "Terminal t1/2 (h)") ) ), card( card_header("Concentration-Time Profile"), full_screen = TRUE, height = "740px", card_body( div( style = "height: clamp(420px, 58vh, 680px); min-height: 420px;", plotOutput("pkPlot", height = "100%") ), style = "min-height: 680px; padding: 12px;" ) ), 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 <- function(input, output, session) { analyte_meta <- reactive({ get_analyte_meta(input$analyte) }) n_doses <- reactive({ max(1, as.integer(input$n_doses)) }) observeEvent(input$show_model_info, { showModal(modalDialog( title = "Ziftomenib model information", size = "l", easyClose = TRUE, markdown("## Ziftomenib popPK / exposure-response model **Primary paper:** Mitra A et al. *CPT: Pharmacometrics & Systems Pharmacology* (2026) **Drug class:** Oral menin inhibitor **Therapeutic area:** Relapsed/refractory NPM1-mutated acute myeloid leukemia **Route:** Oral once-daily dosing ### Final structural model - Parent ziftomenib: **2-compartment** oral model with first-order absorption and lag time - Active metabolites KO-739 and KO-516: sequential **2-compartment** models conditioned on parent disposition - Final model pooled **188 subjects** (14 healthy volunteers, 174 R/R AML patients) ### Typical parent PK parameters | Parameter | Estimate | Units | |-----------|----------|-------| | F1 | 0.129 | fraction | | Ka | 0.0928 | h^-1 | | Lag time | 0.325 | h | | CL | 11.6 | L/h | | Vc | 54.6 | L | | Q | 27.7 | L/h | | Vp | 1106 | L | ### Key covariates from the paper - **Food:** F1 multiplied by **6.09** and lag time multiplied by **1.38** - **PPI:** F1 multiplied by **0.627** and Ka multiplied by **0.616** - **Strong CYP3A4 inhibitor:** parent CL multiplied by **0.459** - **Healthy volunteers:** parent CL multiplied by **2.59**, total metabolite formation multiplied by **0.348** ### Exposure-response takeaways - No statistically significant relationship between ziftomenib exposure and efficacy endpoints - No statistically significant relationship between ziftomenib exposure and grade >=3 or grade >=4 safety endpoints - The paper supported **600 mg once daily** in adult R/R NPM1-m AML patients - Concomitant antifungal azoles / strong CYP3A4 inhibitors were not associated with clinically meaningful efficacy or safety changes in the exposure-response analysis ### Observed clinical parent exposure range in the efficacy dataset - **AUCss:** 3520 to 12,500 ng*h/mL - **Cmax,ss:** 206 to 605 ng/mL - **Ctrough,ss:** 108 to 466 ng/mL ### Notes - This simulator shows **typical** model behavior only and omits IIV/IOV. - Metabolite concentrations are represented as model-derived plasma concentration outputs suitable for relative comparison across covariate scenarios. ") )) }) observeEvent(input$show_references, { showModal(modalDialog( title = "References", size = "m", easyClose = TRUE, div( class = "ref-box", tags$h5("Primary references"), tags$ol( tags$li( tags$strong("Mitra A, Yang X, Ortiz RH, et al. (2026)."), " Population Pharmacokinetics and Exposure-Response Analysis of Ziftomenib in Relapsed or Refractory Acute Myeloid Leukemia Patients With NPM1 Mutation." ), tags$li( tags$strong("Mitra A, et al. (2025)."), " Pharmacokinetics and ADME Characterization After Oral and Intravenous Administration of [14C]-Ziftomenib in Healthy Male Participants. ", tags$a(href = "https://pubmed.ncbi.nlm.nih.gov/39928533/", target = "_blank", "PMID: 39928533") ) ) ) )) }) sim_data <- reactive({ shiny::req(input$dose, input$interval, n_doses()) last_dose_time <- (n_doses() - 1) * input$interval end_time <- last_dose_time + input$interval ev1 <- ev( amt = input$dose, cmt = 1, ii = input$interval, addl = n_doses() - 1 ) mod %>% param( FED = ifelse(input$fed, 1, 0), PPI = ifelse(input$ppi, 1, 0), CYP3A_STRONG = ifelse(input$cyp3a, 1, 0), HV = ifelse(input$population == "hv", 1, 0) ) %>% ev(ev1) %>% mrgsim(end = end_time, delta = 0.25) %>% as.data.frame() |> mutate(time_h = .data$time) }) metric_window <- reactive({ d <- sim_data() analyte_col <- analyte_meta()$col last_start <- (n_doses() - 1) * input$interval d |> dplyr::filter(.data$time >= last_start, .data$time <= last_start + input$interval + 1e-9) |> mutate(conc = .data[[analyte_col]]) }) output$cmax_label <- renderText({ paste0("Cmax,ss (", analyte_meta()$label, ")") }) output$ctrough_label <- renderText({ paste0("Ctrough,ss (", analyte_meta()$label, ")") }) output$auc_label <- renderText({ paste0("AUCtau,ss (", analyte_meta()$label, ")") }) output$cmax <- renderText({ d <- metric_window() shiny::req(nrow(d) > 0) sprintf("%.0f", max(d$conc, na.rm = TRUE)) }) output$ctrough <- renderText({ d <- metric_window() shiny::req(nrow(d) > 0) sprintf("%.0f", tail(d$conc, 1)) }) output$auc <- renderText({ d <- metric_window() shiny::req(nrow(d) > 1) auc_val <- calc_auc(d$time, d$conc) sprintf("%.0f", auc_val) }) output$thalf <- renderText({ sim_row <- sim_data() |> dplyr::slice_tail(n = 1) shiny::req(nrow(sim_row) == 1) th <- switch( input$analyte, parent = terminal_half_life_2c(sim_row$PARENT_CL, sim_row$PARENT_VC, sim_row$PARENT_Q, sim_row$PARENT_VP), ko739 = terminal_half_life_2c(sim_row$KO739_CL, sim_row$KO739_VC, sim_row$KO739_Q, sim_row$KO739_VP), ko516 = terminal_half_life_2c(sim_row$KO516_CL, sim_row$KO516_VC, sim_row$KO516_Q, sim_row$KO516_VP) ) sprintf("%.1f", th) }) output$pkPlot <- renderPlot({ d <- sim_data() analyte <- analyte_meta() d_plot <- d |> mutate(conc = .data[[analyte$col]]) if (input$log_scale) { d_plot <- d_plot |> dplyr::filter(.data$conc > 0.001) } shiny::req(nrow(d_plot) > 0) subtitle_text <- paste( analyte$label, if (input$population == "aml") "R/R AML" else "Healthy volunteer", if (input$fed) "fed" else "fasted", if (input$ppi) "with PPI" else "without PPI", if (input$cyp3a) "with strong CYP3A4 inhibitor" else "without strong CYP3A4 inhibitor", sep = " • " ) p <- ggplot(d_plot, aes(x = .data$time_h, y = .data$conc)) + geom_line(color = analyte$color, linewidth = 0.9) + labs( x = "Time (hours)", y = "Plasma concentration (ng/mL)", title = paste0(analyte$label, " concentration-time profile"), subtitle = subtitle_text ) + theme_minimal(base_size = 14) if (isTRUE(input$show_reference) && identical(input$analyte, "parent")) { p <- p + annotate("rect", xmin = -Inf, xmax = Inf, ymin = 108, ymax = 466, fill = "#10b981", alpha = 0.10) + geom_hline(yintercept = c(108, 466), color = "#10b981", linetype = "dashed", alpha = 0.7) + annotate( "text", x = max(d_plot$time_h) * 0.02, y = 486, label = "Paper Ctrough,ss range: 108-466 ng/mL", hjust = 0, size = 3.4, color = "#10b981" ) } if (input$log_scale) { p <- p + scale_y_log10() } p }) } shinyApp(ui = ui, server = server)