Commit 191d4c70 authored by Dorchies David's avatar Dorchies David
Browse files

feat(SD): Add parameter 'QupstrUnit'

Refs #110
Showing with 120 additions and 26 deletions
+120 -26
...@@ -5,6 +5,7 @@ CreateInputsModel <- function(FUN_MOD, ...@@ -5,6 +5,7 @@ CreateInputsModel <- function(FUN_MOD,
TempMean = NULL, TempMin = NULL, TempMax = NULL, TempMean = NULL, TempMin = NULL, TempMax = NULL,
ZInputs = NULL, HypsoData = NULL, NLayers = 5, ZInputs = NULL, HypsoData = NULL, NLayers = 5,
Qupstream = NULL, LengthHydro = NULL, BasinAreas = NULL, Qupstream = NULL, LengthHydro = NULL, BasinAreas = NULL,
QupstrUnit = "mm",
verbose = TRUE) { verbose = TRUE) {
...@@ -215,6 +216,9 @@ CreateInputsModel <- function(FUN_MOD, ...@@ -215,6 +216,9 @@ CreateInputsModel <- function(FUN_MOD,
if(any(LengthHydro > 1000)) { if(any(LengthHydro > 1000)) {
warning("The unit of 'LengthHydro' has changed from m to km in v1.7 of airGR: values superior to 1000 km seem unrealistic") warning("The unit of 'LengthHydro' has changed from m to km in v1.7 of airGR: values superior to 1000 km seem unrealistic")
} }
if (!(QupstrUnit %in% c("mm", "m3", "m3/s", "l/s", "L/s"))) {
stop("'QupstrUnit' must be one of these values: 'mm', 'm3', 'm3/s', 'L/s' or 'l/s'")
}
} }
##check_NA_values ##check_NA_values
...@@ -330,6 +334,15 @@ CreateInputsModel <- function(FUN_MOD, ...@@ -330,6 +334,15 @@ CreateInputsModel <- function(FUN_MOD,
ZLayers = RESULT$ZLayers)) ZLayers = RESULT$ZLayers))
} }
if ("SD" %in% ObjectClass) { if ("SD" %in% ObjectClass) {
# Qupstream is internally stored in m3/time step
if (QupstrUnit == "mm") {
iConvBasins <- which(!is.na(BasinAreas[seq.int(length(LengthHydro))]))
Qupstream[,iConvBasins] <- Qupstream[,iConvBasins] * BasinAreas[iConvBasins] * 1e3
} else if (QupstrUnit == "m3/s") {
Qupstream <- Qupstream * TimeStep
} else if (QupstrUnit %in% c("l/s", "L/s")) {
Qupstream <- Qupstream * TimeStep / 1e3
}
InputsModel <- c(InputsModel, list(Qupstream = Qupstream, InputsModel <- c(InputsModel, list(Qupstream = Qupstream,
LengthHydro = LengthHydro, LengthHydro = LengthHydro,
BasinAreas = BasinAreas)) BasinAreas = BasinAreas))
......
...@@ -44,7 +44,7 @@ RunModel_Lag <- function(InputsModel, RunOptions, Param) { ...@@ -44,7 +44,7 @@ RunModel_Lag <- function(InputsModel, RunOptions, Param) {
NbUpBasins <- length(InputsModel$LengthHydro) NbUpBasins <- length(InputsModel$LengthHydro)
LengthTs <- length(OutputsModel$QsimDown) LengthTs <- length(OutputsModel$QsimDown)
OutputsModel$Qsim <- OutputsModel$QsimDown * InputsModel$BasinAreas[length(InputsModel$BasinAreas)] * 1e3 OutputsModel$Qsim_m3 <- OutputsModel$QsimDown * InputsModel$BasinAreas[length(InputsModel$BasinAreas)] * 1e3
IniSD <- RunOptions$IniStates[grep("SD", names(RunOptions$IniStates))] IniSD <- RunOptions$IniStates[grep("SD", names(RunOptions$IniStates))]
if (length(IniSD) > 0) { if (length(IniSD) > 0) {
...@@ -74,15 +74,15 @@ RunModel_Lag <- function(InputsModel, RunOptions, Param) { ...@@ -74,15 +74,15 @@ RunModel_Lag <- function(InputsModel, RunOptions, Param) {
for (upstream_basin in seq_len(NbUpBasins)) { for (upstream_basin in seq_len(NbUpBasins)) {
Qupstream <- c(IniStates[[upstream_basin]], Qupstream <- c(IniStates[[upstream_basin]],
InputsModel$Qupstream[RunOptions$IndPeriod_Run, upstream_basin]) InputsModel$Qupstream[RunOptions$IndPeriod_Run, upstream_basin])
if (!is.na(InputsModel$BasinAreas[upstream_basin])) {
# Upstream flow with area needs to be converted to m3 by time step
Qupstream <- Qupstream * InputsModel$BasinAreas[upstream_basin] * 1e3
}
# message("Qupstream[", upstream_basin, "]: ", paste(Qupstream, collapse = ", ")) # message("Qupstream[", upstream_basin, "]: ", paste(Qupstream, collapse = ", "))
OutputsModel$Qsim <- OutputsModel$Qsim + OutputsModel$Qsim_m3 <- OutputsModel$Qsim_m3 +
Qupstream[2:(1 + LengthTs)] * HUTRANS[1, upstream_basin] + Qupstream[2:(1 + LengthTs)] * HUTRANS[1, upstream_basin] +
Qupstream[1:LengthTs] * HUTRANS[2, upstream_basin] Qupstream[1:LengthTs] * HUTRANS[2, upstream_basin]
} }
# Convert back Qsim to mm
OutputsModel$Qsim <- OutputsModel$Qsim_m3 / sum(InputsModel$BasinAreas, na.rm = TRUE) / 1e3
# message("Qsim: ", paste(OutputsModel$Qsim, collapse = ", "))
# Warning for negative flows or NAs only in extended outputs # Warning for negative flows or NAs only in extended outputs
if(length(RunOptions$Outputs_Sim) > 2) { if(length(RunOptions$Outputs_Sim) > 2) {
if (any(OutputsModel$Qsim[!is.na(OutputsModel$Qsim)] < 0)) { if (any(OutputsModel$Qsim[!is.na(OutputsModel$Qsim)] < 0)) {
...@@ -94,9 +94,6 @@ RunModel_Lag <- function(InputsModel, RunOptions, Param) { ...@@ -94,9 +94,6 @@ RunModel_Lag <- function(InputsModel, RunOptions, Param) {
warning(length(which(is.na(OutputsModel$Qsim))), " time steps with NA values") warning(length(which(is.na(OutputsModel$Qsim))), " time steps with NA values")
} }
} }
# Convert back Qsim to mm
OutputsModel$Qsim <- OutputsModel$Qsim / sum(InputsModel$BasinAreas, na.rm = TRUE) / 1e3
# message("Qsim: ", paste(OutputsModel$Qsim, collapse = ", "))
if ("StateEnd" %in% RunOptions$Outputs_Sim) { if ("StateEnd" %in% RunOptions$Outputs_Sim) {
OutputsModel$StateEnd$SD <- lapply(seq(NbUpBasins), function(x) { OutputsModel$StateEnd$SD <- lapply(seq(NbUpBasins), function(x) {
......
...@@ -19,7 +19,7 @@ CreateInputsModel(FUN_MOD, DatesR, Precip, PrecipScale = TRUE, PotEvap = NULL, ...@@ -19,7 +19,7 @@ CreateInputsModel(FUN_MOD, DatesR, Precip, PrecipScale = TRUE, PotEvap = NULL,
TempMean = NULL, TempMin = NULL, TempMax = NULL, TempMean = NULL, TempMin = NULL, TempMax = NULL,
ZInputs = NULL, HypsoData = NULL, NLayers = 5, ZInputs = NULL, HypsoData = NULL, NLayers = 5,
Qupstream = NULL, LengthHydro = NULL, BasinAreas = NULL, Qupstream = NULL, LengthHydro = NULL, BasinAreas = NULL,
verbose = TRUE) QupstrUnit = "mm", verbose = TRUE)
\method{[}{InputsModel}(x, i) \method{[}{InputsModel}(x, i)
} }
...@@ -50,12 +50,14 @@ CreateInputsModel(FUN_MOD, DatesR, Precip, PrecipScale = TRUE, PotEvap = NULL, ...@@ -50,12 +50,14 @@ CreateInputsModel(FUN_MOD, DatesR, Precip, PrecipScale = TRUE, PotEvap = NULL,
\item{verbose}{(optional) [boolean] boolean indicating if the function is run in verbose mode or not, default = \code{TRUE}} \item{verbose}{(optional) [boolean] boolean indicating if the function is run in verbose mode or not, default = \code{TRUE}}
\item{Qupstream}{(optional) [numerical matrix] time series of upstream flows (catchment average) [mm/time step or m3/time step, see details], required to create the SD model inputs . See details} \item{Qupstream}{(optional) [numerical matrix] time series of upstream flows (catchment average), its unit is defined by the \code{QupstrUnit} parameter, required to create the SD model inputs. See details}
\item{LengthHydro}{(optional) [numeric] real giving the distance between the downstream outlet and each upstream inlet of the sub-catchment [km], required to create the SD model inputs . See details} \item{LengthHydro}{(optional) [numeric] real giving the distance between the downstream outlet and each upstream inlet of the sub-catchment [km], required to create the SD model inputs . See details}
\item{BasinAreas}{(optional) [numeric] real giving the area of each upstream sub-catchment [km2] and the area of the downstream sub-catchment in the last item, required to create the SD model inputs . See details} \item{BasinAreas}{(optional) [numeric] real giving the area of each upstream sub-catchment [km2] and the area of the downstream sub-catchment in the last item, required to create the SD model inputs . See details}
\item{QupstrUnit}{(optional) [character] unit of the flow in the argument \code{Qupstream}, available units are: "mm" for mm/time-step (default), "m3" for m3/time-step, "m3/s" and "l/s". See details}
\item{x}{[InputsModel] object of class InputsModel} \item{x}{[InputsModel] object of class InputsModel}
\item{i}{[integer] of the indices to subset a time series or [character] names of the elements to extract} \item{i}{[integer] of the indices to subset a time series or [character] names of the elements to extract}
...@@ -80,9 +82,11 @@ Users wanting to use \code{FUN_MOD} functions that are not included in ...@@ -80,9 +82,11 @@ Users wanting to use \code{FUN_MOD} functions that are not included in
the package must create their own InputsModel object accordingly. \cr the package must create their own InputsModel object accordingly. \cr
Please note that if CemaNeige is used, and \code{ZInputs} is different than \code{HypsoData}, then precipitation and temperature are interpolated with the \code{DataAltiExtrapolation_Valery} function. Please note that if CemaNeige is used, and \code{ZInputs} is different than \code{HypsoData}, then precipitation and temperature are interpolated with the \code{DataAltiExtrapolation_Valery} function.
Users wanting to use a semi-distributed (SD) lag model should provide valid \code{Qupstream}, \code{LengthHydro}, and \code{BasinAreas} parameters. Each upstream sub-catchment is described by an upstream flow time series (one column in \code{Qupstream} matrix), a distance between the downstream outlet and the upstream inlet (one item in \code{LengthHydro}) and an area (one item in \code{BasinAreas}). Users wanting to use a semi-distributed (SD) model should provide valid \code{Qupstream}, \code{LengthHydro}, and \code{BasinAreas} arguments. Each upstream sub-catchment is described by an upstream flow time series (one column in \code{Qupstream} matrix), a distance between the downstream outlet and the upstream inlet (one item in \code{LengthHydro}) and an area (one item in \code{BasinAreas}).
The order of the columns or of the items should be consistent for all these parameters. \code{BasinAreas} should contain one extra information (stored in the last item) which is the area of the downstream sub-catchment. The order of the columns or of the items should be consistent for all these parameters.
Upstream flows that are not related to a sub-catchment such as a release or withdraw spot are identified by an area equal to \code{NA} and an upstream flow expressed in m3/time step instead of mm/time step. \code{BasinAreas} should contain one extra information (stored in the last item) which is the area of the downstream sub-catchment.
Upstream flows that are not related to a sub-catchment such as release or withdraw spots are identified by an area equal to \code{NA}, and if \code{unit="mm"} the upstream flow must be expressed in m3/time step instead of mm/time step which is not possible in absence of a related area.
Please note that the use of SD model requires to use the \code{\link{RunModel}} function instead of \code{\link{RunModel_GR4J}} or the other \code{RunModel_*} functions.
} }
\examples{ \examples{
......
...@@ -39,6 +39,22 @@ RunOptions <- suppressWarnings(CreateRunOptions(FUN_MOD = RunModel_GR4J, ...@@ -39,6 +39,22 @@ RunOptions <- suppressWarnings(CreateRunOptions(FUN_MOD = RunModel_GR4J,
InputsModel = InputsModel, InputsModel = InputsModel,
IndPeriod_Run = Ind_Run)) IndPeriod_Run = Ind_Run))
test_that("'QupstrUnit' must correspond to one possible value", {
expect_error(
InputsModel <- CreateInputsModel(
FUN_MOD = RunModel_GR4J,
DatesR = BasinObs$DatesR,
Precip = BasinObs$P,
PotEvap = BasinObs$E,
Qupstream = matrix(Qupstream, ncol = 1),
LengthHydro = 1,
BasinAreas = BasinAreas,
QupstrUnit = "m3/h"
),
regexp = "'QupstrUnit' must be one of these values: 'mm', 'm3', 'm3/s', 'L/s' or 'l/s'"
)
})
test_that("InputsModel parameter should contain an OutputsModel key", { test_that("InputsModel parameter should contain an OutputsModel key", {
expect_error( expect_error(
RunModel_Lag(InputsModel = InputsModel, RunOptions = RunOptions, Param = 1), RunModel_Lag(InputsModel = InputsModel, RunOptions = RunOptions, Param = 1),
...@@ -111,8 +127,15 @@ test_that("'Qupstream' contain NA values", { ...@@ -111,8 +127,15 @@ test_that("'Qupstream' contain NA values", {
}) })
test_that("Upstream basin with nil area should return same Qdown as GR4J alone", { test_that("Upstream basin with nil area should return same Qdown as GR4J alone", {
UpstBasinArea <- InputsModel$BasinAreas[1L] InputsModel <- CreateInputsModel(
InputsModel$BasinAreas[1L] <- 0 FUN_MOD = RunModel_GR4J,
DatesR = BasinObs$DatesR,
Precip = BasinObs$P,
PotEvap = BasinObs$E,
Qupstream = matrix(Qupstream, ncol = 1),
LengthHydro = 1,
BasinAreas = c(0,BasinAreas[2])
)
OutputsSD <- RunModel(InputsModel, OutputsSD <- RunModel(InputsModel,
RunOptions, RunOptions,
Param = c(1, Param), Param = c(1, Param),
...@@ -121,8 +144,15 @@ test_that("Upstream basin with nil area should return same Qdown as GR4J alone", ...@@ -121,8 +144,15 @@ test_that("Upstream basin with nil area should return same Qdown as GR4J alone",
}) })
test_that("Downstream basin with nil area and nul upstream length should return same Qdown as Qupstream alone", { test_that("Downstream basin with nil area and nul upstream length should return same Qdown as Qupstream alone", {
InputsModel$LengthHydro <- 0 InputsModel <- CreateInputsModel(
InputsModel$BasinAreas <- c(BasinInfo$BasinArea, 0) FUN_MOD = RunModel_GR4J,
DatesR = BasinObs$DatesR,
Precip = BasinObs$P,
PotEvap = BasinObs$E,
Qupstream = matrix(Qupstream, ncol = 1),
LengthHydro = 0,
BasinAreas = c(BasinInfo$BasinArea, 0)
)
OutputsSD <- RunModel(InputsModel, OutputsSD <- RunModel(InputsModel,
RunOptions, RunOptions,
Param = c(1, Param), Param = c(1, Param),
...@@ -132,22 +162,72 @@ test_that("Downstream basin with nil area and nul upstream length should return ...@@ -132,22 +162,72 @@ test_that("Downstream basin with nil area and nul upstream length should return
ParamSD <- c(InputsModel$LengthHydro * 1e3 / (24 * 60 * 60), Param) # Speed corresponding to one time step delay ParamSD <- c(InputsModel$LengthHydro * 1e3 / (24 * 60 * 60), Param) # Speed corresponding to one time step delay
QlsGR4Only <- OutputsGR4JOnly$Qsim * InputsModel$BasinAreas[2L] * 1e6 / 86400 Qm3GR4Only <- OutputsGR4JOnly$Qsim * InputsModel$BasinAreas[2L] * 1e3
test_that("1 input with lag of 1 time step delay out gives an output delayed of one time step", { test_that("1 input with lag of 1 time step delay out gives an output delayed of one time step", {
OutputsSD <- RunModel(InputsModel, RunOptions, Param = ParamSD, FUN_MOD = RunModel_GR4J) OutputsSD <- RunModel(InputsModel, RunOptions, Param = ParamSD, FUN_MOD = RunModel_GR4J)
QlsSdSim <- OutputsSD$Qsim * sum(InputsModel$BasinAreas) * 1e6 / 86400 Qm3SdSim <- OutputsSD$Qsim_m3
QlsUpstLagObs <- c(0, Qupstream[Ind_Run[1:(length(Ind_Run) - 1)]]) * InputsModel$BasinAreas[1L] * 1e6 / 86400 Qm3UpstLagObs <- c(0, Qupstream[Ind_Run[1:(length(Ind_Run) - 1)]]) * InputsModel$BasinAreas[1] * 1E3
expect_equal(QlsSdSim - QlsGR4Only, QlsUpstLagObs) expect_equal(Qm3SdSim - Qm3GR4Only, Qm3UpstLagObs)
}) })
test_that("1 input with lag of 0.5 time step delay out gives an output delayed of 0.5 time step", { test_that("1 input with lag of 0.5 time step delay out gives an output delayed of 0.5 time step", {
OutputsSD <- RunModel(InputsModel, RunOptions, OutputsSD <- RunModel(InputsModel, RunOptions,
Param = c(InputsModel$LengthHydro * 1e3 / (12 * 3600), Param), Param = c(InputsModel$LengthHydro * 1e3 / (12 * 3600), Param),
FUN_MOD = RunModel_GR4J) FUN_MOD = RunModel_GR4J)
QlsSdSim <- OutputsSD$Qsim * sum(InputsModel$BasinAreas) * 1e6 / 86400 Qm3SdSim <- OutputsSD$Qsim_m3
QlsUpstLagObs <- (Qupstream[Ind_Run] + c(0, Qupstream[Ind_Run[1:(length(Ind_Run) - 1)]]))/2 * InputsModel$BasinAreas[1L] * 1e6 / 86400 Qm3UpstLagObs <- (Qupstream[Ind_Run] + c(0, Qupstream[Ind_Run[1:(length(Ind_Run) - 1)]]))/2 * InputsModel$BasinAreas[1] * 1E3
expect_equal(QlsSdSim - QlsGR4Only, QlsUpstLagObs) expect_equal(Qm3SdSim - Qm3GR4Only, Qm3UpstLagObs)
})
test_that("Qupstream in different units should return the same result", {
OutputsSD_mm <- RunModel(InputsModel, RunOptions,
Param = ParamSD,
FUN_MOD = RunModel_GR4J)
InputsModel_m3 <- CreateInputsModel(
FUN_MOD = RunModel_GR4J,
DatesR = BasinObs$DatesR,
Precip = BasinObs$P,
PotEvap = BasinObs$E,
Qupstream = matrix(Qupstream, ncol = 1) * BasinAreas[1] * 1e3,
LengthHydro = 1,
BasinAreas = BasinAreas,
QupstrUnit = "m3"
)
OutputsSD_m3 <- RunModel(InputsModel_m3, RunOptions,
Param = ParamSD,
FUN_MOD = RunModel_GR4J)
expect_equal(OutputsSD_mm$Qsim, OutputsSD_m3$Qsim)
InputsModel_m3s <- CreateInputsModel(
FUN_MOD = RunModel_GR4J,
DatesR = BasinObs$DatesR,
Precip = BasinObs$P,
PotEvap = BasinObs$E,
Qupstream = matrix(Qupstream, ncol = 1) * BasinAreas[1] * 1e3 / 86400,
LengthHydro = 1,
BasinAreas = BasinAreas,
QupstrUnit = "m3/s"
)
OutputsSD_m3s <- RunModel(InputsModel_m3s, RunOptions,
Param = ParamSD,
FUN_MOD = RunModel_GR4J)
expect_equal(OutputsSD_mm$Qsim, OutputsSD_m3s$Qsim)
InputsModel_ls <- CreateInputsModel(
FUN_MOD = RunModel_GR4J,
DatesR = BasinObs$DatesR,
Precip = BasinObs$P,
PotEvap = BasinObs$E,
Qupstream = matrix(Qupstream, ncol = 1) * BasinAreas[1] * 1e6 / 86400,
LengthHydro = 1,
BasinAreas = BasinAreas,
QupstrUnit = "L/s"
)
OutputsSD_ls <- RunModel(InputsModel_ls, RunOptions,
Param = ParamSD,
FUN_MOD = RunModel_GR4J)
expect_equal(OutputsSD_mm$Qsim, OutputsSD_ls$Qsim)
}) })
test_that("Params from calibration with simulated data should be similar to initial params", { test_that("Params from calibration with simulated data should be similar to initial params", {
...@@ -186,7 +266,7 @@ test_that("1 no area input with lag of 1 time step delay out gives an output del ...@@ -186,7 +266,7 @@ test_that("1 no area input with lag of 1 time step delay out gives an output del
expect_false(any(is.na(OutputsSD$Qsim))) expect_false(any(is.na(OutputsSD$Qsim)))
Qm3SdSim <- OutputsSD$Qsim * sum(InputsModel$BasinAreas, na.rm = TRUE) * 1e3 Qm3SdSim <- OutputsSD$Qsim_m3
Qm3UpstLagObs <- c(0, InputsModel$Qupstream[Ind_Run[1:(length(Ind_Run) - 1)]]) Qm3UpstLagObs <- c(0, InputsModel$Qupstream[Ind_Run[1:(length(Ind_Run) - 1)]])
expect_equal(Qm3SdSim - Qm3GR4Only, Qm3UpstLagObs) expect_equal(Qm3SdSim - Qm3GR4Only, Qm3UpstLagObs)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment