From 0cc13ddc15c4c9c91038ba5bd1deaf13ffb9fe90 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Wed, 9 Sep 2026 15:02:02 -0400 Subject: [PATCH] Update error handling and documentation for harm bound --- R/gs_design_npe.R | 34 +-------------------- R/gs_power_npe.R | 8 ++--- man/gs_design_ahr.Rd | 8 ++--- man/gs_power_ahr.Rd | 8 ++--- man/gs_power_design_npe.Rd | 8 ++--- tests/testit/test-developer-gs_design_npe.R | 3 +- tests/testit/test-developer-gs_power_npe.R | 9 +++++- 7 files changed, 27 insertions(+), 51 deletions(-) diff --git a/R/gs_design_npe.R b/R/gs_design_npe.R index 33a13529..5c3300fe 100644 --- a/R/gs_design_npe.R +++ b/R/gs_design_npe.R @@ -271,39 +271,7 @@ gs_design_npe <- function( # for a fixed design, this is all you need. if (n_analysis == 1) { if (any(test_harm)) { - ans_h1 <- gs_power_npe( - theta = theta, theta0 = theta0, theta1 = theta1, - info = info * min_x, info0 = info0 * min_x, info1 = info1 * min_x, - info_scale = info_scale, - upper = gs_b, upar = qnorm(1 - alpha), test_upper = test_upper, - lower = if (two_sided) lower else gs_b, - lpar = if (two_sided) lpar else rep(-Inf, n_analysis), - test_lower = test_lower, binding = binding, - harm = harm, hpar = hpar, test_harm = test_harm, - r = r, tol = tol - ) - ans_h0 <- gs_power_npe( - theta = 0, theta0 = theta0, theta1 = theta1, - info = info0 * min_x, info0 = info0 * min_x, info1 = info1 * min_x, - info_scale = info_scale, - upper = gs_b, upar = qnorm(1 - alpha), test_upper = test_upper, - lower = if (two_sided) lower else gs_b, - lpar = if (two_sided) lpar else rep(-Inf, n_analysis), - test_lower = test_lower, binding = binding, - harm = harm, hpar = hpar, test_harm = test_harm, - r = r, tol = tol - ) - suppressMessages( - ans <- ans_h1 |> - full_join( - ans_h0 |> - select(analysis, bound, probability) |> - rename(probability0 = probability) - ) - ) - ans <- ans |> select(analysis, bound, z, probability, probability0, theta, info_frac, info, info0, info1) - class(ans) <- c("gs_design_npe", class(ans)) - return(ans) + stop("gs_design_npe() harm bound cannot be tested if there is only one analysis.") } ans <- tibble( diff --git a/R/gs_power_npe.R b/R/gs_power_npe.R index 4be56910..69a0d024 100644 --- a/R/gs_power_npe.R +++ b/R/gs_power_npe.R @@ -94,12 +94,12 @@ #' a logical vector of the same length as `info` should #' indicate which analyses will have a lower bound. #' @param test_harm Indicator of which analyses should include a harm bound; -#' single value of `TRUE` (default) indicates all analyses; -#' single value of `FALSE` indicates no harm bound; otherwise, +#' single value of `FALSE` (default) indicates no harm bound; +#' single value of `TRUE` indicates all analyses; otherwise, #' a logical vector of the same length as `info` should #' indicate which analyses will have a harm bound. -#' For fixed designs, the harm bound is typically not included. #' For group sequential designs, the harm bound is always smaller than the lower bound (if any). +#' For fixed designs, requesting a harm bound fails immediately with an error. #' @param r Integer value controlling grid for numerical integration as in #' Jennison and Turnbull (2000); default is 18, range is 1 to 80. #' Larger values provide larger number of grid points and greater accuracy. @@ -295,7 +295,7 @@ gs_power_npe <- function(theta = .1, theta0 = 0, theta1 = theta, # 3 theta ) { # Check & set up parameters ---- n_analysis <- length(info) - if (n_analysis == 1 && test_harm) { + if (n_analysis == 1 && any(test_harm)) { stop("gs_power_npe() harm bound cannot be tested if there is only one analysis.") } diff --git a/man/gs_design_ahr.Rd b/man/gs_design_ahr.Rd index 52d6f795..5389797c 100644 --- a/man/gs_design_ahr.Rd +++ b/man/gs_design_ahr.Rd @@ -97,12 +97,12 @@ a logical vector of the same length as \code{info} should indicate which analyses will have a lower bound.} \item{test_harm}{Indicator of which analyses should include a harm bound; -single value of \code{TRUE} (default) indicates all analyses; -single value of \code{FALSE} indicates no harm bound; otherwise, +single value of \code{FALSE} (default) indicates no harm bound; +single value of \code{TRUE} indicates all analyses; otherwise, a logical vector of the same length as \code{info} should indicate which analyses will have a harm bound. -For fixed designs, the harm bound is typically not included. -For group sequential designs, the harm bound is always smaller than the lower bound (if any).} +For group sequential designs, the harm bound is always smaller than the lower bound (if any). +For fixed designs, requesting a harm bound fails immediately with an error.} \item{info_scale}{Information scale for calculation. Options are: \itemize{ diff --git a/man/gs_power_ahr.Rd b/man/gs_power_ahr.Rd index c9193476..912c5416 100644 --- a/man/gs_power_ahr.Rd +++ b/man/gs_power_ahr.Rd @@ -81,12 +81,12 @@ a logical vector of the same length as \code{info} should indicate which analyses will have an efficacy bound.} \item{test_harm}{Indicator of which analyses should include a harm bound; -single value of \code{TRUE} (default) indicates all analyses; -single value of \code{FALSE} indicates no harm bound; otherwise, +single value of \code{FALSE} (default) indicates no harm bound; +single value of \code{TRUE} indicates all analyses; otherwise, a logical vector of the same length as \code{info} should indicate which analyses will have a harm bound. -For fixed designs, the harm bound is typically not included. -For group sequential designs, the harm bound is always smaller than the lower bound (if any).} +For group sequential designs, the harm bound is always smaller than the lower bound (if any). +For fixed designs, requesting a harm bound fails immediately with an error.} \item{ratio}{Experimental:Control randomization ratio.} diff --git a/man/gs_power_design_npe.Rd b/man/gs_power_design_npe.Rd index e88a4840..2242905d 100644 --- a/man/gs_power_design_npe.Rd +++ b/man/gs_power_design_npe.Rd @@ -137,12 +137,12 @@ default of \code{FALSE} is recommended.} \item{hpar}{Parameters passed to \code{harm}, which can be set up similarly as \code{lpar.}} \item{test_harm}{Indicator of which analyses should include a harm bound; -single value of \code{TRUE} (default) indicates all analyses; -single value of \code{FALSE} indicates no harm bound; otherwise, +single value of \code{FALSE} (default) indicates no harm bound; +single value of \code{TRUE} indicates all analyses; otherwise, a logical vector of the same length as \code{info} should indicate which analyses will have a harm bound. -For fixed designs, the harm bound is typically not included. -For group sequential designs, the harm bound is always smaller than the lower bound (if any).} +For group sequential designs, the harm bound is always smaller than the lower bound (if any). +For fixed designs, requesting a harm bound fails immediately with an error.} \item{r}{Integer value controlling grid for numerical integration as in Jennison and Turnbull (2000); default is 18, range is 1 to 80. diff --git a/tests/testit/test-developer-gs_design_npe.R b/tests/testit/test-developer-gs_design_npe.R index 1105f55b..e3b08460 100644 --- a/tests/testit/test-developer-gs_design_npe.R +++ b/tests/testit/test-developer-gs_design_npe.R @@ -281,7 +281,8 @@ assert("Harm bound is not provided for fixed designs", { theta = 0.1, info = 40, upper = gs_b, upar = -qnorm(0.025), test_upper = TRUE, lower = gs_b, lpar = -Inf, test_lower = FALSE, - harm = gs_b, hpar = -2, test_harm = TRUE) + harm = gs_b, hpar = -2, test_harm = TRUE), + "gs_design_npe() harm bound cannot be tested if there is only one analysis." )) }) diff --git a/tests/testit/test-developer-gs_power_npe.R b/tests/testit/test-developer-gs_power_npe.R index 99006dbf..1846a90c 100644 --- a/tests/testit/test-developer-gs_power_npe.R +++ b/tests/testit/test-developer-gs_power_npe.R @@ -333,7 +333,14 @@ assert("Harm bound is not provided for fixed designs", { theta = 0.1, info = 40, upper = gs_b, upar = -qnorm(0.025), test_upper = TRUE, lower = gs_b, lpar = -Inf, test_lower = FALSE, - harm = gs_b, hpar = -2, test_harm = TRUE) + harm = gs_b, hpar = -2, test_harm = TRUE), + "gs_power_npe() harm bound cannot be tested if there is only one analysis." + )) + + # Throw informative error even if test_harm is not length 1 + (has_error( + gs_power_npe(test_harm = c(FALSE, TRUE)), + "gs_power_npe() harm bound cannot be tested if there is only one analysis." )) })