tests/testthat/test-RunModel_Ungauged.R

skip_on_cran()

# data set up
test_that("RunModel_Ungauged should act as RunModel", {
  nodes <- loadSevernNodes()
  nodes <- nodes[nodes$id %in% c("54001", "54095"), ]
  nodes[nodes$id == "54001", c("down", "length")] <- c(NA, NA)
  nodes$model[nodes$id == "54095"] <- "Ungauged"
  g <- CreateGRiwrm(nodes)
  e <- setupRunModel(runRunModel = FALSE, griwrm = g)
  for (x in ls(e)) assign(x, get(x, e))

  Param <- ParamMichel["54001"]
  Param[["54095"]] <-
    ParamMichel[["54001"]][InputsModel[["54095"]]$model$indexParamUngauged]
  donorArea <- tail(InputsModel[["54001"]]$BasinAreas, 1)
  X4 <- Param[["54001"]][InputsModel[["54001"]]$model$iX4]
  Param[["54095"]][InputsModel[["54095"]]$model$iX4] <-
    max(
      X4 * (tail(InputsModel[["54095"]]$BasinAreas, 1) / donorArea) ^ 0.3,
      0.5
    )
  OM <- RunModel(InputsModel, RunOptions = RunOptions, Param = Param)
  attr(RunOptions[["54001"]], "GRiwrmRunOptions") <- RunOptions
  OMU <- RunModel_Ungauged(InputsModel,
                           RunOptions = RunOptions[["54001"]],
                           Param = Param[["54001"]],
                           output.all = TRUE)
  expect_equal(OMU, OM)
})

# data set up
nodes <- loadSevernNodes()

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"

e <- runCalibration(nodes)
for(x in ls(e)) assign(x, get(x, e))
OC <- OutputsCalib

test_that("RunModel_Ungauged works for intermediate basin with ungauged station", {
  expect_true(all(sapply(OC[-3], "[[", "CritFinal") > 0.95))
})

