library(shiny) library(bslib) library(mrgsolve) library(dplyr) library(ggplot2) model_code <- ' $PARAM @annotated CL : 0.87 : Clearance (L/day) V1 : 12.64 : Central volume (L) V2 : 2.61 : Peripheral volume (L) Q : 0.21 : Intercompartmental clearance (L/day) KA : 0.48 : First-order absorption rate constant (1/day) F1 : 0.45 : SC bioavailability WT : 77.4 : Body weight (kg) REFWT : 77.4 : Reference body weight (kg) ALLOM : 0 : Apply exploratory allometry (0/1) EXP_CL : 0.75 : Allometric exponent for CL EXP_V : 1.0 : Allometric exponent for volume MW_TOZO : 145500 : Molecular weight (g/mol) KEL_IL33 : 4.83 : IL-33 elimination rate (1/day) KON_TOZO : 68.85 : IL-33-tozorakimab association constant (1/nM/day) KD_TOZO : 0.00003 : IL-33-tozorakimab dissociation constant (nM) KEL_SST2 : 0.069 : sST2 elimination rate (1/day) KON_SST2 : 91.38 : IL-33-sST2 association constant (1/nM/day) KD_SST2 : 0.0006 : IL-33-sST2 dissociation constant (nM) BL_COMPLEX : 0.00047 : Baseline IL-33/sST2 complex (nM) BL_SST2 : 0.20 : Baseline free sST2 (nM) EC50_TE : 0.0111 : Published Figure 5-calibrated EC50 for systemic TE (ug/mL) HILL_TE : 0.96 : Published Figure 5-calibrated Hill coefficient for systemic TE $CMT @annotated DEPOT : SC depot (mg) CENT : Central compartment (mg) PERIPH : Peripheral compartment (mg) IL33FREE : Free IL-33 in central compartment (nM) SST2FREE : Free sST2 in central compartment (nM) IL33TOZO : IL-33/tozorakimab complex (nM) IL33SST2 : IL-33/sST2 complex (nM) $MAIN if(NEWIND <= 1) { IL33FREE_0 = 1.41e-06; SST2FREE_0 = 0.20; IL33TOZO_0 = 0.0; IL33SST2_0 = 0.00047; } F_DEPOT = F1; $ODE double nm_per_mgL_ode = 1.0e6 / MW_TOZO; double mgL_per_nM_ode = MW_TOZO / 1.0e6; double scale_cl_ode = ALLOM > 0.5 ? pow(WT / REFWT, EXP_CL) : 1.0; double scale_v_ode = ALLOM > 0.5 ? pow(WT / REFWT, EXP_V) : 1.0; double CLi_ode = CL * scale_cl_ode; double V1i_ode = V1 * scale_v_ode; double V2i_ode = V2 * scale_v_ode; double KOFF_TOZO_ode = KON_TOZO * KD_TOZO; double KOFF_SST2_ode = KON_SST2 * KD_SST2; double BL_IL33_ode = BL_COMPLEX * KD_SST2 / BL_SST2; double KIN_IL33_ode = KEL_IL33 * BL_IL33_ode + KON_SST2 * BL_IL33_ode * BL_SST2 - KOFF_SST2_ode * BL_COMPLEX; double KIN_SST2_ode = KEL_SST2 * BL_SST2 + KON_SST2 * BL_IL33_ode * BL_SST2 - KOFF_SST2_ode * BL_COMPLEX; double TOZO_FREE_NM_ode = (CENT / V1i_ode) * nm_per_mgL_ode; double bind_tozo_nM_ode = KON_TOZO * TOZO_FREE_NM_ode * IL33FREE - KOFF_TOZO_ode * IL33TOZO; double bind_sst2_nM_ode = KON_SST2 * IL33FREE * SST2FREE - KOFF_SST2_ode * IL33SST2; double bind_tozo_mg_ode = bind_tozo_nM_ode * mgL_per_nM_ode * V1i_ode; dxdt_DEPOT = -KA * DEPOT; dxdt_CENT = KA * DEPOT - (CLi_ode / V1i_ode + Q / V1i_ode) * CENT + (Q / V2i_ode) * PERIPH - bind_tozo_mg_ode; dxdt_PERIPH = (Q / V1i_ode) * CENT - (Q / V2i_ode) * PERIPH; dxdt_IL33FREE = KIN_IL33_ode - KEL_IL33 * IL33FREE - bind_tozo_nM_ode - bind_sst2_nM_ode; dxdt_SST2FREE = KIN_SST2_ode - KEL_SST2 * SST2FREE - bind_sst2_nM_ode; dxdt_IL33TOZO = bind_tozo_nM_ode - (CLi_ode / V1i_ode) * IL33TOZO; dxdt_IL33SST2 = bind_sst2_nM_ode - KEL_SST2 * IL33SST2; $TABLE double scale_cl_tab = ALLOM > 0.5 ? pow(WT / REFWT, EXP_CL) : 1.0; double scale_v_tab = ALLOM > 0.5 ? pow(WT / REFWT, EXP_V) : 1.0; double CLi_tab = CL * scale_cl_tab; double V1i_tab = V1 * scale_v_tab; double V2i_tab = V2 * scale_v_tab; double CP = CENT / V1i_tab; double BL_PCT = BL_COMPLEX > 0.0 ? 100.0 * (1.0 - IL33SST2 / BL_COMPLEX) : 0.0; if (BL_PCT < 0.0) BL_PCT = 0.0; if (BL_PCT > 100.0) BL_PCT = 100.0; double TOZO_COMPLEX_MGL = IL33TOZO * (MW_TOZO / 1.0e6); double TOTAL_CP = CP + TOZO_COMPLEX_MGL; double K10 = CLi_tab / V1i_tab; double K12 = Q / V1i_tab; double K21 = Q / V2i_tab; double disc = pow(K10 + K12 + K21, 2.0) - 4.0 * K10 * K21; if (disc < 0.0) disc = 0.0; double beta = 0.5 * ((K10 + K12 + K21) - sqrt(disc)); double THALF = beta > 0.0 ? log(2.0) / beta : 0.0; double TE_PCT = TOTAL_CP > 0.0 ? 100.0 * pow(TOTAL_CP, HILL_TE) / (pow(EC50_TE, HILL_TE) + pow(TOTAL_CP, HILL_TE)) : 0.0; if (TE_PCT < 0.0) TE_PCT = 0.0; if (TE_PCT > 100.0) TE_PCT = 100.0; double IL33SST2_OUT = IL33SST2; double IL33TOZO_OUT = IL33TOZO; $CAPTURE @annotated CP : Free tozorakimab concentration (mg/L) TOTAL_CP : Total tozorakimab concentration (mg/L) IL33SST2_OUT: IL-33/sST2 complex concentration (nM) IL33TOZO_OUT: IL-33/tozorakimab complex concentration (nM) BL_PCT : Mechanistic IL-33/sST2 inhibition from baseline (%) TE_PCT : Figure 5-calibrated systemic TE (%) THALF : Terminal half-life estimate (days) ' mod <- mcode("tozorakimab_pkte", 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 = "Tozorakimab PK/TE Simulator", theme = app_theme, sidebar = sidebar( title = "Simulation Settings", width = 340, h6("Dosing"), selectInput("route", "Route", choices = c("Subcutaneous" = "sc", "Intravenous" = "iv"), selected = "sc"), sliderInput("dose", "Dose (mg)", min = 30, max = 600, value = 90, step = 30), selectInput("interval_days", "Regimen", choices = c("Q2W (14 days)" = 14, "Q4W (28 days)" = 28, "Q6W (42 days)" = 42), selected = 14), numericInput("n_doses", "Number of doses", value = 6, min = 1, max = 12), hr(), h6("Patient / Scenario"), sliderInput("wt", "Body weight (kg)", min = 50, max = 110, value = 77.4, step = 0.5), checkboxInput("apply_allom", "Apply exploratory weight scaling", value = FALSE), hr(), h6("Display"), checkboxInput("log_scale", "Log Scale (PK Y-axis)", value = FALSE), checkboxInput("show_te_target", "Show >95% TE target band", value = TRUE) ), 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, last interval (μg/mL)") ), div(class = "metric-card metric-warning", div(class = "metric-value", textOutput("ctrough")), div(class = "metric-label", "Ctrough, end interval (μg/mL)") ), div(class = "metric-card metric-primary", div(class = "metric-value", textOutput("auc")), div(class = "metric-label", "AUC0-τ, last interval (μg·day/mL)") ), div(class = "metric-card metric-info", div(class = "metric-value", textOutput("thalf")), div(class = "metric-label", "Terminal t½ (days)") ) ), card( card_header("Serum Tozorakimab Concentration-Time Profile"), full_screen = TRUE, plotOutput("pkPlot", height = "500px") ), card( card_header("Systemic Target Engagement"), full_screen = TRUE, plotOutput("tePlot", height = "420px") ), navset_card_underline( title = "Drug Information", nav_panel( "Model Information", div( style = "padding: 10px 4px;", tags$h4("Tozorakimab mechanistic popPK/TE model"), tags$p(tags$strong("Structure:"), " Two-compartment SC/IV monoclonal antibody model with mechanistic IL-33 / soluble ST2 target-engagement in the central compartment."), tags$ul( tags$li(tags$strong("Clearance:"), " 0.87 L/day"), tags$li(tags$strong("Central volume:"), " 12.64 L"), tags$li(tags$strong("Peripheral volume:"), " 2.61 L (fixed)"), tags$li(tags$strong("Intercompartmental clearance:"), " 0.21 L/day (fixed)"), tags$li(tags$strong("SC absorption rate (Ka):"), " 0.48 day⁻¹"), tags$li(tags$strong("SC bioavailability:"), " 45%"), tags$li(tags$strong("Observed half-life:"), " 11.7–17.3 days in phase 1 cohorts") ), tags$h5("Clinical context"), tags$p("Tozorakimab is a human IgG1 anti-IL-33 monoclonal antibody studied in healthy volunteers and patients with mild COPD. The published simulations showed >95% systemic IL-33/sST2 inhibition at doses above 90 mg across Q2W, Q4W, and Q6W SC regimens."), tags$p("The PK panel uses the published two-compartment popPK model. The displayed systemic TE curve is calibrated to reproduce the published Figure 5 dose-response while retaining the manuscript-derived biomarker states in the background model."), tags$h5("Covariates"), tags$p("No statistically significant covariate effects on PK were retained in the final published model. The optional weight control applies exploratory standard mAb allometry for scenario analysis only; leave it off for paper-faithful simulations.") ) ), nav_panel( "References", div(class = "ref-box", tags$h5("Key references"), tags$ol( tags$li("Sadiq MW, Yu H, et al. Population pharmacokinetic/target engagement modelling of tozorakimab in healthy volunteers and patients with chronic obstructive pulmonary disease. Br J Clin Pharmacol. 2024."), tags$li("Reid J, et al. First-in-human phase 1 study of tozorakimab in healthy volunteers and mild COPD participants. Clin Pharmacol Ther. 2024.") ), tags$h5("Therapeutic context"), tags$ul( tags$li(tags$strong("Class:"), " Anti-IL-33 monoclonal antibody"), tags$li(tags$strong("Therapeutic area:"), " Respiratory and inflammatory disease (COPD-focused early development)"), tags$li(tags$strong("Routes evaluated:"), " Subcutaneous and intravenous"), tags$li(tags$strong("Primary PD readout:"), " Inhibition of circulating IL-33/sST2 complex") ) ) ) ), 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) { sim_data <- reactive({ interval_days <- as.numeric(input$interval_days) n_doses <- as.integer(input$n_doses) shiny::req(!is.na(interval_days), !is.na(n_doses), n_doses >= 1) dosing_cmt <- if (identical(input$route, "iv")) 2 else 1 addl_doses <- max(n_doses - 1, 0) sim_end <- max(interval_days * n_doses, interval_days) ev1 <- ev( amt = input$dose, cmt = dosing_cmt, ii = interval_days, addl = addl_doses ) out <- mod %>% param( WT = input$wt, ALLOM = ifelse(isTRUE(input$apply_allom), 1, 0) ) %>% ev(ev1) %>% mrgsim(end = sim_end, delta = 0.25) %>% as.data.frame() |> mutate( time_days = time, interval_days = interval_days, route_label = ifelse(input$route == "iv", "IV", "SC") ) out }) steady_window <- reactive({ d <- sim_data() interval_days <- as.numeric(input$interval_days) end_time <- max(d$time_days, na.rm = TRUE) start_time <- max(end_time - interval_days, 0) d |> dplyr::filter(time_days >= start_time, time_days <= end_time) }) output$cmax <- renderText({ d <- steady_window() shiny::req(nrow(d) > 0) sprintf("%.1f", max(d$TOTAL_CP, na.rm = TRUE)) }) output$ctrough <- renderText({ d <- steady_window() shiny::req(nrow(d) > 0) sprintf("%.1f", d$TOTAL_CP[nrow(d)]) }) output$auc <- renderText({ d <- steady_window() shiny::req(nrow(d) > 1) auc_val <- sum(diff(d$time_days) * (head(d$TOTAL_CP, -1) + tail(d$TOTAL_CP, -1)) / 2) sprintf("%.1f", auc_val) }) output$thalf <- renderText({ d <- sim_data() shiny::req(nrow(d) > 0) sprintf("%.1f", d$THALF[1]) }) output$pkPlot <- renderPlot({ d <- sim_data() shiny::req(nrow(d) > 0) d_plot <- if (isTRUE(input$log_scale)) { d |> dplyr::filter(TOTAL_CP > 0.001) } else { d } shiny::req(nrow(d_plot) > 0) p <- ggplot(d_plot, aes(x = time_days, y = TOTAL_CP)) + geom_line(color = "#8b5cf6", linewidth = 0.9) + labs( x = "Time (days)", y = "Total tozorakimab concentration (μg/mL)", title = paste0("Tozorakimab ", input$dose, " mg ", toupper(input$route), " every ", as.numeric(input$interval_days), " days") ) + theme_minimal(base_size = 14) if (isTRUE(input$log_scale)) { p <- p + scale_y_log10() } p }) output$tePlot <- renderPlot({ d <- sim_data() shiny::req(nrow(d) > 0) p <- ggplot(d, aes(x = time_days, y = TE_PCT)) if (isTRUE(input$show_te_target)) { p <- p + annotate("rect", xmin = -Inf, xmax = Inf, ymin = 95, ymax = 100, fill = "#10b981", alpha = 0.15) + geom_hline(yintercept = 95, linetype = "dashed", color = "#10b981", linewidth = 0.8) } p + geom_line(color = "#0dcaf0", linewidth = 0.9) + labs( x = "Time (days)", y = "IL-33/sST2 inhibition from baseline (%)", title = "Predicted systemic target engagement" ) + coord_cartesian(ylim = c(0, 100)) + theme_minimal(base_size = 14) }) } shinyApp(ui = ui, server = server)