Newer
Older
Delaigue Olivier
committed
CreateInputsCrit <- function(FUN_CRIT,
InputsModel,
RunOptions,
Delaigue Olivier
committed
Qobs, # deprecated
Obs,
Delaigue Olivier
committed
VarObs = "Q",
Delaigue Olivier
committed
BoolCrit = NULL,
transfo = "",
Delaigue Olivier
committed
Weights = NULL,
Delaigue Olivier
committed
Ind_zeroes = NULL, # deprecated
Delaigue Olivier
committed
epsilon = NULL,
Delaigue Olivier
committed
warnings = TRUE,
Delaigue Olivier
committed
verbose = TRUE) {
ObjectClass <- NULL
Delaigue Olivier
committed
## ---------- check arguments
Delaigue Olivier
committed
Delaigue Olivier
committed
if (!missing(Qobs)) {
if (missing(Obs)) {
Delaigue Olivier
committed
if (warnings) {
Delaigue Olivier
committed
warning("argument 'Qobs' is deprecated. Please use 'Obs' and 'VarObs' instead")
Delaigue Olivier
committed
}
Obs <- Qobs
Delaigue Olivier
committed
# VarObs <- "Qobs"
Delaigue Olivier
committed
} else {
warning("argument 'Qobs' is deprecated. The values set in 'Obs' will be used instead")
Delaigue Olivier
committed
}
if (!missing(Ind_zeroes) & warnings) {
Delaigue Olivier
committed
warning("deprecated 'Ind_zeroes' argument")
Delaigue Olivier
committed
}
if (!missing(verbose)) {
Delaigue Olivier
committed
warning("deprecated 'verbose' argument. Use 'warnings', instead")
Delaigue Olivier
committed
}
Delaigue Olivier
committed
## check 'InputsModel'
if (!inherits(InputsModel, "InputsModel")) {
stop("'InputsModel' must be of class 'InputsModel'")
Delaigue Olivier
committed
}
## length of index of period to be used for the model run
LLL <- length(InputsModel$DatesR[RunOptions$IndPeriod_Run])
## check 'Obs' and definition of idLayer
vecObs <- unlist(Obs)
Delaigue Olivier
committed
if (length(vecObs) %% LLL != 0 | !is.numeric(vecObs)) {
stop(sprintf("'Obs' must be a (list of) vector(s) of numeric values of length %i", LLL), call. = FALSE)
Delaigue Olivier
committed
}
if (!is.list(Obs)) {
Delaigue Olivier
committed
idLayer <- list(1L)
Obs <- list(Obs)
Delaigue Olivier
committed
} else {
idLayer <- lapply(Obs, function(i) {
if (is.list(i)) {
length(i)
} else {
1L
}
})
Obs <- lapply(Obs, function(x) rowMeans(as.data.frame(x)))
Delaigue Olivier
committed
}
Delaigue Olivier
committed
Delaigue Olivier
committed
## create list of arguments
listArgs <- list(FUN_CRIT = FUN_CRIT,
Obs = Obs,
Delaigue Olivier
committed
VarObs = VarObs,
Delaigue Olivier
committed
BoolCrit = BoolCrit,
Delaigue Olivier
committed
idLayer = idLayer,
transfo = as.character(transfo),
Delaigue Olivier
committed
Weights = Weights,
Delaigue Olivier
committed
epsilon = epsilon)
## check lists lengths
for (iArgs in names(listArgs)) {
Delaigue Olivier
committed
if (iArgs %in% c("Weights", "BoolCrit", "epsilon")) {
Delaigue Olivier
committed
if (any(is.null(listArgs[[iArgs]]))) {
listArgs[[iArgs]] <- lapply(seq_along(listArgs$FUN_CRIT), function(x) NULL)
}
Delaigue Olivier
committed
if (iArgs %in% c("FUN_CRIT", "VarObs", "transfo", "Weights") & length(listArgs[[iArgs]]) > 1L) {
Delaigue Olivier
committed
listArgs[[iArgs]] <- as.list(listArgs[[iArgs]])
}
Delaigue Olivier
committed
if (!is.list(listArgs[[iArgs]])) {
listArgs[[iArgs]] <- list(listArgs[[iArgs]])
Delaigue Olivier
committed
}
Delaigue Olivier
committed
## check 'FUN_CRIT'
listArgs$FUN_CRIT <- lapply(listArgs$FUN_CRIT, FUN = match.fun)
Delaigue Olivier
committed
## check 'VarObs'
if (missing(VarObs)) {
listArgs$VarObs <- as.list(rep("Q", times = length(listArgs$Obs)))
Delaigue Olivier
committed
# if (warnings) {
Delaigue Olivier
committed
# warning("'VarObs' automatically set to \"Q\"")
Delaigue Olivier
committed
# }
Delaigue Olivier
committed
}
Delaigue Olivier
committed
Delaigue Olivier
committed
## check 'VarObs' + 'RunOptions'
if ("Q" %in% VarObs & !inherits(RunOptions, "GR")) {
stop("'VarObs' cannot contain Q if a GR rainfall-runoff model is not used")
Delaigue Olivier
committed
}
Delaigue Olivier
committed
if (any(c("SCA", "SWE") %in% VarObs) & !inherits(RunOptions, "CemaNeige")) {
stop("'VarObs' cannot contain SCA or SWE if CemaNeige is not used")
Delaigue Olivier
committed
}
Delaigue Olivier
committed
if ("SCA" %in% VarObs & inherits(RunOptions, "CemaNeige") & !"Gratio" %in% RunOptions$Outputs_Sim) {
Delaigue Olivier
committed
stop("'Gratio' is missing in 'Outputs_Sim' of 'RunOptions', which is necessary to output SCA with CemaNeige")
}
Delaigue Olivier
committed
if ("SWE" %in% VarObs & inherits(RunOptions, "CemaNeige") & !"SnowPack" %in% RunOptions$Outputs_Sim) {
Delaigue Olivier
committed
stop("'SnowPack' is missing in 'Outputs_Sim' of 'RunOptions', which is necessary to output SWE with CemaNeige")
}
Delaigue Olivier
committed
Delaigue Olivier
committed
## check 'transfo'
if (missing(transfo)) {
listArgs$transfo <- as.list(rep("", times = length(listArgs$Obs)))
Delaigue Olivier
committed
# if (warnings) {
# warning("'transfo' automatically set to \"\"")
# }
Delaigue Olivier
committed
}
## check length of each args
if (length(unique(sapply(listArgs, FUN = length))) != 1) {
stopListArgs <- paste(sapply(names(listArgs), shQuote), collapse = ", ")
stop(sprintf("arguments %s must have the same length", stopListArgs))
}
## check 'RunOptions'
if (!inherits(RunOptions , "RunOptions")) {
stop("'RunOptions' must be of class 'RunOptions'")
Delaigue Olivier
committed
}
Delaigue Olivier
committed
Delaigue Olivier
committed
## check 'Weights'
if (length(listArgs$Weights) > 1 & sum(unlist(listArgs$Weights)) == 0 & !any(sapply(listArgs$Weights, is.null))) {
stop("sum of 'Weights' cannot be equal to zero")
Delaigue Olivier
committed
}
Delaigue Olivier
committed
Delaigue Olivier
committed
## ---------- reformat
Delaigue Olivier
committed
## reformat list of arguments
listArgs2 <- lapply(seq_along(listArgs$FUN_CRIT), function(i) lapply(listArgs, "[[", i))
## preparation of warning messages
Delaigue Olivier
committed
inVarObs <- c("Q", "SCA", "SWE")
Delaigue Olivier
committed
msgVarObs <- "'VarObs' must be a (list of) character vector(s) and one of %s"
Delaigue Olivier
committed
msgVarObs <- sprintf(msgVarObs, paste(sapply(inVarObs, shQuote), collapse = ", "))
Delaigue Olivier
committed
inTransfo <- c("", "sqrt", "log", "inv", "sort", "boxcox") # pow is not checked by inTransfo, but appears in the warning message and checkef after (see ## check 'transfo')
msgTransfo <- "'transfo' must be a (list of) character vector(s) and one of %s, or numeric value for power transformation"
Delaigue Olivier
committed
msgTransfo <- sprintf(msgTransfo, paste(sapply(inTransfo, shQuote), collapse = ", "))
Delaigue Olivier
committed
## ---------- loop on the list of inputs
InputsCrit <- lapply(listArgs2, function(iListArgs2) {
## check 'FUN_CRIT'
if (!(identical(iListArgs2$FUN_CRIT, ErrorCrit_NSE ) | identical(iListArgs2$FUN_CRIT, ErrorCrit_KGE ) |
identical(iListArgs2$FUN_CRIT, ErrorCrit_KGE2) | identical(iListArgs2$FUN_CRIT, ErrorCrit_RMSE))) {
stop("incorrect 'FUN_CRIT' for use in 'CreateInputsCrit'", call. = FALSE)
Delaigue Olivier
committed
if (identical(iListArgs2$FUN_CRIT, ErrorCrit_RMSE) & length(listArgs$Weights) > 1 & all(!is.null(unlist(listArgs$Weights)))) {
stop("calculating a composite criterion with the RMSE is not allowed since RMSE is not a dimensionless metric", call. = FALSE)
Delaigue Olivier
committed
## check 'Obs'
if (!is.vector(iListArgs2$Obs) | length(iListArgs2$Obs) != LLL | !is.numeric(iListArgs2$Obs)) {
stop(sprintf("'Obs' must be a (list of) vector(s) of numeric values of length %i", LLL), call. = FALSE)
Delaigue Olivier
committed
}
Delaigue Olivier
committed
## check 'BoolCrit'
if (is.null(iListArgs2$BoolCrit)) {
iListArgs2$BoolCrit <- rep(TRUE, length(iListArgs2$Obs))
Delaigue Olivier
committed
if (!is.logical(iListArgs2$BoolCrit)) {
stop("'BoolCrit' must be a (list of) vector(s) of boolean", call. = FALSE)
Delaigue Olivier
committed
if (length(iListArgs2$BoolCrit) != LLL) {
stop("'BoolCrit' and 'InputsModel' series must have the same length", call. = FALSE)
Delaigue Olivier
committed
Delaigue Olivier
committed
## check 'VarObs'
if (!is.vector(iListArgs2$VarObs) | length(iListArgs2$VarObs) != 1 | !is.character(iListArgs2$VarObs) | !all(iListArgs2$VarObs %in% inVarObs)) {
Delaigue Olivier
committed
stop(msgVarObs, call. = FALSE)
Delaigue Olivier
committed
Delaigue Olivier
committed
## check 'VarObs' + 'Obs'
if (any(iListArgs2$VarObs %in% "SCA")) {
idSCA <- which(iListArgs2$VarObs == "SCA")
Delaigue Olivier
committed
if (length(idSCA) == 1L) {
vecSCA <- iListArgs2$Obs
Delaigue Olivier
committed
} else {
vecSCA <- unlist(iListArgs2$Obs[idSCA])
Delaigue Olivier
committed
}
Delaigue Olivier
committed
if (min(vecSCA, na.rm = TRUE) < 0 | max(vecSCA, na.rm = TRUE) > 1) {
stop("'Obs' outside [0,1] for \"SCA\"", call. = FALSE)
Delaigue Olivier
committed
}
}
Delaigue Olivier
committed
inPosVarObs <- c("Q", "SWE")
Delaigue Olivier
committed
if (any(iListArgs2$VarObs %in% inPosVarObs)) {
idQSS <- which(iListArgs2$VarObs %in% inPosVarObs)
Delaigue Olivier
committed
if (length(idQSS) == 1L) {
vecQSS <- iListArgs2$Obs
Delaigue Olivier
committed
} else {
vecQSS <- unlist(iListArgs2$Obs[idQSS])
Delaigue Olivier
committed
}
if (all(is.na(vecQSS))) {
stop("'Obs' contains only missing values", call. = FALSE)
}
Delaigue Olivier
committed
if (min(vecQSS, na.rm = TRUE) < 0) {
Delaigue Olivier
committed
stop(sprintf("'Obs' outside [0,Inf[ for \"%s\"", iListArgs2$VarObs), call. = FALSE)
Delaigue Olivier
committed
}
}
Delaigue Olivier
committed
## check 'transfo'
if (is.null(iListArgs2$transfo) | !is.vector(iListArgs2$transfo) | length(iListArgs2$transfo) != 1 | !is.character(iListArgs2$transfo)) {
Delaigue Olivier
committed
stop(msgTransfo, call. = FALSE)
isNotInTransfo <- !(iListArgs2$transfo %in% inTransfo)
if (any(isNotInTransfo)) {
powTransfo <- iListArgs2$transfo[isNotInTransfo]
Delaigue Olivier
committed
powTransfo <- gsub("\\^|[[:alpha:]]", "", powTransfo)
numExpTransfo <- suppressWarnings(as.numeric(powTransfo))
if (any(is.na(numExpTransfo))) {
stop(msgTransfo, call. = FALSE)
}
iListArgs2$transfo <- paste0("^", iListArgs2$transfo)
}
Delaigue Olivier
committed
## check 'Weights'
if (!is.null(iListArgs2$Weights)) {
if (!is.vector(iListArgs2$Weights) | length(iListArgs2$Weights) != 1 | !is.numeric(iListArgs2$Weights) | any(iListArgs2$Weights < 0)) {
stop("'Weights' must be a single (list of) positive or equal to zero value(s)", call. = FALSE)
Delaigue Olivier
committed
## check 'epsilon'
if (!is.null(iListArgs2$epsilon)) {
if (!is.vector(iListArgs2$epsilon) | length(iListArgs2$epsilon) != 1 | !is.numeric(iListArgs2$epsilon) | any(iListArgs2$epsilon <= 0)) {
stop("'epsilon' must be a single (list of) positive value(s)", call. = FALSE)
Delaigue Olivier
committed
}
} else if (iListArgs2$transfo %in% c("log", "inv") & any(iListArgs2$Obs %in% 0) & warnings) {
warning("zeroes detected in Obs: the corresponding time-steps will be excluded by the 'ErrorCrit*' functions as the epsilon argument was set to NULL", call. = FALSE)
Delaigue Olivier
committed
}
Delaigue Olivier
committed
## check 'transfo' + 'FUN_CRIT'
if (iListArgs2$transfo == "log" & warnings) {
warn_log_kge <- "we do not advise using the %s with a log transformation on Obs (see the details section in the 'CreateInputsCrit' help)"
Delaigue Olivier
committed
if (identical(iListArgs2$FUN_CRIT, ErrorCrit_KGE)) {
warning(sprintf(warn_log_kge, "KGE"), call. = FALSE)
}
if (identical(iListArgs2$FUN_CRIT, ErrorCrit_KGE2)) {
warning(sprintf(warn_log_kge, "KGE'"), call. = FALSE)
}
}
Delaigue Olivier
committed
## Create InputsCrit
iInputsCrit <- list(FUN_CRIT = iListArgs2$FUN_CRIT,
Obs = iListArgs2$Obs,
Delaigue Olivier
committed
VarObs = iListArgs2$VarObs,
Delaigue Olivier
committed
BoolCrit = iListArgs2$BoolCrit,
Delaigue Olivier
committed
idLayer = iListArgs2$idLayer,
Delaigue Olivier
committed
transfo = iListArgs2$transfo,
epsilon = iListArgs2$epsilon,
Delaigue Olivier
committed
Weights = iListArgs2$Weights)
Delaigue Olivier
committed
class(iInputsCrit) <- c("Single", "InputsCrit", ObjectClass)
return(iInputsCrit)
Delaigue Olivier
committed
})
names(InputsCrit) <- paste0("IC", seq_along(InputsCrit))
## define FUN_CRIT as a characater string
listErrorCrit <- c("ErrorCrit_KGE", "ErrorCrit_KGE2", "ErrorCrit_NSE", "ErrorCrit_RMSE")
InputsCrit <- lapply(InputsCrit, function(i) {
i$FUN_CRIT <- listErrorCrit[sapply(listErrorCrit, function(j) identical(i$FUN_CRIT, get(j)))]
i
})
Delaigue Olivier
committed
Delaigue Olivier
committed
listVarObs <- sapply(InputsCrit, FUN = "[[", "VarObs")
Delaigue Olivier
committed
inCnVarObs <- c("SCA", "SWE")
if (!"ZLayers" %in% names(InputsModel)) {
if(any(listVarObs %in% inCnVarObs)) {
Delaigue Olivier
committed
stop(sprintf("'VarObs' can not be equal to %i if CemaNeige is not used",
Delaigue Olivier
committed
paste(sapply(inCnVarObs, shQuote), collapse = " or ")))
}
} else {
listGroupLayer0 <- sapply(InputsCrit, FUN = "[[", "idLayer")
listGroupLayer <- rep(listVarObs, times = listGroupLayer0)
tabGroupLayer <- as.data.frame(table(listGroupLayer))
Delaigue Olivier
committed
colnames(tabGroupLayer) <- c("VarObs", "freq")
Delaigue Olivier
committed
nLayers <- length(InputsModel$ZLayers)
for (iInCnVarObs in inCnVarObs) {
if (any(listVarObs %in% iInCnVarObs)) {
Delaigue Olivier
committed
if (tabGroupLayer[tabGroupLayer$VarObs %in% iInCnVarObs, "freq"] != nLayers) {
stop(sprintf("'Obs' must contain %i vector(s) about %s", nLayers, iInCnVarObs))
Delaigue Olivier
committed
}
}
}
}
## define idLayer as an index of the layer to use
for (iInCnVarObs in unique(listVarObs)) {
if (iInCnVarObs == "Q") {
for (i in which(listVarObs == iInCnVarObs)) {
InputsCrit[[i]]$idLayer <- NA
}
} else {
aa <- listGroupLayer0[listVarObs == iInCnVarObs]
aa <- unname(aa)
bb <- cumsum(c(0, aa[-length(aa)]))
Delaigue Olivier
committed
cc <- lapply(seq_along(aa), function(x) seq_len(aa[x]) + bb[x])
k <- 1
for (i in which(listVarObs == iInCnVarObs)) {
InputsCrit[[i]]$idLayer <- cc[[k]]
k <- k + 1
}
}
}
Delaigue Olivier
committed
## if only one criterion --> not a list of InputsCrit but directly an InputsCrit
Delaigue Olivier
committed
if (length(InputsCrit) < 2) {
InputsCrit <- InputsCrit[[1L]]
Delaigue Olivier
committed
InputsCrit["Weights"] <- list(Weights = NULL)
Delaigue Olivier
committed
} else {
Delaigue Olivier
committed
if (any(sapply(listArgs$Weights, is.null))) {
Delaigue Olivier
committed
for (iListArgs in InputsCrit) {
Delaigue Olivier
committed
iListArgs$Weights <- NULL
Delaigue Olivier
committed
}
class(InputsCrit) <- c("Multi", "InputsCrit", ObjectClass)
} else {
class(InputsCrit) <- c("Compo", "InputsCrit", ObjectClass)
}
combInputsCrit <- combn(x = length(InputsCrit), m = 2)
apply(combInputsCrit, MARGIN = 2, function(i) {
equalInputsCrit <- identical(InputsCrit[[i[1]]], InputsCrit[[i[2]]])
if(equalInputsCrit) {
Delaigue Olivier
committed
warning(sprintf("elements %i and %i of the criteria list are identical. This might not be necessary", i[1], i[2]), call. = FALSE)
Delaigue Olivier
committed
}
})
Delaigue Olivier
committed
return(InputsCrit)
}