Newer
Older
Ruben Heinrich
committed
Ruben Heinrich
committed
#===== generate_all_benchmarks =====
#'generate_all_benchmark_scripts
#'@description Wrapper function to generate benchmark scripts from all .prj
#' files in a directory
#'@param path string: Path to a benchmark directory to generate scripts from
#'@param sim_path string: Path where all simulation files will be saved
#'@param scripts_path string: Path where benchmark scripts will be saved
#'@param read_in_gmls flag: Optional: Should .gml files just be copied or read
#' in too?
#'@param read_in_vtus flag: Optional: Should .vtu files just be copied or read
#' in too?
#'@param starting_from_prj_path string: Optional:
#'@param skip_prj_paths character: Optional: .prj paths to skip
Ruben Heinrich
committed
generate_all_benchmark_scripts <-
function(path,
starting_from_prj_path = "",
skip_prj_paths = character()){
Ruben Heinrich
committed
path <- unlist(options("r2ogs6.default_benchmark_path"))
Ruben Heinrich
committed
}
Ruben Heinrich
committed
sim_path <- unlist(options("r2ogs6.default_sim_path"))
if(missing(scripts_path)){
scripts_path <- unlist(options("r2ogs6.default_script_path"))
missing_read_in_gmls <- missing(read_in_gmls)
path <- as_dir_path(path)
scripts_path <- as_dir_path(scripts_path)
assertthat::assert_that(assertthat::is.string(starting_from_prj_path))
assertthat::assert_that(is.character(skip_prj_paths))
assertthat::assert_that(assertthat::is.flag(read_in_vtus))
Ruben Heinrich
committed
prj_paths <- list.files(path = path,
pattern = "\\.prj$",
recursive = TRUE,
full.names = TRUE)
Ruben Heinrich
committed
# If we know the benchmarks up to a specific file are working, skip them
if(starting_from_prj_path != ""){
if(is.na(match(starting_from_prj_path, prj_paths))){
warning(paste("Couldn't find path to start from.",
"Returning all paths."),
call. = FALSE)
}else{
start_index <- match(starting_from_prj_path, prj_paths)
prj_paths <- prj_paths[start_index:length(prj_paths)]
}
Ruben Heinrich
committed
}
invalid_xml_paths <- character()
invalid_prj_paths <- character()
Ruben Heinrich
committed
for(i in seq_len(length(prj_paths))){
if(prj_paths[[i]] %in% skip_prj_paths){
next
}
skip_generate <- FALSE
generate_failed <- FALSE
Ruben Heinrich
committed
xml2::read_xml(prj_paths[[i]],
encoding="ISO-8859-1")
message(paste("\nxml2::read_xml() failed for",
prj_paths[[i]], ". Original error message:"))
message(cond)
skip_generate <<- TRUE
if(skip_generate){
invalid_xml_paths <- c(invalid_xml_paths, prj_paths[[i]])
# cat("\nGenerating script from path", prj_paths[[i]])
# Put simulations in their own subfolders under sim_path
sim_subdir <-
paste0(sim_path,
basename(dirname(prj_paths[[i]])), "_",
tools::file_path_sans_ext(basename(prj_paths[[i]])))
out<- tryCatch(
{
if(missing_read_in_gmls){
generate_benchmark_script(prj_path = prj_paths[[i]],
sim_path = sim_subdir,
script_path = scripts_path,
read_in_vtu = read_in_vtus)
}else{
generate_benchmark_script(prj_path = prj_paths[[i]],
sim_path = sim_subdir,
script_path = scripts_path,
read_in_gml = read_in_gmls,
read_in_vtu = read_in_vtus)
}
},
error = function(cond){
message(paste("\ngenerate_benchmark_script() failed for",
prj_paths[[i]], ". Original error message:"))
message(cond)
generate_failed <<- TRUE
}
)
if(generate_failed){
invalid_prj_paths <- c(invalid_prj_paths, prj_paths[[i]])
}
}
return(invisible(list(invalid_xml_paths, invalid_prj_paths)))
#===== generate_benchmark_script =====
Ruben Heinrich
committed
Ruben Heinrich
committed
#'generate_benchmark_script
#'@description Generates a benchmark script from an existing .prj file.
#'@param prj_path string: .prj file the script will be based on
#'@param sim_path string: Path where all simulation files will be saved
#'@param ogs_bin_path string: OpenGeoSys bin folder path
#'@param script_path string: Path where benchmark script will be saved
#'@param read_in_gml flag: Optional: Should .gml file just be copied or read
#' in too?
#'@param read_in_vtu flag: Optional: Should .vtu file(s) just be copied or read
#' in too?
Ruben Heinrich
committed
#'@export
generate_benchmark_script <- function(prj_path,
sim_path,
ogs_bin_path,
script_path,
read_in_gml,
read_in_vtu = FALSE) {
if(missing(ogs_bin_path)){
ogs_bin_path <- unlist(options("r2ogs6.default_ogs_bin_path"))
if(missing(script_path)){
script_path <- unlist(options("r2ogs6.default_script_path"))
Ruben Heinrich
committed
assertthat::assert_that(assertthat::is.string(prj_path))
assertthat::assert_that(assertthat::is.string(sim_path))
assertthat::assert_that(assertthat::is.string(ogs_bin_path))
Ruben Heinrich
committed
assertthat::assert_that(assertthat::is.string(script_path))
assertthat::assert_that(assertthat::is.flag(read_in_vtu))
Ruben Heinrich
committed
#Construct an object from a benchmark and then reverse engineer the call
Ruben Heinrich
committed
ogs6_obj <- OGS6$new(sim_name = "",
sim_id = 1,
Ruben Heinrich
committed
read_in_prj(ogs6_obj,
prj_path,
read_in_vtu,
read_in_gml = FALSE)
Ruben Heinrich
committed
prj_components = prj_top_level_classes()
Ruben Heinrich
committed
sim_name <- tools::file_path_sans_ext(basename(prj_path))
script_str <- paste0("library(r2ogs6)\n\n",
"ogs6_obj <- OGS6$new(sim_name = \"",
sim_name, "\",\n",
Ruben Heinrich
committed
"sim_id = 1,\n",
"sim_path = \"", sim_path, "\")\n\n\n")
# If there is a .gml but it shouldn't be read in, add reference
if (!is.null(ogs6_obj$geometry)) {
# If read_in_gml isn't supplied, check number of lines in .gml file
# since string concatenation is slow
if(missing(read_in_gml)){
read_in_gml <- (length(readLines(ogs6_obj$geometry)) <=
unlist(options("r2ogs6.max_lines_gml")))
}
assertthat::assert_that(assertthat::is.flag(read_in_gml))
if(!read_in_gml){
script_str <- paste0(script_str,
"ogs6_obj$add_gml(",
construct_add_call(ogs6_obj$geometry),
")\n\n"
)
}else{
ogs6_obj$add_gml(OGS6_gml$new(ogs6_obj$geometry))
script_str <- paste0(script_str,
construct_add_call(ogs6_obj$gml),
"\n\n")
}
Ruben Heinrich
committed
# Add .vtu references and optionally, OGS6_vtu objects
for(i in seq_len(length(ogs6_obj$meshes))){
script_str <- paste0(script_str,
"ogs6_obj$add_vtu(",
construct_add_call(ogs6_obj$meshes[[i]]), ",\n",
read_in_vtu,
")\n\n")
}
# Add class objects (and such in wrapper lists)
for(i in seq_len(length(prj_components))){
get_component_call <- paste0("ogs6_obj$", names(prj_components)[[i]])
Ruben Heinrich
committed
ogs6_component <- eval(parse(text = get_component_call))
# If benchmark doesn't have components of specified name, skip
Ruben Heinrich
committed
if(is.null(ogs6_component) || length(ogs6_component) == 0){
next
}
#If objects are not in wrapper list, wrap them up for seq_along()
if(any(grepl("r2ogs6_", class(ogs6_component), fixed = TRUE)) ||
any(grepl("OGS6_", class(ogs6_component), fixed = TRUE))){
Ruben Heinrich
committed
ogs6_component <- list(ogs6_component)
}
for(j in seq_along(ogs6_component)){
script_str <-
paste0(script_str,
paste0(construct_add_call(ogs6_component[[j]]),
"\n\n"))
Ruben Heinrich
committed
}
}
script_str <- paste0(script_str,
"run_simulation(ogs6_obj,\n",
"ogs_bin_path = \"", ogs_bin_path, "\")\n")
Ruben Heinrich
committed
#If no destination file was defined, print output to console
Ruben Heinrich
committed
if(script_path != ""){
if(!dir.exists(script_path)){
dir.create(script_path, showWarnings = FALSE)
Ruben Heinrich
committed
}
filename <- paste0(script_path, sim_name, ".R")
if(file.exists(filename)){
filename <- paste0(script_path,
basename(dirname(prj_path)),
"__",
sim_name,
".R")
if(file.exists(filename)){
warning("\nMultiple .prj files with same name in 'path'\n",
call. = FALSE)
}
}
cat(script_str, file = filename)
Ruben Heinrich
committed
}else{
cat(script_str)
}
return(invisible(script_str))
}
#'construct_add_call
#'@description Constructs a call based on an OGS6 component. This is a
#' recursive function, handle with care.
#'@param object An object (numeric, character, list, NULL, OGS6 or r2ogs6 class
#' object)
#'@param nested_call Optional: For recursion purposes, you should leave this as
#' it is.
Ruben Heinrich
committed
#'@return A string representing the code with which the component would be added
#' to an OGS6 object
construct_add_call <- function(object, nested_call = FALSE) {
#For values of type numeric or character, dput will give us usable output
if(is.character(object) ||
is.numeric(object)){
ret_str <- paste(utils::capture.output(dput(object)), collapse="\n")
return(invisible(ret_str))
}
#For NULL values we return "NULL" as string
if(is.null(object)){
return("NULL")
}
#For r2ogs6 objects we need to use recursion
if(any(grepl("r2ogs6", class(object), fixed = TRUE)) ||
any(grepl("OGS6", class(object), fixed = TRUE))){
class_name <- ""
formals_call <- ""
init_prefix <- ""
use_s3_syntax <- TRUE
if("R6" %in% class(object)){
class_name <- grep("OGS6", class(object),
fixed = TRUE, value = TRUE)
use_s3_syntax <- FALSE
init_prefix <- "$new"
}else{
class_name <- grep("r2ogs6", class(object),
fixed = TRUE, value = TRUE)
}
assertthat::assert_that(length(class_name) == 1)
tag_name <- paste(utils::tail(unlist(strsplit(class_name, "_")), -1),
collapse = "_")
Ruben Heinrich
committed
Ruben Heinrich
committed
#Grab helper
param_names <- get_class_args(class_name)
Ruben Heinrich
committed
#Handle Ellipsis if it exists by removing and substituting it
if("..." %in% param_names){
param_names <- param_names[param_names != "..."]
param_names <- c(param_names, object$unwrap_on_exp)
}
Ruben Heinrich
committed
param_strs <- list()
for(i in seq_len(length(param_names))){
get_param_call <- paste0("object$", param_names[[i]])
param <- eval(parse(text = get_param_call))
param_str <- construct_add_call(param, TRUE)
param_strs <- c(param_strs, list(param_str))
}
content_str <- paste(param_names,
param_strs,
sep = " = ",
collapse = ",\n")
Ruben Heinrich
committed
ret_str <- paste0(class_name, init_prefix,
"(", content_str, ")")
#If call isn't nested, it can be added
if(!nested_call){
ret_str <- paste0("ogs6_obj$add(", ret_str, ")\n")
Ruben Heinrich
committed
}
ret_str <- delete_nulls_from_str(ret_str)
ret_str <- delete_keywords_from_str(ret_str)
Ruben Heinrich
committed
ret_str <- delete_empty_from_str(ret_str)
Ruben Heinrich
committed
return(invisible(ret_str))
}
#For tibbles we don't need recursion, but they still require extra handling
if("tbl_df" %in% class(object)){
tib_str <- paste(names(object), object, sep = " = ", collapse = ",\n")
ret_str <- paste0("tibble::tibble(", tib_str, ")")
return(invisible(ret_str))
}
#For lists we need to use recursion
Ruben Heinrich
committed
element_strs <- lapply(object, function(x){construct_add_call(x, TRUE)})
if(is.null(names(object)) ||
rlist::list.any(names(object) == "")){
content_str <- paste(element_strs, collapse = ",\n")
}else{
content_str <- paste(names(object),
element_strs,
sep = " = ",
collapse = ",\n")
Ruben Heinrich
committed
}
ret_str <- paste0("list(", content_str, ")")
return(invisible(ret_str))
}
}
#'delete_nulls_from_str
#'@description Utility function to delete "param_name = NULL" from a string,
#' this isn't necessary for functionality of generate_benchmark_script but will
#' make generated scripts way more readable.
delete_nulls_from_str <- function(string){
Ruben Heinrich
committed
regexp_1 <- ",[\n|[:space:]]?[\\w_]* = NULL"
regexp_2 <- "[\\w_]* = NULL,[\n|[:space:]]?"
string <- stringr::str_remove_all(string, regexp_1)
string <- stringr::str_remove_all(string, regexp_2)
return(invisible(string))
}
Ruben Heinrich
committed
#'delete_empty_from_str
#'@description Utility function to delete "param_name = list()" from a string,
#' this isn't necessary for functionality of generate_benchmark_script but will
#' make generated scripts way more readable.
#'@param string string
delete_empty_from_str <- function(string){
Ruben Heinrich
committed
regexp_1 <- ",[\n|[:space:]]?[\\w_]* = list\\(\\)"
regexp_2 <- "[\\w_]* = list\\(\\),[\n|[:space:]]?"
Ruben Heinrich
committed
string <- stringr::str_remove_all(string, regexp_1)
string <- stringr::str_remove_all(string, regexp_2)
return(invisible(string))
}
#'delete_keywords_from_str
#'@description Utility function to delete keywords from a string,
#' this is important because there is a <repeat> tag in <time_loop> and
#' "repeat" is a reserved word in R (extend this function if you find more
#' reserved words)
#'@param string string
delete_keywords_from_str <- function(string){
string <- stringr::str_remove_all(string, "repeat = ")
return(invisible(string))
}