sic_inputs.R 9.58 KiB
#' Get PAR filename for a given scenario in a SIC project
#'
#' @inheritParams sic_run_export
#'
#' @return A [character] with the path of the PAR file
#' @noRd
#'
sic_get_par_filename <- function(cfg, scenario) {
  x <- read_xml(cfg$project$path)
  xPath <- sprintf("/Reseau/Flu[@nScenario=%d]/NomFicPar", scenario)
  file <- x %>% xml_find_first(xPath) %>% xml_text
  return(file.path(dirname(cfg$project$path), file))
#' Write a PAR file for SIC simulation
#' Write inputs  in a PAR file respectively to \url{https://sic.g-eau.fr/Format-of-the-par-file}.
#' @inheritParams sic_run_mesh
#' @return Nothing.
#' @export
#' @examples
#' \dontrun{
#' # Setting the model configuration
#' cfg <- cfg_tmp_project()
#' # How to impose an hydrograph at the upstream offtake of the model?
#' # Define the location of the upstream boundary condition to set
#' locations <- SicLocations(list(Nd = 1, Pr = 1, car = "Q"))
#' # Define its time series
#' sicInputs <- SicInput(data.frame(t = c(0, 3600, 7200), # time in seconds
#'                                 v = c(5, 20, 5)),     # flows
#'                       locations = locations)
#' # Write the parameters in the PAR file
#' sic_write_par(cfg, 1, sicInputs)
#' }
sic_write_par <- function(cfg, scenario, sicInputs) {
  if (is.null(attr(cfg, "config"))) {
    stop("`cfg` should be created with `loadConfig()`")
  stopifnot(is.numeric(scenario),
            length(scenario) == 1,
            inherits(sicInputs, "SicInput") || inherits(sicInputs, "SicInputs"))
  if (inherits(sicInputs, "SicInput")) sicInputs <- merge(sicInputs)
  sParFileName = sic_get_par_filename(cfg, scenario)
  df = data.frame(C1 = "// PAR file for SIC",
                  C2 = "automatically generated by rsic2",
                  C3 = "",
                  stringsAsFactors = FALSE)
  i = 0
  for (input in sicInputs) {
    i = i + 1
    # Localisation
    if (is.null(input$locations)) {
      dfLoc <- NULL
    } else {
      dfLoc <- data.frame(C1 = paste0("L", i), C2 = input$locations, C3 = "", stringsAsFactors = FALSE)
    if (is.data.frame(input$data)) {
      dfHeader <- data.frame(C1 = paste0("X", i), C2 = "LOI", C3 = ifelse(input$interpolated, "R", "E"))
      dfData <- cbind(rep(paste0("X", i), nrow(input$data)), input$data)
      names(dfData) <- names(df)
      dfData <- rbind(dfHeader, dfData)
    } else {
      dfData <- data.frame(C1 = paste0("X", i), C2 = input$data, C3 = "")
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
} df = rbind(df, dfLoc, dfData) } write.table(df, file = sParFileName, col.names = F, row.names = F, sep = "\t", quote = F) } #' Create a SIC input for a PAR file #' #' @param x either a fixed input, a [numeric] vector of time in seconds, #' a [POSIXt] vector of time, a [matrix] or a [data.frame] with 2 columns (time and value) #' @param values a [numeric] vector used if `x` is a vector of [numeric] or [POSIXt] #' @param start a [POSIXt] indicating the start time to use as time zero in the simulation #' @param locations input locations created with [SicLocation] or [SicLocations] #' @param interpolated Interpolation mode `TRUE` for "ramp" mode and `FALSE` for "step" mode #' @param ... used for S3 method compatibility #' #' @return A *SicInput* object which is a list with the following items: #' #' - `locations`: a [SicLocations] object #' - `data`: [numeric], the fixed value, or [data.frame], the time series to apply to the locations #' - `interpolated`: [logical], interpolation mode #' #' @rdname SicInput #' @family SicInput #' @export #' #' @examples #' # How to impose 5 m3/s in the upstream offtake of the model? #' # Define location of the boundary condition to set #' locations <- SicLocations(list(Nd = 1, Pr = 1, car = "Q")) #' # Define its value #' sicInputs <- SicInput(5, locations = locations) #' #' # How to impose an hydrograph at the upstream offtake of the model? #' sicInputs <- SicInput(data.frame(t = c(0, 3600, 7200), # time in seconds #' v = c(5, 20, 5)), # flows #' locations = locations) SicInput <- function(x, ...) { UseMethod("SicInput", x) } #' @rdname SicInput #' @export SicInput.numeric <- function(x, values = NULL, locations, interpolated = TRUE, ...) { if (!(inherits(locations, "SicLocation") || inherits(locations, "SicLocations"))){ stop("`locations` should be of class 'SicLocation' or 'SicLocations'") } if (is.null(values)) { if (length(x) != 1) stop("For a single value `x` should be of length 1") data <- x } else { if (length(x) != length(values)) stop("Lenghts of `x` and `values`should be equal.") data <- data.frame(t = x, v = values) } sicInput <- list( locations = locations, data = data, interpolated = interpolated ) class(sicInput) <- c("SicInput", class(sicInput)) return(sicInput) } #' @rdname SicInput #' @export SicInput.POSIXt <- function(x, values, start = NULL, ...) { if (is.null(start)) start = x[1]
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
if (!inherits(start, "POSIXt")) stop("`start` should be of class 'POSIXt'.") # Conversion in seconds x <- as.numeric(difftime(x, start, units = "secs")) if (any(x < 0)) stop("Negative time detected. Check the start time or the time series order.") SicInput(x, values, ...) } #' @rdname SicInput #' @export SicInput.data.frame <- function(x, ...) { SicInput(x[,1], x[, 2], ...) } #' @rdname SicInput #' @export SicInput.matrix <- function(x, ...) { SicInput(x[,1], x[, 2], ...) } #' Merge SicInput objects into a list #' #' @param x a [SicInput] object to merge #' @param y a [SicInput] object to merge #' @param ... other [SicInput] objects to merge #' #' @details The list of parameter is compliant with the `merge` S3 method #' available in R, so this method can either be called by typing #' `merge` or `merge.SicInput`. #' #' @return A `SicInputs` object which is a list of [SicInput] #' @export #' #' @examples #' # How to impose 5 m3/s in the upstream offtake of the model? #' # Define location of the boundary condition to set #' locations <- SicLocation(list(Nd = 1, Pr = 1, car = "Q")) #' # Define its value #' sicInputUpstream <- SicInput(5, locations = locations) #' #' # Opening a gate at 0.5 m #' sicInputGate <- SicInput( #' 0.5, #' locations = SicLocations(list(Bf = 3, Sn = 2, Ouv = 1, Car = "Ouverture")) #' ) #' #' # Merging all inputs #' sicInputs <- merge(sicInputUpstream, sicInputGate) #' merge.SicInput <- function(x, y = NULL, ...) { sicInputs <- list(x, y, ...) sicInputs[sapply(sicInputs, is.null)] <- NULL class(sicInputs) <- c("SicInputs", class(sicInputs)) return(sicInputs) } #' Set locations of a SIC model input #' #' Do the same as [SicLocation] for eventually several locations #' #' @param ... One or several [list] describing a location (See [SicLocation]) #' #' @return a *SicLocations* object which is a [list] of [SicLocation]. #' @family SicInput #' @export #'
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
#' @examples #' # Applying the same flow to offtakes located in nodes number 1 to 10 #' locations <- lapply(seq(10), function(i) { list(Nd = i, Pr = 1, Car = "Q")}) #' locations <- do.call(SicLocations, locations) #' sicInputOfftakes <- SicInput(-0.5, locations = locations) SicLocations <- function(...) { locations <- list(...) if (length(locations) == 1) locations <- locations[[1]] # Handle a single location in the parameters if (length(locations[[1]]) == 1) locations <- list(locations) if(!is.list(locations)) stop("`locations` must be a list") l <- sapply(locations, SicLocation) class(l) <- c("SicLocations", class(l)) return(l) } #' Set a location of a SIC model input #' #' @param location a [list] containing the location keys (see details) #' #' @return a *SicLocation* object which is a [character] string in the same format as the locations described in the sic documentation of PAR files: \url{https://sic.g-eau.fr/Format-of-the-par-file}. #' #' @family SicInput #' @export #' #' @inherit SicInput return examples #' SicLocation <- function(location) { names(location) <- toupper(names(location)) # Checks availLoc <- c("BF", "ND", "PR", "ST", "OUV", "SN", "PBF", "CAR") availCar <- c("Q", "Z", "KMin", "KMoy", "Inf", "CoteRadier", "Largeur", "Ouverture", "CoefQR", "SurverseHauteur", "CoefQSurverse", "TanAl", "CoefQT", "CoteAxe", "Rayon", "D", "JMax", "S1S2", "Decal", "Decrement", "CoteAmont") if (!is.list(location)) stop("Each `location` must be a list") if (!"CAR" %in% names(location)) stop("Each location should have at least an item 'CAR'") if (!any(names(location) %in% c("BF", "ND"))) stop("a location should have at least an item 'BF' or 'ND'") dfIncompat <- expand.grid(c("BF", "SN", "PBF"), c("ND", "PR", "ST")) lapply(seq_len(nrow(dfIncompat)), function(i) { if (all(unlist(dfIncompat[i, ]) %in% names(location))) stop("These items can't be together in a location: ", paste(paste0("'", unlist(dfIncompat[i, ]), "'"), collapse = ", ")) }) if (any(names(location) %in% c("SN", "PBF")) & !"BF" %in% names(location)) stop("Location with 'SN' or 'PBF' item should have an item 'BF'") # Create location string l <- lapply(names(location), function(objType) { if (!objType %in% availLoc) stop("Unknown location type: ", objType) if (objType == "CAR" && !location[[objType]] %in% availCar) stop("Value '", location[[objType]], "' of item 'CAR' unsupported. It should be one of: ", paste(paste0("'", availCar, "'"), sep = ", ")) if (objType != "CAR" && !is.numeric(location[[objType]])) stop("Item '", objType, "' should be numeric.") return(paste(objType, location[[objType]], sep = "=")) }) s <- paste(unlist(l), collapse = "\t") class(s) <- c("SicLocation", class(s)) return(s) }