Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel run #511

Merged
merged 11 commits into from
Apr 30, 2021
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export(removeSimulationFromCache)
export(resetSimulationCache)
export(runSensitivityAnalysis)
export(runSimulation)
export(runSimulationsConcurrently)
export(saveSimulation)
export(scaleParameterValues)
export(setMoleculeInitialValues)
Expand Down
2 changes: 1 addition & 1 deletion R/get-net-task.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#' Returns an instace of the specified .NET Task
#' Returns an instance of the specified .NET Task
#'
#' @param taskName The name of the task to retrieve (without the Get)
#'
Expand Down
61 changes: 61 additions & 0 deletions R/utilities-simulation.R
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,67 @@ runSimulation <- function(simulation, population = NULL, agingData = NULL, simul
SimulationResults$new(results, simulation)
}

#' @title Runs a set of simulations.
#' @details Runs a set of simulations (only individual simulations) and returns
#' a named list of \code{SimulationResults}. The names of the entries are the IDs of the
#' corresponding simulation (i.e. \code{simulation$id}).
#'
#' @param simulations A list of \code{Simulation} objects to simulate.
#' @param simulationRunOptions Optional instance of a \code{SimulationRunOptions} used during the simulation run.
#' @param silentMode If \code{TRUE}, no warnings are displayed if a simulation fails.
#' Default is \code{FALSE}.
#'
#' @return A list of \code{SimulationResults} objects with names being the IDs of the simulations. If a simulation fails, the result for this simulation is \code{NULL}
#'
#' @examples
#' simPath <- system.file("extdata", "simple.pkml", package = "ospsuite")
#' sim <- loadSimulation(simPath)
#' sim2 <- loadSimulation(simPath)
#' sim3 <- loadSimulation(simPath)
#' results <- runSimulationsConcurrently(list(sim, sim2, sim3))
#' @export
runSimulationsConcurrently <- function(simulations, simulationRunOptions = NULL, silentMode = FALSE) {
validateIsOfType(simulations, Simulation)
simulationRunner <- getNetTask("ConcurrentSimulationRunner")
if (!is.null(simulationRunOptions)) {
validateIsOfType(simulationRunOptions, SimulationRunOptions)
rClr::clrSet(simulationRunner, "SimulationRunOptions", simulationRunOptions$ref)
}

simulations <- c(simulations)
# Create an Id <-> simulation map to get the correct simulation for the results.
simulationsIdMap <- list()

# Create SimulationRunnerConcurrentOptions and add all simulations
for (simulation in simulations) {
simulationsIdMap[[simulation$id]] <- simulation
rClr::clrCall(simulationRunner, "AddSimulation", simulation$ref)
}
# Run all simulations
results <- rClr::clrCall(simulationRunner, "RunConcurrently")
# Pre-allocate lists for SimulationResult
simulationResults <- vector("list", length(simulations))
# Set the order of IDs so the results appear in the same order as simulations were provided
names(simulationResults) <- names(simulationsIdMap)

for (i in seq_along(results)) {
resultObject <- results[[i]]
id <- rClr::clrGet(resultObject, "Id")
succeeded <- rClr::clrGet(resultObject, "Succeeded")
if (succeeded) {
# Get the correct simulation and create a SimulationResults object
simulationResults[[id]] <- SimulationResults$new(ref = rClr::clrGet(resultObject, "Result"), simulation = simulationsIdMap[[id]])
next()
}
# If the simulation run failed, show a warning
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not we set simulationResults[[id]] to NULL or sthg here ? to show that something is off?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All values are NULL after initialization of simulationResults and are overwritten if the result exists.

if (!silentMode) {
errorMessage <- rClr::clrGet(resultObject, "ErrorMessage")
warning(errorMessage)
}
}
return(simulationResults)
}

#' @title Creates and returns an instance of a \code{SimulationBatch} that can be used to efficiently vary parameters and initial values in a simulation
#'
#' @param simulation Instance of a \code{Simulation} to simulate in a batch mode
Expand Down
4 changes: 2 additions & 2 deletions man/getNetTask.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions man/runSimulationsConcurrently.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions tests/testthat/test-utilities-simulation.R
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,60 @@ test_that("It creates a simulation batch when using only molecule instances", {
simulationBatch <- createSimulationBatch(sim, moleculesOrPaths = molecule)
expect_false(is.null(simulationBatch))
})

context("runSimulationsConcurrent")

test_that("It runs one individual simulation without simulationRunOptions", {
resetSimulationCache()

sim <- loadTestSimulation("S1", loadFromCache = FALSE)
results <- runSimulationsConcurrently(sim)
expect_equal(length(results), 1)
expect_true(isOfType(results[[1]], "SimulationResults"))
})

test_that("It runs one individual simulation with simulationRunOptions", {
resetSimulationCache()
sim <- loadTestSimulation("S1", loadFromCache = FALSE)
simRunOptions <- SimulationRunOptions$new()
results <- runSimulationsConcurrently(sim, simulationRunOptions = simRunOptions)
expect_equal(length(results), 1)
expect_true(isOfType(results[[1]], "SimulationResults"))
})

test_that("It runs multiple individual simulations", {
resetSimulationCache()
sim <- loadTestSimulation("S1", loadFromCache = FALSE)
sim2 <- loadTestSimulation("S1", loadFromCache = FALSE)
results <- runSimulationsConcurrently(c(sim, sim2))
expect_equal(length(results), 2)
# Check the ids
expect_equal(names(results)[[1]], sim$id)
expect_true(isOfType(results[[1]], "SimulationResults"))
})

test_that("It shows a warning if one of simulations fails. Results for this simulation are NULL", {
resetSimulationCache()
sim <- loadTestSimulation("S1", loadFromCache = FALSE)
sim2 <- loadTestSimulation("S1", loadFromCache = FALSE)
sim$solver$relTol <- 1000

expect_warning(results <- runSimulationsConcurrently(c(sim, sim2)))
expect_equal(length(results), 2)
expect_equal(names(results)[[2]], sim2$id)
expect_null(results[[sim$id]])
expect_true(isOfType(results[[2]], "SimulationResults"))
})

test_that("It does not show a warning if one of simulations fails in silent mode. Results for this simulation are NULL", {
resetSimulationCache()
sim <- loadTestSimulation("S1", loadFromCache = FALSE)
sim2 <- loadTestSimulation("S1", loadFromCache = FALSE)
sim$solver$relTol <- 1000

expect_warning(results <- runSimulationsConcurrently(c(sim, sim2), silentMode = TRUE), regexp = NA)
expect_equal(length(results), 2)
expect_equal(names(results)[[2]], sim2$id)
expect_null(results[[sim$id]])
expect_true(isOfType(results[[2]], "SimulationResults"))
})