Param <- extractParam(OC)
OM <- RunModel(
  InputsModel,
  RunOptions = RunOptions,
  Param = Param
)
CritValue <- ErrorCrit_KGE2(
  InputsCrit = InputsCrit$`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"))
  Qinf <- matrix(0, ncol = 1, nrow = 11536)
  colnames(Qinf) <- "54032"
  e <- runCalibration(nodes, Qinf = Qinf)
  for (x in ls(e)) assign(x, get(x, e))
  OCdiv <- OutputsCalib
  expect_equal(OCdiv, OC)
})

# 3 nodes on one branch with ungauged node in the middle
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 == "54001"] <- "Ungauged"
e <- runCalibration(nodes)
for(x in ls(e)) assign(x, get(x, e))
np <- getAllNodesProperties(griwrm)

OC_ref <- OutputsCalib
ParamRef <- Param
OM <- RunModel(
  InputsModel,
  RunOptions = RunOptions,
  Param = ParamRef
)
CritValue <- ErrorCrit_KGE2(
  InputsCrit = InputsCrit$`54032`,
  OutputsModel = OM$`54032`
)$CritValue

test_that("Ungauged node with gauged upstream node should works", {
  expect_equal(OC$`54032`$CritFinal, CritValue, tolerance = 1E3)
})

test_that("RunModel_Ungauged works with a diversion as upstream node (#113)", {
  nodes <- rbind(nodes,
                 data.frame(id = "54095", down = "54032", length = 100, area = NA, model = "Diversion"))
  Qinf <- matrix(0, ncol = 1, nrow = 11536)
  colnames(Qinf) <- "54095"
  e <- runCalibration(nodes, Qinf = Qinf)
  for (x in ls(e)) assign(x, get(x, e))
  expect_equal(OutputsCalib$`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"))
  Qinf <- matrix(0, ncol = 1, nrow = 11536)
  colnames(Qinf) <- "54095"
  e <- runCalibration(nodes, Qinf = Qinf)
  for (x in ls(e)) assign(x, get(x, e))
  expect_equal(OutputsCalib$`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
  e <- runCalibration(nodes, Qinf = Qinf)
  for (x in ls(e)) assign(x, get(x, e))
  OC1 <- OutputsCalib
  Param1 <- Param
  OM1 <- RunModel(
    InputsModel,
    RunOptions = RunOptions,
    Param = Param1
  )
  sapply(c("54001", "54032"), function(id) {
    CritValue <- ErrorCrit_KGE2(
      InputsCrit = InputsCrit[[id]],
      OutputsModel = OM1[[id]]
    )$CritValue
    expect_equal(OC1[[id]]$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"))
  Qinf <- matrix(0, ncol = 1, nrow = 11536)
  colnames(Qinf) <- "54095"
  e <- runCalibration(nodes, Qinf = Qinf)
  for (x in ls(e)) assign(x, get(x, e))
  OC2 <- OutputsCalib
  expect_equal(OC2$`54001`$CritFinal, OC1$`54001`$CritFinal)
  expect_equal(OC2$`54032`$CritFinal, OC1$`54032`$CritFinal)
  Param2 <- Param
  expect_equal(Param2, Param1)
  OM2 <- RunModel(
    InputsModel,
    RunOptions = RunOptions,
    Param = Param2
  )
  sapply(c("54001", "54032"), function(id) {
    CritValue <- ErrorCrit_KGE2(
      InputsCrit = InputsCrit[[id]],
      OutputsModel = OM2[[id]]
    )$CritValue
    expect_equal(OC1[[id]]$CritFinal, CritValue)
  })
})

test_that("Ungauged node with upstream node with diversion should work", {
  nodes <- loadSevernNodes()
  nodes <- nodes[nodes$id %in% c("54095", "54001", "54032"), ]
  nodes[nodes$id == "54032", c("down", "length")] <- c(NA, NA)
  nodes$model[nodes$id == "54001"] <- "Ungauged"
  nodes$down[nodes$id == "54095"] <- "P"
  nodes$length[nodes$id == "54095"] <- 0
  nodes <- rbind(nodes,
                 data.frame(id = "P", down = "54001", length = 42, area = NA, model = "RunModel_Lag"),
                 data.frame(id = c("54095", "P", "54001", "54032"),
                            down = NA,
                            length = NA,
                            area = NA,
                            model = "Diversion"))
  g <- CreateGRiwrm(nodes)
  Qinf <- matrix(0, ncol = length(g$id[g$model == "Diversion"]), nrow = 11536)
  colnames(Qinf) <- g$id[g$model == "Diversion"]
  e <- setupRunModel(griwrm = g, runRunModel = FALSE, Qinf = Qinf)
  for (x in ls(e)) assign(x, get(x, e))

  ParamRef[["P"]] <- 1
  OM <- RunModel(InputsModel,
                 RunOptions = RunOptions,
                 Param = ParamRef)

  l <- updateParameters4Ungauged(GaugedId = "54032",
                                 InputsModel = InputsModel,
                                 RunOptions = RunOptions,
                                 CalibOptions = CO,
                                 OutputsModel = OM,
                                 useUpstreamQsim = TRUE)
  g_reduced <- attr(l$InputsModel, "GRiwrm")

  expect_true(!any(g_reduced$id == "P" & !is.na(g_reduced$model) & g_reduced$model == "Diversion"))
  expect_true(any(g_reduced$id == "54001" & !is.na(g_reduced$model) & g_reduced$model == "Diversion"))
  expect_true(any(g_reduced$id == "54032" & !is.na(g_reduced$model) & g_reduced$model == "Diversion"))

  np <- getAllNodesProperties(g)

  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)
  CO[["P"]]$FixedParam = 1
  OC_Lag <- Calibration(InputsModel, RunOptions, IC, CO)
  Param_Lag <- extractParam(OC_Lag)
  expect_equal(Param_Lag, ParamRef[names(Param_Lag)])
})

test_that("Donor node with diversion should work", {
  nodes <- rbind(nodes,
                 data.frame(id = "54032",
                            down = NA,
                            length = NA,
                            area = NA,
                            model = "Diversion"))
  g <- CreateGRiwrm(nodes)
  Qinf <- matrix(0, ncol = length(g$id[g$model == "Diversion"]), nrow = 11536)
  colnames(Qinf) <- g$id[g$model == "Diversion"]
  e <- runCalibration(nodes, Qinf = Qinf)
  for (x in ls(e)) assign(x, get(x, e))
  expect_equal(OC_ref$`54032`$CritFinal, OutputsCalib$`54032`$CritFinal, tolerance = 1E-3)
})

test_that("Cemaneige with hysteresis works",  {
  nodes <- loadSevernNodes()
  nodes <- nodes[nodes$id %in% c("54057", "54032", "54001"), ]
  nodes$model <- "RunModel_CemaNeigeGR4J"
  nodes$model[nodes$id != 54057] <- "Ungauged"
  griwrm <- CreateGRiwrm(nodes)

  # # The custom ErrorCrit function !!!
  ErrorCrit_KGE3 <- function(InputsCrit, OutputsModel, warnings = TRUE, verbose = TRUE) {
    OutputsCritQ <- suppressMessages(
      ErrorCrit_KGE2(InputsCrit, OutputsModel, warnings = TRUE, verbose = TRUE)
    )
    InputsCrit$Obs <- InputsCrit$SCA #a adapter
    OutputsModel$Qsim <- OutputsModel$CemaNeigeLayers[[1]]$Gratio #a adapter
    OutputsCritSCA <- suppressMessages(
      ErrorCrit_KGE2(InputsCrit, OutputsModel, warnings = TRUE, verbose = TRUE)
    )
    OutputsCritQ$CritValue <-
      (OutputsCritQ$CritValue + OutputsCritSCA$CritValue) / 2
    OutputsCritQ$CritName <- "(0.5 * KGE2[Q] + 0.5 * KGE2[SCA])"
    return(OutputsCritQ)
  }
  class(ErrorCrit_KGE3) <- c("FUN_CRIT", class(ErrorCrit_KGE3))

  e <- suppressWarnings(
    setupRunModel(griwrm = griwrm, runRunModel = FALSE, IsHyst = TRUE)
  )
  for (x in ls(e)) assign(x, get(x, e))

  expect_true(all(sapply(InputsModel, function(x) x$model$hasX4)))

  np <- getAllNodesProperties(griwrm)
  InputsCrit <- CreateInputsCrit(
    InputsModel,
    FUN_CRIT = ErrorCrit_KGE3,
    RunOptions = RunOptions,
    Obs = Qobs[IndPeriod_Run, np$id[np$RunOff & np$calibration == "Gauged"], drop = FALSE],
  )
  InputsCrit$`54057`$SCA <- runif(length(IndPeriod_Run)) # Fake SCA
  CalibOptions <- CreateCalibOptions(InputsModel)
  CO <- lapply(CalibOptions, function(x) {
    x$StartParamList <- matrix(
      c(0.605,  320.596,   -0.042,   37.991,    2.221,    0.705,    6.764,   85.000,    0.850),
      nrow = 1)
    x$StartParamDistrib <- NULL
    x
  })
  class(CO) <- class(CalibOptions)
  e <- suppressWarnings(
    runCalibration(nodes, InputsCrit = InputsCrit, CalibOptions = CO, IsHyst = TRUE)
  )
  for (x in ls(e)) assign(x, get(x, e))
  expect_equal(sapply(Param, length),
               c("54057" = 9, "54001" = 8, "54032" = 9))
})

test_that("Ungauged node with derivation to reservoir should work", {
  testDerivedUngauged(FALSE)
})

test_that("Ungauged node with donor by derivation through reservoir should work", {
  testDerivedUngauged(TRUE)
})

test_that("Diversion to an ungauged node should works", {
  nodes <- loadSevernNodes()
  nodes[nodes$id == "54001", c("down", "length")] <- c(NA, NA)
  nodes <- rbind(
    nodes,
    data.frame(
      id = "54001",
      down = "54032",
      length = 45,
      area = NA,
      model = "Diversion"
    )
  )
  nodes$model[nodes$id == "54032"] <- "Ungauged"
  Qinf <- Qinf_rsrvr + 1E9
  colnames(Qinf) <- "54001"
  e <- suppressWarnings(
    runCalibration(nodes, Qinf = Qinf)
  )
  for (x in ls(e)) assign(x, get(x, e))
  expect_true(OutputsCalib$`54057`$CritFinal > 0.4)
})

nupd <- loadSevernNodes()
nupd$donor[nupd$id == "54032"] <- "54001"
nupd$model[nupd$id == "54032"] <- "Ungauged"
e <- runCalibration(nupd)
for (x in ls(e)) assign(x, get(x, e))

test_that("Ungauged with donor at upstream works", {
  expect_true(OutputsCalib$`54057`$CritFinal > 0.96)
})

test_that("Ungauged with upstream donor without hydraulic routing parameters", {
  nupd$donor[nupd$id == "54032"] <- "54029"
  expect_error(runCalibration(nupd),
               regexp = "Missing parameters in transfer between nodes '54029' and '54032'")
  CO <- CreateCalibOptions(InputsModel,
                           FixedParam = list("54032" = c(1, rep(NA, 4))))
  e <- runCalibration(nupd, CalibOptions = CO)
  for (x in ls(e)) assign(x, get(x, e))
  expect_equal(OutputsCalib$`54032`$ParamFinalR[1:4],
               c(1 , OutputsCalib$`54029`$ParamFinalR[1:3]))
})

test_that("Ungauged upstream node with upstream donor works", {
  nup2up <- loadSevernNodes()
  nup2up$model[nup2up$id == "54095"] <- "Ungauged"
  nup2up$donor[nup2up$id == "54095"] <- "54029"
  e <- runCalibration(nup2up)
  for (x in ls(e)) assign(x, get(x, e))
  expect_equal(OutputsCalib$`54095`$ParamFinalR[1:3],
               OutputsCalib$`54029`$ParamFinalR[1:3])
})

test_that("Gauged node inside ungauged cluster must only work if parameters are fixed",  {
  ngiuc <- loadSevernNodes()
  ngiuc <- ngiuc[ngiuc$id %in% c("54001", "54032", "54057"),]
  ngiuc$model[ngiuc$id == "54032"] <- "RunModel_GR4J"
  ngiuc$donor <- as.character(NA)
  ngiuc$model[ngiuc$id == "54001"] <- "Ungauged"
  ngiuc$donor[ngiuc$id == "54001"] <- "54057"
  expect_warning(CreateGRiwrm(ngiuc),
                 regexp = "Node '54032' is included in the ungauged node cluster '54057'")
  e <- suppressWarnings(runCalibration(ngiuc, doCalibration = FALSE))
  for (x in ls(e)) assign(x, get(x, e))
  rm(e)
  expect_error(Calibration(InputsModel, RunOptions, InputsCrit, CalibOptions),
               regexp = "Node '54032' .* must have its parameters fixed")
  CalibOptions[["54032"]]$FixedParam <- ParamMichel[["54032"]]
  OC <- Calibration(InputsModel, RunOptions, InputsCrit, CalibOptions)
  expect_equal(OC[["54032"]]$ParamFinalR, ParamMichel[["54032"]])
})
inrae/airGRiwrm documentation built on Sept. 27, 2024, 6:08 p.m.