#' Parameters for bsub
#'
#' @param ... Arguments for the parameters, see "details" section
#' @param RESET reset to default values
#' @param READ.ONLY please ignore
#' @param LOCAL please ignore
#' @param ADD please ignore
#'
#' @details
#' There are following global parameters:
#'
#' - `packages`: A character vector with package names that will be loaded before running the script.
#' - `image`: A character vector of RData/rda files that will be loaded before running the script.
#' - `temp_dir`: Path of temporary folder where the temporary R/bash scripts will be put.
#' - `output_dir`: Path of output folder where the output/flag files will be put.
#' - `enforce`: If a flag file for the job is found, whether to enforce to rerun the job.
#' - `R_version`: The version of R.
#' - `working_dir`: The working directory.
#' - `ignore`: Whether ignore `bsub_chunk`, `bsub_script` and `bsub_cmd`.
#' - `local`: Run job locally (not submitting to the LSF cluster)?
#' - `call_Rscript`: How to call `Rscript` by specifying an R version number.
#' - `submission_node`: A list of node names for submitting jobs.
#' - `login_node`: This value basically is the same as ``submission_node`` unless the login nodes are different from submission nodes.
#' - `sh_head`: Commands that are written as head of the sh script.
#' - `user`: Username on the submission node.
#' - `group`: The user group
#' - `ssh_envir`: The commands for setting bash environment for successfully running bjobs, bsub, ...
#' - `bsub_template`: Template for constructing `bsub` command.
#' - `parse_time`: A function that parses time string from the LSF `bjobs` command to a `POSIXct` object.
#' - `verbose`: Whether to print more messages.
#'
#' `ssh_envir` should be properly set so that LSF binaries such as `bsub` or `bjobs` can be properly found.
#' There are some environment variables initialized when logging in the bash terminal while they are not initialized with the
#' ssh connection. Thus, some environment variables should be manually set.
#'
#' An example for `ssh_envir` is as follows. The `LSF_ENVDIR` and `LSF_SERVERDIR` should be defined and exported.
#'
#' ```r
#' c("source /etc/profile",
#' "export LSF_ENVDIR=/opt/lsf/conf",
#' "export LSF_SERVERDIR=/opt/lsf/10.1/linux3.10-glibc2.17-x86_64/etc")
#'```
#'
#' The values of these two variables can be obtained by entering following commands in your bash terminal (on the submission node):
#'
#' ```
#' echo $LSF_ENVDIR
#' echo $LSF_SERVERDIR
#'```
#'
#' The time strings by LSF `bjobs` command might be different for different configurations. The **bsub**
#' package needs to convert the time strings to `POSIXlt` objects for calculating the time difference. Thus, if
#' the default time string parsing fails, users need to provide a user-defined function and set with ``parse_time``
#' option in `bsub_opt`. The function accepts a vector of time strings and returns a `POSIXlt` object. For example,
#' if the time string returned from ``bjobs`` command is in a form of ``Dec 1 18:00:00 2019``, the parsing function
#' can be defined as:
#'
#' ```r
#' bsub_opt$parse_time = function(x) {
#' as.POSIXlt(x, format = "\\%b \\%d \\%H:\\%M:\\%S \\%Y")
#' }
#' ```
#'
#' @returns The corresponding option values.
#' @import GlobalOptions
#' @import GetoptLong
#' @export
#' @examples
#' # The default bsub_opt
#' bsub_opt
bsub_opt = set_opt(
ask = list(
.value = TRUE,
.visible = FALSE
),
packages = list(
.class = "character"
),
image = list(
.class = "character",
.filter = function(x) {
l = file.exists(x)
if(all(!l)) {
message_wrap(qq("All the image files specified do not exist. Save current workspace to @{x[1]}? [y/n]"), appendLF = FALSE)
answer = readline()
if(answer %in% c("y", "Y", "yes", "Yes")) {
save.image(file = x[1])
x = x[1]
l = TRUE
} else {
stop("All image files do not exist.")
}
}
if(any(!l)) {
stop(qq("Some of the image files do not exist."))
}
normalizePath(x)
}
),
temp_dir = list(
.value = "~/.bsub_temp",
.validate = function(x) {
check_temp_dir(x, ask = .v$ask)
TRUE
},
.filter = function(x) {
normalizePath(x)
}
),
output_dir = list(
.value = function() .v$temp_dir,
.validate = function(x) {
if(!file.exists(x) && .v$ask) {
answer = readline(qq("create output_dir: @{x}? [y|n] "))
if(answer %in% c("y", "Y", "yes", "Yes", "YES")) {
dir.create(x, recursive = TRUE, showWarnings = FALSE)
} else {
stop_wrap(qq("not allowed to create @{x}."))
}
}
TRUE
},
.filter = function(x) {
normalizePath(x)
}
),
enforce = list(
.value = TRUE,
.class = "logical"
),
R_version = list(
.value = qq("@{R.version$major}.@{R.version$minor}")
),
working_dir = list(
.value = "",
.validate = file.exists,
.length = 1
),
wd = list(.synonymous = "working_dir"),
ignore = list(
.value = FALSE,
.class = "logical"
),
local = list(
.value = FALSE,
.class = "logical"
),
call_Rscript = list(
.value = function(version) "Rscript",
.class = "function"
),
submission_node = list(
.value = NULL
),
login_node = list(
.value = function() .v$submission_node
),
ssh_session = list(
.value = NULL,
.private = TRUE,
.visible = FALSE
),
debug = list(
.value = TRUE,
.visible = FALSE
),
sh_head = list(
.value = "",
.class = "character"
),
user = list(
.value = Sys.info()['user']
),
group = list(
.value = NULL
),
ssh_envir = list(
.value = c("source /etc/profile")
),
bsub_template = list(
.value = function(name, hour, memory, core, output, group = NULL, ...) {
if(identical(group, "") || identical(group, NULL)) {
cmd = qq("bsub -J '@{name}' -W '@{floor(hour)}:@{floor((hour-floor(hour))*60)}' -n @{core} -R 'rusage[mem=@{round(memory*1024)}]'",
" -o '@{output}'",
" ", sep = " \\\n")
} else {
cmd = qq("bsub -J '@{name}' -W '@{floor(hour)}:@{floor((hour-floor(hour))*60)}' -n @{core} -R 'rusage[mem=@{round(memory*1024)}]'",
" -G @{bsub_opt$group} -o '@{output}'",
" ", sep = " \\\n")
}
return(cmd)
},
.class = "function"
),
parse_time = list(
.value = NULL,
.class = "function"
),
verbose = list(
.value = FALSE,
.class = "logical"
),
history_timestamp = list(
.value = NULL,
.visible = FALSE
)
)
check_temp_dir = function(x, ask = TRUE) {
if(!file.exists(x) && ask) {
answer = readline(qq("create temp_dir: @{x}? [y|n] "))
if(answer %in% c("y", "Y", "yes", "Yes", "YES")) {
dir.create(x, recursive = TRUE, showWarnings = FALSE)
} else {
stop_wrap(qq("not allowed to create @{x}."))
}
} else {
all_f = list.files(x, full.names = TRUE)
if(length(all_f)) {
file_size = sum(file.info(all_f)[, "size"], na.rm = TRUE)
if(file_size < 1024) {
fs = paste0(file_size, "Byte")
} else if(file_size < 1024^2) {
fs = paste0(round(file_size/1024, 1), "KB")
} else if(file_size < 1024^3) {
fs = paste0(round(file_size/1024^2, 1), "MB")
} else {
fs = paste0(round(file_size/1024^3, 1), "GB")
}
message(qq("There are @{length(all_f)} temporary files (@{fs}) in @{x}"))
}
}
}
#' Print current configurations
#'
#' @details
#' This function is only for printing. Use [`bsub_opt()`] to change configurations.
#'
#' You simply type `bconf` (without the brackets) in the interactive R console.
#'
#' @returns A `bconf` object.
#'
#' @export
#' @rdname bconf
#' @docType data
#' @examples
#' bconf
bconf = structure(NA, class = "bconf")
#' @param x A `bconf` object.
#' @param ... Other parameters.
#'
#' @export
#' @rdname bconf
print.bconf = function(x, ...) {
cat(get_bconf_message(x), "\n")
flag = 0
msg = NULL
if(!file.exists(bsub_opt$temp_dir)) {
msg = c(msg, crayon::red(qq("!! The temp_dir (@{bsub_opt$temp_dir}) does not exist.")))
flag = 1
}
if(bsub_opt$temp_dir != bsub_opt$output_dir) {
if(!file.exists(bsub_opt$output_dir)) {
msg = c(msg, crayon::red(qq("!! The output_dir (@{bsub_opt$output_dir}) does not exist.")))
flag = 2
}
}
if(flag) {
if(flag == 1) {
msg = c(msg, crayon::red("!! The directory can be created manually, or by setting a value to"),
crayon::red("!! `bsub_opt$temp_dir`, or by the first call of `bsub_*()`."))
} else {
msg = c(msg, crayon::red("!! The directory can be created manually, or by setting a value to"),
crayon::red("!! `bsub_opt$output_dir`, or by the first call of `bsub_*()`."))
}
cat("\n")
cat(msg, sep = "\n")
cat("\n")
}
}
get_bconf_message = function(x, ...) {
x = bsub_opt()
msg = NULL
msg = c(msg, "Configurations for bsub:")
if(is.null(x$user)) {
msg = c(msg, " * user is not defined")
} else {
msg = c(msg, qq(" * user for connecting submission node: @{crayon::green(x$user)}"))
}
if(!is.null(x$group)) {
msg = c(msg, qq(" * user group : @{crayon::green(x$group)}"))
}
if(is.null(x$submission_node)) {
msg = c(msg, qq(crayon::red(" ! submission node is not defined")))
} else {
msg = c(msg, qq(" * submission node: @{crayon::green(paste(x$submission_node, collapse = ', '))}"))
if(length(setdiff(x$login_node, x$submission_node))) {
msg = c(msg, qq(" * login node: @{crayon::green(paste(x$login_node, collapse = ', '))}"))
}
}
if(!is.null(x$R_version)) {
msg = c(msg, qq(" * global R version: @{crayon::green(x$R_version)}"))
}
msg = c(msg, " * command to call `Rscript`:")
msg = c(msg, crayon::green(paste0(" ", deparse(body(x$call_Rscript)), " foo.R")))
if(!is.null(x$package)) {
msg = c(msg, qq(" * Global R packages: @{crayon::green(paste(x$packages, collapse=','))}"))
}
if(!is.null(x$image)) {
msg = c(msg, " * Global image files that will be loaded to every job:")
for(f in x$image) {
msg = c(msg, crayon::green(qq(" - @{f}")))
}
}
msg = c(msg, qq(" * temporary directory: @{crayon::green(x$temp_dir)}"))
if(!x$enforce) {
msg = c(msg, " - successful jobs are skipped.")
}
msg = c(msg, "")
msg = c(msg, "Configurations can be modified by `bsub_opt()` function")
return(paste(msg, collapse = "\n"))
}
STATUS_COL = c("RUN" = "darkgreen", "EXIT" = "#e41a1c", "PEND" = "#ff7f00", "DONE" = "#377eb8", "unknown" = "grey")
ENV = new.env()
ENV$on_same_file_system = NULL
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.