library(acousticTS) test_that("worker libraries reuse installations and handle errors", { # Isolate filesystem and subprocess boundaries without installing another # package or changing the caller's worker cache. resolver <- acousticTS:::.resolve_simulation_worker_library source <- "/source/acousticTS" libraries <- "/installed" directories <- c(source) install_status <- 0L calls <- list() cache <- new.env(parent = emptyenv()) environment(resolver) <- list2env(list( getNamespaceInfo = function(...) source, normalizePath = function(path, ...) path, dir.exists = function(path) path %in% directories, file.exists = function(path) { basename(path) == "DESCRIPTION" & dirname(path) %in% directories }, .libPaths = function() libraries, tempdir = function() "/worker-cache", dir.create = function(path, ...) { directories <<- union(directories, path) TRUE }, unlink = function(path, ...) { directories <<- setdiff(directories, path) 0L }, system2 = function(command, args, ...) { calls[[length(calls) + 1L]] <<- list(command = command, args = args) directories <<- union( directories, "/worker-cache/acousticTS-simulation-lib/acousticTS" ) structure("installation output", status = install_status) }, .simulation_worker_cache = cache ), parent = environment(resolver)) library <- resolver() expect_equal(library, "/worker-cache/acousticTS-simulation-lib") expect_length(calls, 1L) expect_true(source %in% calls[[1]]$args) expect_true(library %in% calls[[1]]$args) expect_equal(resolver(), library) expect_length(calls, 1L) cache$acousticTS.simulation_worker_library <- NULL install_status <- 1L expect_error( resolver(), "Unable to prepare the temporary worker installation" ) expect_length(calls, 2L) expect_null(cache$acousticTS.simulation_worker_library) install_status <- 0L environment(resolver)$.Platform <- list(OS.type = "unix") expect_equal(resolver(), library) expect_equal(basename(calls[[3]]$command), "R") expect_equal(calls[[3]]$args[1:2], c("CMD", "INSTALL")) directories <- c(source, paste0(source, "/Meta")) expect_equal(resolver(), dirname(source)) source <- "/missing/acousticTS" directories <- "/installed/acousticTS" expect_equal(resolver(), "/installed") directories <- character() expect_null(resolver()) }) test_that("composite simulation aliases agree with explicit geometry changes", { data(sardine, package = "acousticTS") aliased <- simulate_ts( sardine, frequency = 38000, model = "KRM", parameters = list( length_bladder = 0.025, maintain_ratio = FALSE ), parallel = FALSE, verbose = FALSE ) explicit <- target_strength( reforge( sardine, swimbladder_target = c(length = 0.025), maintain_ratio = FALSE ), frequency = 38000, model = "KRM" ) expect_equal(aliased$KRM$TS, explicit@model$KRM$TS) expect_error( simulate_ts( sardine, frequency = 38000, model = "KRM", parameters = list(length_bladder = 0.025, length_swimbladder = 0.025), parallel = FALSE, verbose = FALSE ), "duplicate one or more dimensions" ) expect_error( simulate_ts( sardine, frequency = 38000, model = "KRM", parameters = list(length_bladder = function() NA_real_), parallel = FALSE, verbose = FALSE ), "one non-missing numeric value" ) fish <- bbf_generate( body_shape = cylinder(0.08, 0.006), backbone_shape = cylinder( 0.04, 8e-04 ), g_body = 1.04, h_body = 1.02, density_backbone = 1900, sound_speed_longitudinal_backbone = 3500, sound_speed_transversal_backbone = 1700, x_offset_backbone = 0.02 ) aliased <- simulate_ts( fish, frequency = 38000, model = "BBFM", parameters = list( length_backbone = 0.03, maintain_ratio = FALSE, sound_speed_transversal = 1800 ), parallel = FALSE, verbose = FALSE ) changed <- reforge( fish, backbone_target = c(length = 0.03), maintain_ratio = FALSE ) changed@backbone$sound_speed_transversal <- 1800 changed@components$backbone <- changed@backbone explicit <- target_strength(changed, frequency = 38000, model = "BBFM") expect_equal(aliased$BBFM$TS, explicit@model$BBFM$TS) overridden <- acousticTS:::.apply_simulation_parameter_overrides( fish, list(sound_speed_transversal = 1800) ) expect_equal(overridden@components$backbone, overridden@backbone) expect_equal(overridden@backbone$sound_speed_transversal, 1800) }) test_that("simulate_ts function works with empty parameters", { # Test with a simple CAL object cal_obj <- cal_generate() frequency <- c(38e3, 120e3) # Basic simulation parameters -- nothing varies parameters <- list() # Run simulation result <- simulate_ts( object = cal_obj, frequency = frequency, model = "calibration", n_realizations = 5, parameters = parameters, parallel = FALSE, verbose = FALSE ) # Check that result is a data frame expect_type(result, "list") # Get the results result_df <- result$calibration # Check that we get the expected number of rows expect_equal(nrow(result_df), 5 * length(frequency)) # Check that required columns exist expect_true("frequency" %in% colnames(result_df)) expect_true("TS" %in% colnames(result_df)) # Check values cal_reference <- cal_generate() cal_reference <- target_strength( cal_reference, frequency = frequency, model = "calibration" ) reference_df <- cal_reference@model$calibration expect_equal(unique(result_df$TS), unique(reference_df$TS)) }) test_that("simulate_ts is the stable simulation entry point", { cal_obj <- cal_generate() expect_silent( simulate_ts( object = cal_obj, frequency = 38e3, model = "calibration", n_realizations = 1, parameters = list(), parallel = FALSE, verbose = FALSE ) ) }) test_that("simulate_ts function works with single sets of parameters", { # Test with a simple CAL object data(krill) frequency <- c(38e3, 120e3) # Basic simulation parameters -- nothing varies parameters <- list( length = 20e-3, radius = 4e-3 ) # Run simulation result <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 1, parameters = parameters, parallel = FALSE, verbose = FALSE ) # Check that result is a data frame expect_type(result, "list") # Get the results result_df <- result$DWBA # Check that we get the expected number of rows expect_equal(nrow(result_df), length(frequency)) # Check that required columns exist expect_true("frequency" %in% colnames(result_df)) expect_true("TS" %in% colnames(result_df)) # Check values krill_reference <- target_strength( krill, frequency = frequency, model = "DWBA" ) reference_df <- krill_reference@model$DWBA expect_false(all(unique(result_df$TS) == unique(reference_df$TS))) # Pass undefined variable (i.e. not already within the object) expect_warning( result_curved <- simulate_ts( object = krill, frequency = frequency, model = "DWBA_curved", n_realizations = 1, parameters = list(radius_curvature_ratio = 3), parallel = FALSE, verbose = FALSE ), "deprecated" ) # Check values expect_false(all(result_curved$DWBA_curved$TS == reference_df$TS)) }) test_that("simulate_ts accepts model names case-insensitively", { data(krill) frequency <- c(38e3, 120e3) parameters <- list(length = 20e-3) result_upper <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 1, parameters = parameters, parallel = FALSE, verbose = FALSE ) result_lower <- simulate_ts( object = krill, frequency = frequency, model = "dwba", n_realizations = 1, parameters = parameters, parallel = FALSE, verbose = FALSE ) expect_true("DWBA" %in% names(result_upper)) expect_true("DWBA" %in% names(result_lower)) expect_equal(result_upper$DWBA$TS, result_lower$DWBA$TS) }) test_that("simulate_ts infers realizations from deterministic parameters", { data(krill) # A single deterministic grid axis needs no explicit n_realizations. one_by_two <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list(length_body = 0.02, radius_body = c(2e-3, 3e-3)), parallel = FALSE, verbose = FALSE ) expect_equal(nrow(one_by_two$DWBA), 2) # Multiple deterministic axes expand to their Cartesian product. two_by_three <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list( length_body = c(0.02, 0.03), radius_body = c(2e-3, 3e-3, 4e-3) ), parallel = FALSE, verbose = FALSE ) expect_equal(nrow(two_by_three$DWBA), 6) expect_equal( nrow(unique(two_by_three$DWBA[, c("length_body", "radius_body")])), 6 ) # With no varying parameters a single realization runs. none <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parallel = FALSE, verbose = FALSE ) expect_equal(nrow(none$DWBA), 1) # A structured multi-dimension target is one unit, not a grid axis. structured <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list( body_target = c(length = 0.02, radius = 3e-3), isometric_body = FALSE ), parallel = FALSE, verbose = FALSE ) expect_equal(nrow(structured$DWBA), 1) # A list of structured targets is a grid axis. target_list <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list( body_target = list(c(length = 0.02), c(length = 0.03)) ), parallel = FALSE, verbose = FALSE ) expect_equal(nrow(target_list$DWBA), 2) # Generating functions draw once per grid cell when n_realizations is absent. inferred_gen <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list( length_body = c(0.02, 0.03), theta = function() stats::runif(1, 0, pi) ), parallel = FALSE, verbose = FALSE ) expect_equal(nrow(inferred_gen$DWBA), 2) # An explicit n_realizations still repeats every deterministic cell. repeated <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 3, parameters = list(length_body = c(0.02, 0.03)), batch_by = "length_body", parallel = FALSE, verbose = FALSE ) expect_equal(nrow(repeated$DWBA), 6) expect_error( simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 0, parameters = list(), parallel = FALSE, verbose = FALSE ), "'n_realizations' must be a single positive integer" ) }) test_that("simulate_ts pairs parameters when permute = FALSE", { data(krill) # Default crosses the two axes into the full grid. crossed <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list(theta_body = c(1, 2), density_body = c(1040, 1050)), parallel = FALSE, verbose = FALSE ) expect_equal(nrow(crossed$DWBA), 4) # permute = FALSE zips the axes element-wise into aligned pairs. paired <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list(theta_body = c(1, 2), density_body = c(1040, 1050)), permute = FALSE, parallel = FALSE, verbose = FALSE ) expect_equal(nrow(paired$DWBA), 2) expect_equal(paired$DWBA$theta_body, c(1, 2)) expect_equal(paired$DWBA$density_body, c(1040, 1050)) # Length-one parameters stay constant across the pairs. const <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list(theta_body = c(1, 2, 3), density_body = 1040), permute = FALSE, parallel = FALSE, verbose = FALSE ) expect_equal(nrow(const$DWBA), 3) expect_true(all(const$DWBA$density_body == 1040)) # n_realizations still repeats each paired cell. repeated <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 3, parameters = list(theta_body = c(1, 2), density_body = c(1040, 1050)), permute = FALSE, parallel = FALSE, verbose = FALSE ) expect_equal(nrow(repeated$DWBA), 6) # Mismatched axis lengths cannot be zipped. expect_error( simulate_ts( object = krill, frequency = 38e3, model = "DWBA", parameters = list(theta_body = c(1, 2), density_body = c(1, 2, 3)), permute = FALSE, parallel = FALSE, verbose = FALSE ), "share the same number of values" ) }) test_that("simulate_ts works with batch_by parameter", { # Test batching with different parameter values data(krill) frequency <- c(38e3, 120e3) # Parameters with batch_by parameters <- list( length = c(10e-3, 20e-3, 30e-3) # Will batch over these values ) # Run simulation with batching result <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 1, parameters = parameters, batch_by = "length", parallel = FALSE, verbose = FALSE ) # Check that result is a data frame expect_type(result, "list") result_df <- result$DWBA expect_s3_class(result_df, "data.frame") # Should have more rows due to batching expect_true(nrow(result_df) == 6) # Check values against direct reforge() + target_strength() references reference_df <- do.call( rbind, lapply(parameters$length, function(length_value) { obj <- reforge(krill, length = length_value) obj <- target_strength( object = obj, frequency = frequency, model = "DWBA" ) df <- extract(obj, "model")$DWBA data.frame( length = length_value, frequency = df$frequency, TS = df$TS ) }) ) result_df <- result_df[order(result_df$length, result_df$frequency), ] reference_df <- reference_df[order( reference_df$length, reference_df$frequency ), ] rownames(result_df) <- NULL rownames(reference_df) <- NULL expect_true(length(unique(result_df$TS)) == 6) expect_equal(result_df$length, reference_df$length) expect_equal(result_df$frequency, reference_df$frequency) expect_equal(result_df$TS, reference_df$TS, tolerance = 1e-6) }) test_that("simulate_ts supports structured reforge parameters", { data(krill) result <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 1, parameters = list( body_target = c(length = 0.02), n_segments_body = 120 ), parallel = FALSE, verbose = FALSE ) expect_type(result, "list") expect_s3_class(result$DWBA, "data.frame") expect_true(is.list(result$DWBA$body_target)) expect_equal(result$DWBA$body_target[[1]], c(length = 0.02)) expect_equal(result$DWBA$n_segments_body, 120) expect_true(all(is.finite(result$DWBA$TS))) }) test_that("simulate_ts supports convenience FLS reforge aliases", { data(krill) result <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 1, parameters = list( length_body = 0.02, n_segments_body = 120 ), parallel = FALSE, verbose = FALSE ) expect_type(result, "list") expect_s3_class(result$DWBA, "data.frame") expect_equal(result$DWBA$length_body, 0.02) expect_equal(result$DWBA$n_segments_body, 120) expect_true(all(is.finite(result$DWBA$TS))) }) test_that("simulate_ts supports batched structured reforge parameters", { data(krill) result <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 1, parameters = list( body_target = list( c(length = 0.02), c(length = 0.03) ) ), batch_by = "body_target", parallel = FALSE, verbose = FALSE ) expect_type(result, "list") expect_s3_class(result$DWBA, "data.frame") expect_equal(nrow(result$DWBA), 2) expect_true(is.list(result$DWBA$body_target)) expect_equal(result$DWBA$body_target[[1]], c(length = 0.02)) expect_equal(result$DWBA$body_target[[2]], c(length = 0.03)) expect_true(length(unique(result$DWBA$TS)) == 2) }) test_that("simulate_ts supports batched convenience FLS reforge aliases", { data(krill) frequency <- c(38e3, 120e3) result <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 1, parameters = list( length_body = c(0.02, 0.03) ), batch_by = "length_body", parallel = FALSE, verbose = FALSE ) reference_df <- do.call( rbind, lapply(c(0.02, 0.03), function(length_value) { obj <- reforge(krill, body_target = c(length = length_value)) obj <- target_strength( object = obj, frequency = frequency, model = "DWBA" ) df <- extract(obj, "model")$DWBA data.frame( length_body = length_value, frequency = df$frequency, TS = df$TS ) }) ) result_df <- result$DWBA[order( result$DWBA$length_body, result$DWBA$frequency ), ] reference_df <- reference_df[order( reference_df$length_body, reference_df$frequency ), ] rownames(result_df) <- NULL rownames(reference_df) <- NULL expect_equal(result_df$length_body, reference_df$length_body) expect_equal(result_df$frequency, reference_df$frequency) expect_equal(result_df$TS, reference_df$TS, tolerance = 1e-6) }) test_that("simulate_ts propagates invalid structured reforge inputs", { data(krill) expect_error( simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 1, parameters = list(body_target = c(depth = 0.02)), parallel = FALSE, verbose = FALSE ), "invalid dimensions" ) }) test_that("simulate_ts rejects conflicting convenience and explicit targets", { data(krill) expect_error( simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 1, parameters = list( body_target = c(length = 0.02), length_body = 0.03 ), parallel = FALSE, verbose = FALSE ), "Specify either 'body_target' or its convenience aliases" ) }) test_that("simulate_ts works with generating functions", { # Test with a generating function data(krill) frequency <- c(38e3) # Parameters with a generating function parameters <- list( length = function() stats::rnorm(1, mean = 40e-3, sd = 5e-3) ) # Run simulation result <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 10, parameters = parameters, parallel = FALSE, verbose = FALSE ) # Check that result is a data frame expect_type(result, "list") result_df <- result$DWBA expect_s3_class(result_df, "data.frame") # Get the results result_df <- result$DWBA # Check that we get the expected number of rows expect_equal(nrow(result_df), length(frequency) * 10) # Check that required columns exist expect_true("frequency" %in% colnames(result_df)) expect_true("TS" %in% colnames(result_df)) # Ensure that values are not duplicated expect_true(length(unique(result_df$TS)) == length(frequency) * 10) }) test_that("simulate_ts supports convenience reforge aliases from generators", { data(krill) result <- simulate_ts( object = krill, frequency = 38e3, model = "DWBA", n_realizations = 10, parameters = list( length_body = function() stats::rnorm(1, mean = 40e-3, sd = 5e-3) ), parallel = FALSE, verbose = FALSE ) expect_type(result, "list") expect_s3_class(result$DWBA, "data.frame") expect_equal(nrow(result$DWBA), 10) expect_true("length_body" %in% colnames(result$DWBA)) expect_true(length(unique(result$DWBA$length_body)) > 1) expect_true(length(unique(result$DWBA$TS)) > 1) }) test_that( "simulate_ts preserves legacy curved krill workflows via length_body", { data(krill) frequency <- 120e3 lengths <- c(0.015, 0.02) params_legacy <- list( length = lengths, sound_speed_sw = 1500, density_sw = 1026, g = 1.02, h = 1.03, theta = function() pi / 2, radius_curvature_ratio = function() 5, n_iterations = 5, n_segments_init = 15, length_init = 17.9e-3, frequency_init = 120e3, phase_sd_init = 0.31 ) params_alias <- params_legacy params_alias$length <- NULL params_alias$length_body <- lengths set.seed(20260331) legacy <- suppressWarnings( simulate_ts( object = krill, batch_by = "length", n_realizations = 2, frequency = frequency, model = "SDWBA_curved", parameters = params_legacy, parallel = FALSE, verbose = FALSE ) ) set.seed(20260331) alias <- suppressWarnings( simulate_ts( object = krill, batch_by = "length_body", n_realizations = 2, frequency = frequency, model = "SDWBA_curved", parameters = params_alias, parallel = FALSE, verbose = FALSE ) ) legacy_df <- legacy$SDWBA_curved[ order(legacy$SDWBA_curved$length, legacy$SDWBA_curved$realization), c("length", "TS") ] alias_df <- alias$SDWBA_curved[ order(alias$SDWBA_curved$length_body, alias$SDWBA_curved$realization), c("length_body", "TS") ] expect_equal(legacy_df$length, alias_df$length_body) expect_equal(legacy_df$TS, alias_df$TS) } ) test_that("simulate_ts works with multiple generating functions", { # Test with distribution parameters data(krill) frequency <- c(120e3) # Parameters with distribution parameters <- list( length = function() runif(1, min = 0.01, max = 0.05), theta = function() runif(1, min = 0, max = pi) ) # Run simulation result <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 10, parameters = parameters, parallel = FALSE, verbose = FALSE ) # Check that result is a data frame expect_type(result, "list") result_df <- result$DWBA expect_s3_class(result_df, "data.frame") # Check number of rows expect_equal(nrow(result_df), 10 * length(frequency)) # Ensure that values are not duplicated expect_true(length(unique(result_df$TS)) == length(frequency) * 10) }) test_that("simulate_ts handles mixed parameter types", { # Test mixing single values, vectors, and generating functions data(krill) frequency <- c(38e3, 70e3) parameters <- list( length = 20e-3, # Single value (held constant) radius_body = seq(1e-3, 3e-3, length.out = 10), # Deterministic grid axis theta = function() runif(1, min = 0, max = pi) # Redrawn per grid cell ) # The 10-level radius axis defines the realizations; no n_realizations needed. result <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", parameters = parameters, parallel = FALSE, verbose = FALSE ) # Check that result is a data frame expect_type(result, "list") result_df <- result$DWBA expect_s3_class(result_df, "data.frame") # Get the results result_df <- result$DWBA # Check that we get the expected number of rows expect_equal(nrow(result_df), length(frequency) * 10) # Ensure that values are not duplicated expect_true(length(unique(result_df$TS)) == length(frequency) * 10) }) test_that("simulate_ts validates inputs correctly", { # Test mixing single values, vectors, and generating functions data(krill) frequency <- c(38e3) parameters <- list( length = 20e-3, # Single value radius_body = seq(1e-3, 3e-3, length.out = 10), theta = function() runif(1, min = 0, max = pi) ) # Test invalid model expect_error( simulate_ts(krill, frequency, "invalid_model", 5, parameters), "Unknown target strength model 'invalid_model'" ) # Test missing batch_by parameter expect_error( simulate_ts(krill, frequency, "calibration", 5, parameters, batch_by = "nonexistent_param" ), "missing from 'parameters'" ) # Test invalid object class expect_error( simulate_ts("not_a_scatterer", frequency, "calibration", 5, parameters), "must be a 'scatterer'-based class" ) }) test_that("simulate_ts works with parallel processing", { # Test mixing single values, vectors, and generating functions data(krill) frequency <- c(38e3, 70e3) parameters <- list( length = 20e-3, # Single value theta = function() { set.seed(999) runif(1, min = 0, max = pi) } ) # Test with parallel = TRUE (default) result_parallel <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 10, parameters = parameters, parallel = TRUE, n_cores = 1, verbose = FALSE ) # Test with parallel = FALSE result_sequential <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 10, parameters = parameters, parallel = FALSE, verbose = FALSE ) # Test batch_by parameter with generating function result_gen <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 10, parameters = list(length = function(x) { set.seed(999) rnorm(1, 20e-3, 1e-6) }), parallel = FALSE, verbose = FALSE ) # Check that result is a data frame expect_type(result_parallel, "list") result_df <- result_parallel$DWBA expect_s3_class(result_df, "data.frame") # Extract sequential sequential_df <- result_sequential$DWBA # Check that we get the expected number of rows expect_equal(nrow(result_df), length(frequency) * 10) # Ensure that values are not duplicated expect_true(length(unique(result_df$TS)) == 2) expect_true(all(result_df$TS == sequential_df$TS)) # Extract generative gen_df <- result_gen$DWBA # Check that we get the expected number of rows expect_equal(nrow(gen_df), length(frequency) * 10) # Ensure that values are not duplicated expect_true(length(unique(gen_df$TS)) == 2) expect_true(all(gen_df$TS != sequential_df$TS)) }) test_that("simulate_ts works with PSOCK clusters when n_cores > 1", { skip_on_cran() data(krill) frequency <- c(38e3, 70e3) parameters <- list( length = 20e-3, theta = function() { set.seed(999) runif(1, min = 0, max = pi) } ) result_parallel <- simulate_ts( object = krill, frequency = frequency, model = "dwba", n_realizations = 4, parameters = parameters, parallel = TRUE, n_cores = 2, verbose = FALSE ) result_sequential <- simulate_ts( object = krill, frequency = frequency, model = "DWBA", n_realizations = 4, parameters = parameters, parallel = FALSE, verbose = FALSE ) expect_true("DWBA" %in% names(result_parallel)) expect_true("DWBA" %in% names(result_sequential)) expect_equal(result_parallel$DWBA$theta, result_sequential$DWBA$theta) expect_equal(result_parallel$DWBA$length, result_sequential$DWBA$length) expect_equal(result_parallel$DWBA$frequency, result_sequential$DWBA$frequency) if (dir.exists(file.path(getNamespaceInfo( asNamespace("acousticTS"), "path" ), "Meta"))) { expect_equal(result_parallel$DWBA$TS, result_sequential$DWBA$TS) } else { expect_true(all(is.finite(result_parallel$DWBA$TS))) expect_true(all(is.finite(result_sequential$DWBA$TS))) } }) test_that("simulate_ts supports theta_body for FLS objects in PSOCK mode", { skip_on_cran() obj <- fls_generate( shape = cylinder( length_body = 0.05, radius_body = 0.003, n_segments = 80 ), density_body = 1045, sound_speed_body = 1520 ) frequency <- seq(38e3, 50e3, by = 2e3) parameters <- list( theta_body = function() { set.seed(999) runif(1, min = 0.5 * pi, max = pi) } ) result_parallel <- simulate_ts( object = obj, frequency = frequency, model = "DWBA", n_realizations = 4, parameters = parameters, parallel = TRUE, n_cores = 2, verbose = FALSE ) result_sequential <- simulate_ts( object = obj, frequency = frequency, model = "dwba", n_realizations = 4, parameters = parameters, parallel = FALSE, verbose = FALSE ) expect_true("DWBA" %in% names(result_parallel)) expect_true("DWBA" %in% names(result_sequential)) expect_equal( result_parallel$DWBA$theta_body, result_sequential$DWBA$theta_body ) expect_equal(result_parallel$DWBA$frequency, result_sequential$DWBA$frequency) if (dir.exists(file.path(getNamespaceInfo( asNamespace("acousticTS"), "path" ), "Meta"))) { expect_equal(result_parallel$DWBA$TS, result_sequential$DWBA$TS) } else { expect_true(all(is.finite(result_parallel$DWBA$TS))) expect_true(all(is.finite(result_sequential$DWBA$TS))) } }) test_that("Simulation errors are raised as expected", { # Test mixing single values, vectors, and generating functions data(krill) frequency <- c(38e3, 70e3) parameters <- list( length = function(x) { c() } # NULL value ) # Test case with invalid output [empty] from generating function expect_error( simulate_ts( object = krill, frequency = frequency, model = "DWBA", batch_by = "length", n_realizations = 10, parameters = parameters, parallel = FALSE, verbose = FALSE ), "Batch parameter 'length' function must return at least 1 valid value." ) }) test_that( "simulation helper utilities print headers and prepare optional clusters", { skip_on_cran() cal_obj <- cal_generate() simulation_grid <- data.frame(realization = 1:2) header <- capture.output( acousticTS:::.print_simulation_header( object = cal_obj, model = "calibration", batch_by = NULL, parameters = list(length = 0.02), parallel = FALSE, simulation_grid = simulation_grid ) ) expect_true(any(grepl("Scatterer-class: CAL", header, fixed = TRUE))) expect_true(any(grepl( "Total simulation realizations: 2", header, fixed = TRUE ))) sequential <- capture.output( cluster <- acousticTS:::.prepare_simulation_cluster( parallel = FALSE, n_cores = 2, object = cal_obj, frequency = 38000, normalized_model = "calibration", simulation_grid = simulation_grid, verbose = TRUE ) ) expect_null(cluster) expect_true(any(grepl( "Preparing sequential simulations", sequential, fixed = TRUE ))) parallel_out <- capture.output( cluster <- acousticTS:::.prepare_simulation_cluster( parallel = TRUE, n_cores = 2, object = cal_obj, frequency = 38000, normalized_model = "calibration", simulation_grid = simulation_grid, verbose = TRUE ) ) on.exit(parallel::stopCluster(cluster), add = TRUE) expect_s3_class(cluster, "cluster") expect_true(any(grepl( "Preparing parallelized simulations", parallel_out, fixed = TRUE ))) } ) test_that("simulation helper utilities are exercised under coverage runs", { cal_obj <- cal_generate() simulation_grid <- data.frame(realization = 1:2) header <- capture.output( acousticTS:::.print_simulation_header( object = cal_obj, model = "calibration", batch_by = "length", parameters = list(length = 0.02), parallel = FALSE, simulation_grid = simulation_grid ) ) expect_true(any(grepl("Batching parameter(s): length", header, fixed = TRUE))) sequential <- capture.output( cluster <- acousticTS:::.prepare_simulation_cluster( parallel = FALSE, n_cores = 2, object = cal_obj, frequency = 38000, normalized_model = "calibration", simulation_grid = simulation_grid, verbose = TRUE ) ) expect_null(cluster) expect_true(any(grepl( "Preparing sequential simulations", sequential, fixed = TRUE ))) parallel_out <- capture.output( cluster <- acousticTS:::.prepare_simulation_cluster( parallel = TRUE, n_cores = 2, object = cal_obj, frequency = 38000, normalized_model = "calibration", simulation_grid = simulation_grid, verbose = TRUE ) ) on.exit(parallel::stopCluster(cluster), add = TRUE) expect_s3_class(cluster, "cluster") expect_true(any(grepl( "Preparing parallelized simulations", parallel_out, fixed = TRUE ))) }) test_that( paste0( "simulation helpers cover scalar batch values, empty combines, and ", "verbose sequential execution" ), { expect_equal( acousticTS:::.prepare_simulation_batch_values( batch_by = c("length", "density_body"), parameters = list(length = c(0.02, 0.03), density_body = 1028.9) ), list(length = c(0.02, 0.03), density_body = 1028.9) ) expect_null(acousticTS:::.combine_simulation_results(list())) cal_obj <- cal_generate() verbose_run <- capture.output( result <- simulate_ts( object = cal_obj, frequency = 38e3, model = "calibration", n_realizations = 1, parameters = list(), parallel = FALSE, verbose = TRUE ) ) expect_true(is.list(result)) expect_s3_class(result$calibration, "data.frame") expect_true(any(grepl("Scatterer-class: CAL", verbose_run, fixed = TRUE))) expect_true(any(grepl("Simulations complete!", verbose_run, fixed = TRUE))) } )