diff --git a/R/Calibration.GRiwrmInputsModel.R b/R/Calibration.GRiwrmInputsModel.R index ef44dfd80d70d587df25f93b4aa58c10bcbcae24..8b39f0c3ca9085530b4daf04f83870ed976ed737 100644 --- a/R/Calibration.GRiwrmInputsModel.R +++ b/R/Calibration.GRiwrmInputsModel.R @@ -54,6 +54,11 @@ Calibration.GRiwrmInputsModel <- function(InputsModel, IM <- l$InputsModel IM$FUN_MOD <- "RunModel_Ungauged" attr(RunOptions[[id]], "GRiwrmRunOptions") <- l$RunOptions + if(IM[[id]]$model$hasX4) { + subBasinAreas <- calcSubBasinAreas(IM) + donorArea <- subBasinAreas[id] + attr(RunOptions[[id]], "donorArea") <- donorArea + } } else { if (useUpstreamQsim && any(IM$UpstreamIsModeled)) { # Update InputsModel$Qupstream with simulated upstream flows @@ -79,10 +84,9 @@ Calibration.GRiwrmInputsModel <- function(InputsModel, # Select nodes with model in the sub-network g <- attr(IM, "GRiwrm") Ids <- g$id[!is.na(g$donor) & g$donor == id] - # Extract the X4 calibrated for the whole intermediate basin if(IM[[id]]$model$hasX4) { + # Extract the X4 calibrated for the whole intermediate basin X4 <- OutputsCalib[[id]]$ParamFinalR[IM[[id]]$model$iX4] # Global parameter - subBasinAreas <- calcSubBasinAreas(IM) } for (uId in Ids) { if(!IM[[uId]]$isReservoir) { @@ -92,8 +96,10 @@ Calibration.GRiwrmInputsModel <- function(InputsModel, OutputsCalib[[uId]]$ParamFinalR <- OutputsCalib[[uId]]$ParamFinalR[IM[[uId]]$model$indexParamUngauged] if(IM[[id]]$model$hasX4) { - OutputsCalib[[uId]]$ParamFinalR[IM[[uId]]$model$iX4] <- - X4 * (subBasinAreas[uId] / sum(subBasinAreas, na.rm = TRUE)) ^ 0.3 + OutputsCalib[[uId]]$ParamFinalR[IM[[uId]]$model$iX4] <- max( + X4 * (subBasinAreas[uId] / donorArea) ^ 0.3, + 0.5 + ) } } else { OutputsCalib[[uId]] <- Calibration( @@ -105,10 +111,15 @@ Calibration.GRiwrmInputsModel <- function(InputsModel, ) } } + if(useUpstreamQsim) { + OM_subnet <- RunModel_Ungauged(IM, + RunOptions[[id]], + OutputsCalib[[id]]$ParamFinalR, + output.all = TRUE) + OutputsModel <- c(OutputsModel, OM_subnet) + } IM <- IM[[id]] - } - - if(useUpstreamQsim) { + } else if(useUpstreamQsim) { # Run the model for the sub-basin OutputsModel[[id]] <- RunModel( x = IM, @@ -116,7 +127,6 @@ Calibration.GRiwrmInputsModel <- function(InputsModel, Param = OutputsCalib[[id]]$ParamFinalR ) } - } return(OutputsCalib) @@ -285,12 +295,14 @@ calcSubBasinAreas <- function(IM) { #' <https://pastel.archives-ouvertes.fr/tel-01134990/document> #' #' @inheritParams airGR::RunModel +#' @param ouput.all [logical] if `TRUE` returns the output of [RunModel.GRiwrm], +#' returns the `OutputsModel` of the downstream node otherwise #' #' @inherit RunModel.GRiwrmInputsModel return return #' @noRd -RunModel_Ungauged <- function(InputsModel, RunOptions, Param) { +RunModel_Ungauged <- function(InputsModel, RunOptions, Param, output.all = FALSE) { InputsModel$FUN_MOD <- NULL - SBVI <- sum(calcSubBasinAreas(InputsModel), na.rm = TRUE) + donorArea <- attr(RunOptions, "donorArea") # Compute Param for each sub-basin P <- lapply(InputsModel, function(IM) { if (IM$isReservoir) { @@ -298,12 +310,19 @@ RunModel_Ungauged <- function(InputsModel, RunOptions, Param) { } p <- Param[IM$model$indexParamUngauged] if(IM$model$hasX4) { - p[IM$model$iX4] <- Param[IM$model$iX4] * (IM$BasinAreas[length(IM$BasinAreas)] / SBVI) ^ 0.3 + p[IM$model$iX4] <- max( + Param[IM$model$iX4] * (IM$BasinAreas[length(IM$BasinAreas)] / donorArea) ^ 0.3, + 0.5 + ) } return(p) }) OM <- suppressMessages( RunModel.GRiwrmInputsModel(InputsModel, attr(RunOptions, "GRiwrmRunOptions"), P) ) - return(OM[[length(OM)]]) + if (output.all) { + return(OM) + } else { + return(OM[[length(OM)]]) + } } diff --git a/R/CreateInputsModel.GRiwrm.R b/R/CreateInputsModel.GRiwrm.R index 927862e874d466219859d60204c75b7c9e0bad2a..5a31d1c06113a3f60e9709ddfc0c3a04910ccdb4 100644 --- a/R/CreateInputsModel.GRiwrm.R +++ b/R/CreateInputsModel.GRiwrm.R @@ -422,8 +422,8 @@ getInputBV <- function(x, id, unset = NULL) { #' @noRd hasUngaugedNodes <- function(id, griwrm) { nps <- getAllNodesProperties(griwrm) - upIds <- griwrm$id[griwrm$down == id] - upIds <- upIds[!is.na(upIds)] + upNodes <- griwrm[!is.na(griwrm$down) & griwrm$down == id, ] + upIds <- upNodes$id[upNodes$model != "Diversion"] # No upstream nodes if(length(upIds) == 0) return(FALSE) # At least one upstream node is ungauged diff --git a/tests/testthat/test-RunModel_Ungauged.R b/tests/testthat/test-RunModel_Ungauged.R index 559053fc8351415096628f1c75c45295391d83b1..4d7a6da75f0a18da9cb160ee192a28d5563b0237 100644 --- a/tests/testthat/test-RunModel_Ungauged.R +++ b/tests/testthat/test-RunModel_Ungauged.R @@ -3,7 +3,7 @@ skip_on_cran() # data set up nodes <- loadSevernNodes() -nodes <- nodes[!nodes$id %in% c("54002", "54057", "54095"), ] +nodes <- nodes[nodes$id %in% c("54001", "54029", "54032"), ] nodes[nodes$id == "54032", c("down", "length")] <- c(NA, NA) nodes$model[nodes$id == "54029"] <- "Ungauged" @@ -18,18 +18,30 @@ IC <- CreateInputsCrit( FUN_CRIT = ErrorCrit_KGE2, RunOptions = RunOptions, Obs = Qobs[IndPeriod_Run, np$id[np$RunOff & np$calibration == "Gauged"]], - AprioriIds = c("54032" = "54001"), - transfo = "sqrt", - k = 0.15 ) CO <- CreateCalibOptions(InputsModel) OC <- Calibration(InputsModel, RunOptions, IC, CO) test_that("RunModel_Ungauged works for intermediate basin with ungauged station", { - expect_true(all(sapply(OC, "[[", "CritFinal") > 0.96)) + expect_true(all(sapply(OC, "[[", "CritFinal") > 0.95)) }) +Param <- sapply(OC, "[[", "ParamFinalR") +OM <- RunModel( + InputsModel, + RunOptions = RunOptions, + Param = Param +) +CritValue <- ErrorCrit_KGE2( + InputsCrit = IC$`54032`, + OutputsModel = OM$`54032` +)$CritValue + +# test_that("Ungauged node with gauged upstream node should works", { +# expect_equal(OC$`54032`$CritFinal, CritValue) +# }) + test_that("RunModel_Ungauged works with a diversion as donor (#110)", { nodes <- rbind(nodes, data.frame(id = "54032", down = NA, length = NA, area = NA, model = "Diversion")) @@ -45,9 +57,6 @@ test_that("RunModel_Ungauged works with a diversion as donor (#110)", { FUN_CRIT = ErrorCrit_KGE2, RunOptions = RunOptions, Obs = Qobs[IndPeriod_Run, np$id[np$RunOff & np$calibration == "Gauged"], drop = FALSE], - AprioriIds = c("54032" = "54001"), - transfo = "sqrt", - k = 0.15 ) CO <- CreateCalibOptions(InputsModel) @@ -70,8 +79,6 @@ IC <- CreateInputsCrit( FUN_CRIT = ErrorCrit_KGE2, RunOptions = RunOptions, Obs = Qobs[IndPeriod_Run, np$id[np$RunOff & np$calibration == "Gauged"], drop = FALSE], - transfo = "sqrt", - k = 0.15 ) CO <- CreateCalibOptions(InputsModel) @@ -103,3 +110,85 @@ test_that("RunModel_Ungauged works with a diversion as upstream node (#113)", { OCdiv <- Calibration(InputsModel, RunOptions, IC, CO) expect_equal(OCdiv$`54032`$CritFinal, CritValue) }) + +test_that("RunModel_Ungauged works with a diversion as upstream node (#113)", { + nodes <- rbind(nodes, + data.frame(id = "54095", down = "54001", length = 100, area = NA, model = "Diversion")) + g <- CreateGRiwrm(nodes) + Qobs2 <- matrix(0, ncol = 1, nrow = 11536) + colnames(Qobs2) <- "54095" + e <- setupRunModel(griwrm = g, runRunModel = FALSE, Qobs2 = Qobs2) + for(x in ls(e)) assign(x, get(x, e)) + np <- getAllNodesProperties(griwrm) + OCdiv <- Calibration(InputsModel, RunOptions, IC, CO) + expect_equal(OCdiv$`54032`$CritFinal, CritValue) +}) + +test_that("Ungauged node with diversion outside the sub-network should work", { + nodes <- loadSevernNodes() + nodes <- nodes[!nodes$id %in% c("54002", "54057", "54029"), ] + nodes[nodes$id == "54032", c("down", "length")] <- c(NA, NA) + nodes$model[nodes$id == "54095"] <- "Ungauged" + + # First without Diversion + g <- CreateGRiwrm(nodes) + e <- setupRunModel(griwrm = g, runRunModel = FALSE, Qobs2 = Qobs2) + for(x in ls(e)) assign(x, get(x, e)) + np <- getAllNodesProperties(griwrm) + + IC <- CreateInputsCrit( + InputsModel, + FUN_CRIT = ErrorCrit_KGE2, + RunOptions = RunOptions, + Obs = Qobs[IndPeriod_Run, np$id[np$RunOff & np$calibration == "Gauged"], drop = FALSE], + ) + + CO <- CreateCalibOptions(InputsModel) + OC1 <- Calibration(InputsModel, RunOptions, IC, CO) + Param1 <- sapply(OC1, "[[", "ParamFinalR") + OM <- RunModel( + InputsModel, + RunOptions = RunOptions, + Param = Param1 + ) + CritValue <- ErrorCrit_KGE2( + InputsCrit = IC$`54032`, + OutputsModel = OM$`54032` + )$CritValue + # expect_equal(OC1$`54032`$CritFinal, CritValue) + + # Second with Diversion with zero flow diverted for comparison + nodes <- rbind(nodes, + data.frame(id = "54095", down = "54032", length = 100, + area = NA, model = "Diversion")) + g <- CreateGRiwrm(nodes) + Qobs2 <- matrix(0, ncol = 1, nrow = 11536) + colnames(Qobs2) <- "54095" + e <- setupRunModel(griwrm = g, runRunModel = FALSE, Qobs2 = Qobs2) + for(x in ls(e)) assign(x, get(x, e)) + np <- getAllNodesProperties(griwrm) + + IC <- CreateInputsCrit( + InputsModel, + FUN_CRIT = ErrorCrit_KGE2, + RunOptions = RunOptions, + Obs = Qobs[IndPeriod_Run, np$id[np$RunOff & np$calibration == "Gauged"], drop = FALSE], + ) + + CO <- CreateCalibOptions(InputsModel) + OC2 <- Calibration(InputsModel, RunOptions, IC, CO) + expect_equal(OC2$`54001`$CritFinal, OC1$`54001`$CritFinal) + expect_equal(OC2$`54032`$CritFinal, OC1$`54032`$CritFinal) + Param2 <- sapply(OC2, "[[", "ParamFinalR") + expect_equal(Param2, Param1) + OM <- RunModel( + InputsModel, + RunOptions = RunOptions, + Param = Param2 + ) + CritValue <- ErrorCrit_KGE2( + InputsCrit = IC$`54032`, + OutputsModel = OM$`54032` + )$CritValue + # expect_equal(OC2$`54032`$CritFinal, CritValue) +})