diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 27e2d6d6ac1b8f661d60c1aad82e01179cc4c9b8..cce8efc481615987e0ff35554a93e22f5f680a32 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ update_packages: - mkdir -p R_LIBS - Rscript -e 'if(!dir.exists("R_LIBS/remotes")) install.packages("remotes", lib = "R_LIBS")' - apt-get update && apt-get -y install libxml2-dev - - Rscript -e 'remotes::update_packages(c("dplyr", "rmarkdown", "readr", "lubridate", "zoo", "roxygen2"), lib = "R_LIBS")' + - Rscript -e 'remotes::update_packages(c("dplyr", "rmarkdown", "readr", "lubridate", "zoo", "roxygen2", "DiagrammeR"), lib = "R_LIBS")' - Rscript -e 'remotes::install_gitlab("HYCAR-Hydro/airgr@sd", host = "gitlab.irstea.fr", lib = "R_LIBS")' build: diff --git a/DESCRIPTION b/DESCRIPTION index b4f18fc58f7383911324d8099b49c44be93e4230..f14242f2d72079b8df5960a38eaad5d78156f1f4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: griwrm Title: GR based Integrated Water Resource Management -Version: 0.2.1 +Version: 0.3.0 Authors@R: person(given = "David", family = "Dorchies", @@ -21,6 +21,7 @@ Imports: Suggests: knitr, rmarkdown, - lattice + lattice, + DiagrammeR VignetteBuilder: knitr URL: https://gitlab.irstea.fr/in-wop/griwrm diff --git a/R/Calibration.GriwrmInputsModel.R b/R/Calibration.GriwrmInputsModel.R index 372e087a6ab4db133986d70d58196d9fadd33829..7463cbd91b0e5dfb857fc6b74c4c2f194730627a 100644 --- a/R/Calibration.GriwrmInputsModel.R +++ b/R/Calibration.GriwrmInputsModel.R @@ -29,7 +29,7 @@ Calibration.GriwrmInputsModel <- function(InputsModel, if(useUpstreamQsim) { # Update InputsModel$Qupstream with simulated upstream flows - IM <- UpdateQsimUpstream(IM, OutputsModel) + IM <- UpdateQsimUpstream(IM, RunOptions[[IM$id]]$IndPeriod_Run, OutputsModel) } OutputsCalib[[IM$id]] <- Calibration.InputsModel( diff --git a/R/CreateInputsModel.Griwrm.R b/R/CreateInputsModel.Griwrm.R index 8395318780f5da1737de66467bda4bb8a9145d6b..ac8f24ee8620d3ed05122202185306f3279b33f3 100644 --- a/R/CreateInputsModel.Griwrm.R +++ b/R/CreateInputsModel.Griwrm.R @@ -68,7 +68,7 @@ CreateOneGriwrmInputsModel <- function(id, griwrm, DatesR, Precip, PotEvap, Qobs LengthHydro <- griwrm$length[griwrm$id %in% UpstreamNodes] BasinAreas <- c( griwrm$area[griwrm$id %in% UpstreamNodes], - node$area - sum(griwrm$area[griwrm$id %in% UpstreamNodes]) + node$area - sum(griwrm$area[griwrm$id %in% UpstreamNodes], na.rm = TRUE) ) } diff --git a/R/DiagramGriwrm.R b/R/DiagramGriwrm.R new file mode 100644 index 0000000000000000000000000000000000000000..a285c0af0b3c2e2fe1182c1a48be4fcf9d5d3e9c --- /dev/null +++ b/R/DiagramGriwrm.R @@ -0,0 +1,42 @@ +#' Display a diagram representing the network structure of a Griwrm object +#' +#' @param griwrm the Griwrm object to display. +#' @param display if `TRUE` displays the diagram with `DiagrammeR::mermaid`, return the mermaid code otherwise. +#' @param orientation Orientation of the graph. "LR" by default. +#' +#' @details This function only works inside RStudio because the HTMLwidget produced by DiagrammeR +#' is not handled on some platforms +#' +#' @return Mermaid code of the diagram id display is `FALSE`. +#' +#' @export +#' +#' @examples +#' \dontrun{ +#' # Display diagram +#' DiagramGriwrm(griwrm) +#' # Is the same as +#' DiagrammeR::mermaid(DiagramGriwrm(griwrm, display = FALSE), width = "100%", height = "100%") +#' } +#' +DiagramGriwrm <- function(griwrm, display = TRUE, orientation = "LR") { + if(Sys.getenv("RSTUDIO") != "1") { + return() + } + g2 <- griwrm[!is.na(griwrm$down),] + nodes <- paste( + g2$id, + "-->|", + format(g2$length/1000, trim = TRUE, digits = 0), + "km|", + g2$down + ) + styleSD <- paste("style", unique(g2$down), "fill:#cfc") + styleDF <- paste("style", unique(g2$id[is.na(g2$model)]), "fill:#fcc") + diagram <- paste(c(paste("graph", orientation), nodes, styleSD, styleDF), collapse = "\n") + if(display) { + DiagrammeR::mermaid(diagram, width = "100%", height = "100%") + } else { + return(diagram) + } +} diff --git a/R/Griwrm.R b/R/Griwrm.R index 28665823ee3adacefdc62f73a755d20a029eed49..ab3e13cb619d0584b1c3e4d987eaedcf0b4ed52e 100644 --- a/R/Griwrm.R +++ b/R/Griwrm.R @@ -28,6 +28,8 @@ getNodeRanking <- function(griwrm) { if(!is(griwrm, "Griwrm")) { stop("getNodeRanking: griwrm argument should be of class Griwrm") } + # Remove nodes without model (direct flow connections treated as upstream flows only) + griwrm <- griwrm[!is.na(griwrm$model),] # Rank 1 rank <- setdiff(griwrm$id, griwrm$down) ranking <- rank diff --git a/R/RunModel.GriwrmInputsModel.R b/R/RunModel.GriwrmInputsModel.R index 6b7e2894157f99b70793ce3fbbdb9072d2e973b7..ca6ecef9a4d4f53a9e4e6cbbe7bdcbcf7aac36c2 100644 --- a/R/RunModel.GriwrmInputsModel.R +++ b/R/RunModel.GriwrmInputsModel.R @@ -17,7 +17,7 @@ RunModel.GriwrmInputsModel <- function(InputsModel, RunOptions, Param, verbose = if(verbose) cat("RunModel.GriwrmInputsModel: Treating sub-basin", IM$id, "...\n") # Update InputsModel$Qupstream with simulated upstream flows - IM <- UpdateQsimUpstream(IM, OutputsModel) + IM <- UpdateQsimUpstream(IM, RunOptions[[IM$id]]$IndPeriod_Run, OutputsModel) # Run the model for the sub-basin OutputsModel[[IM$id]] <- RunModel( diff --git a/R/UpdateQsimUpstream.R b/R/UpdateQsimUpstream.R index e1fd8e592e42e1deba274aab0481cf9fb7becc44..ce29edef083ca0ed320979490e2091c87b4e90e0 100644 --- a/R/UpdateQsimUpstream.R +++ b/R/UpdateQsimUpstream.R @@ -1,27 +1,17 @@ #' Update InputsModel$Qupstream with simulated upstream flows provided by GriwrmOutputsModels object. #' #' @param InputsModel \emph{GriwrmInputsModel} object. See \code{[CreateInputsModel.Griwrm]}. +#' @param IndPeriod_Run numeric index of period to be used for the model run (-) #' @param OutputsModel \emph{GriwrmOutputsModel} object provided by \code{[RunModel.GriwrmInputsModel]}. #' #' @description This function is used by \code{\link{RunModel.GriwrmInputsModel}} and \code{\link{Calibration.GriwrmInputsModel}} in order to provide upstream simulated flows to a node. #' #' @return InputsModel object with updated QobsUpsr #' -UpdateQsimUpstream <- function(InputsModel, OutputsModel) { - if(length(InputsModel$UpstreamNodes) > 0) { - for(i in 1:length(InputsModel$UpstreamNodes)) { - Qupstream1 <- matrix( - c( - rep(0, length(RunOptions[[InputsModel$id]]$IndPeriod_WarmUp)), - OutputsModel[[InputsModel$UpstreamNodes[i]]]$Qsim - ), ncol = 1 - ) - if(i == 1) { - InputsModel$Qupstream <- Qupstream1 - } else { - InputsModel$Qupstream <- cbind(InputsModel$Qupstream, Qupstream1) - } - } +UpdateQsimUpstream <- function(InputsModel, IndPeriod_Run, OutputsModel) { + iQ <- which(!is.na(InputsModel$BasinAreas[1:length(InputsModel$LengthHydro)])) + for(i in iQ) { + InputsModel$Qupstream[IndPeriod_Run, i] <- OutputsModel[[InputsModel$UpstreamNodes[i]]]$Qsim } return(InputsModel) } diff --git a/README.md b/README.md index 86a314ed0c53305ada2383554ac9cba4777eadcf..d37b99baf2868116dd211329591788c85086dc38 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,27 @@ GR-IWRM is an extension of airGR for managing semi-distributive hydrological model on an anthropized catchment. -This package is developped as part of the IN-WOP project (http://www.waterjpi.eu/joint-calls/joint-call-2018-waterworks-2017/booklet/in-wop) by the mixed research unit G-EAU (https://g-eau.fr). +This package is developed as part of the IN-WOP project (http://www.waterjpi.eu/joint-calls/joint-call-2018-waterworks-2017/booklet/in-wop) by the mixed research unit G-EAU (https://g-eau.fr). ## Installation -Open a terminal and type: +### Requirements + +This package depends on airGR version 1.6. or more which is currently under development. To install it, use the following instruction: + +``` +remotes::install_gitlab("HYCAR-Hydro/airgr@sd", host = "gitlab.irstea.fr") +``` + +### Local installation + +Installation of the package from source should be done in three steps: + +- download sources +- run `roxygen` for generating `NAMESPACE` file and documentation +- install the package + +Open a terminal and type: ```shell git clone git@gitlab-ssh.irstea.fr:in-wop/griwrm.git @@ -18,4 +34,4 @@ R CMD INSTALL griwrm ## Get started -See the package vignettes. \ No newline at end of file +See the package vignettes for more information. diff --git a/inst/seine_data/config_reservoirs.json b/inst/seine_data/config_reservoirs.json new file mode 100644 index 0000000000000000000000000000000000000000..25c7e0917aa364593cf370c648a13575655696b6 --- /dev/null +++ b/inst/seine_data/config_reservoirs.json @@ -0,0 +1,54 @@ +{ + "Aube": { + "file": "Obs_Aube_1987-2008.txt", + "connections": { + "AUBE-_P2": { + "col": "Q_prise_m3s", + "type": "in" + }, + "AUBE-_R3": { + "col": "Q_restitution_total_m3s", + "type": "out" + } + } + }, + "Marne": { + "file": "Obs_Marne_1973-2008.txt", + "connections": { + "MARNE_P23": { + "col": "Q_prise_Marne_m3s", + "type": "in" + }, + "MARNE_P28": { + "col": "Q_prise_Blaise_m3s", + "type": "in" + }, + "MARNE_R25": { + "col": "Q_restitution_total_m3s", + "type": "out" + } + } + }, + "Panneciere": { + "file": "Obs_Panneciere_1949-2008.txt", + "connections": { + "PANNEC_R": { + "col": "Q_aval_bassin_m3s", + "type": "out" + } + } + }, + "Seine": { + "file": "Obs_Seine_1965-2008.txt", + "connections": { + "SEINE_P7": { + "col": "Q_prise_m3s", + "type": "in" + }, + "SEINE_R8": { + "col": "Q_restitution_total_m3s", + "type": "out" + } + } + } +} \ No newline at end of file diff --git a/inst/seine_data/network_reservoir_connections.txt b/inst/seine_data/network_reservoir_connections.txt new file mode 100644 index 0000000000000000000000000000000000000000..9b1c4338cc44154dab7de62f8e76763b15ada0d8 --- /dev/null +++ b/inst/seine_data/network_reservoir_connections.txt @@ -0,0 +1,10 @@ +id length down +AUBE-_P2 66500 ARCIS_24 +AUBE-_R3 44500 ARCIS_24 +MARNE_P23 3000 STDIZ_04 +MARNE_P28 82420 CHALO_21 +MARNE_R25 56870 CHALO_21 +PANNEC_P 153074 GURGY_02 +PANNEC_R 153074 GURGY_02 +SEINE_P7 73766 MERY-_22 +SEINE_R8 41766 MERY-_22 diff --git a/vignettes/V01_First_network.Rmd b/vignettes/V01_First_network.Rmd index a14ce2fb5512e0ddfb666aa0889dc17b929dbba1..e9615c6f542971ef6149a7fb15ffd6d8259fe13a 100644 --- a/vignettes/V01_First_network.Rmd +++ b/vignettes/V01_First_network.Rmd @@ -45,6 +45,13 @@ griwrm <- Griwrm(seine_nodes, list(id = "id_sgl", down = "id_aval", length = "di griwrm ``` +The diagram of the network structure is represented below with in blue the upstream nodes with a GR4J model and in green the intermediate nodes with an SD (GR4J + LAG) model. + +```{r fig.height = 7, fig.width = 8} +DiagramGriwrm(griwrm) +``` + + ## Observation time series @@ -109,8 +116,8 @@ InputsModel <- CreateInputsModel(griwrm, DatesR, Precip, PotEvap, Qobs) ## Save data for next vignettes ```{r} - dir.create("_cache", showWarnings = FALSE) save(griwrm, Qobs, InputsModel, file = "_cache/V01.RData") +save(griwrm, DatesR, Precip, PotEvap, Qobs, MergeTS, file = "_cache/V01b.RData") ``` diff --git a/vignettes/v02_First_run.Rmd b/vignettes/V02_First_run.Rmd similarity index 100% rename from vignettes/v02_First_run.Rmd rename to vignettes/V02_First_run.Rmd diff --git a/vignettes/V04_Open-loop_influenced_flow.Rmd b/vignettes/V04_Open-loop_influenced_flow.Rmd new file mode 100644 index 0000000000000000000000000000000000000000..9b2a396b2d19842eeeca3ab568e389d67522c1fc --- /dev/null +++ b/vignettes/V04_Open-loop_influenced_flow.Rmd @@ -0,0 +1,327 @@ +--- +title: "Running open-loop influenced semi-distributed flow model" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Running open-loop influenced semi-distributed flow model} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +```{r setup} +library(griwrm) +library(DiagrammeR) +``` + +## Integration of the reservoir connections into the model + +### Loading naturalised data + +```{r} +load("_cache/V01b.RData") +``` + +### Add connections to the reservoirs in the gauging station network + +Because of the airGR SD model structure, the topology of the network will difere between the physical one and the modelled one. + +For example, the physical topology of the Aube lake is represented below: + +```{r echo = FALSE, fig.height = 3} + +mmd <- function(x, ...) { + # For avoiding crash of R CMD build in console mode + if(Sys.getenv("RSTUDIO") == "1") { + mermaid(x, ...) + } +} + +mmd("graph LR + TRANN_01 -->|1.5km| AUBE_P2 + AUBE_P2 -->|22km| AUBE_R3 + AUBE_P2 --> AUBE(AUBE) + AUBE --> AUBE_R3 + AUBE_R3 -->|44.5km| ARCIS_24 +style AUBE_P2 fill:#fcc +style AUBE_R3 fill:#fcc +style AUBE fill:#ccf +", width = "100%") +``` + +In the SD model, we do not model intermediate flows as well as the reservoir in the catchment. As a result, an equivalent topology compatible with airGR will be the one below: + +```{r echo = FALSE} +mmd(" +graph LR +TRANN_01 -->|68km| ARCIS_24 +AUBE_P2 -->|66.5km| ARCIS_24 +AUBE_R3 -->|44.5km| ARCIS_24 +style AUBE_P2 fill:#fcc +style AUBE_R3 fill:#fcc +", width = "100%") +``` + +`AUBE_P2` will propagate negative flow to downstream (reservoir inflows) while `AUBE_R3` will propagate positive flow to downstream (reservoir releases). + +Configuration on lake Seine is similar: + +```{r echo = FALSE, fig.height = 3} +mmd(" +graph LR + BAR-S_06 -->|6km| SEINE_P7 + SEINE_P7 -->|32km| SEINE_R8 + SEINE_P7 --> SEINE(SEINE) + SEINE--> SEINE_R8 + SEINE_R8 -->|41.7km| MERY-_22 + style SEINE_P7 fill:#fcc + style SEINE_R8 fill:#fcc + style SEINE fill:#ccf +", width = "100%") +``` + +which can be translate as: + +```{r echo = FALSE} +mmd(" +graph LR + BAR-S_06 -->|79.7km| MERY-_22 + SEINE_P7 -->|73.7km| MERY-_22 + SEINE_R8 -->|41.7km| MERY-_22 + style SEINE_P7 fill:#fcc + style SEINE_R8 fill:#fcc +", width = "100%") +``` + +Pannecière is an inline reservoir: + +```{r echo = FALSE, fig.height = 2} +mmd(" +graph LR +P(PANNECIERE) +CHAUM_07 -->|0km| P +P --> |153km| GURGY_02 +style P fill:#ccf +", width = "100%") +``` + +It can be modelled as: + +```{r echo = FALSE} +mmd(" +graph LR +CHAUM_07 --> |153km| GURGY_02 +PANNEC_P --> |153km| GURGY_02 +PANNEC_R --> |153km| GURGY_02 +style PANNEC_P fill:#fcc +style PANNEC_R fill:#fcc +", width = "100%") +``` + +With $Q_{PANNEC\_P} = - Q_{CHAUM\_07}$ as all the upstream flow fills the reservoir. + +Marne lake can be mapped as: + +```{r echo = FALSE} +mmd(" +graph LR +LOUVE_19 -->|0.5km| MARNE_P28 +MARNE_P23 -->|3km| STDIZ_04 +MARNE_P23 --> M(MARNE) +MARNE_P28 --> M +MARNE_P28 -->|25.6km| MARNE_R25 +M --> MARNE_R25 +STDIZ_04 -->|28.7km| MARNE_R25 +MARNE_R25 -->|56.9km| CHALO_21 +style MARNE_P28 fill:#fcc +style MARNE_P23 fill:#fcc +style MARNE_R25 fill:#fcc +style M fill:#ccf +", width = "100%") +``` +And can be modeled as: + +```{r echo = FALSE} +mmd(" +graph LR +MARNE_P23 -->|3km| STDIZ_04 +LOUVE_19 -->|83km| CHALO_21 +MARNE_P28 -->|82.5km| CHALO_21 +STDIZ_04 -->|85.6km| CHALO_21 +MARNE_R25 -->|56.9km| CHALO_21 +style MARNE_P28 fill:#fcc +style MARNE_P23 fill:#fcc +style MARNE_R25 fill:#fcc +", width = "100%") +``` + +Hence the topological connection to the reservoirs is described in the model as below: + +```{r} +reservoir_connections <- readr::read_delim( + file = system.file("seine_data", "network_reservoir_connections.txt", package = "griwrm"), + delim = "\t" +) +reservoir_connections +``` + +Reservoir connections are added to the griwrm object: + +```{r} +reservoir_connections$model <- NA +reservoir_connections$area <- NA +griwrm2 <- rbind(griwrm, reservoir_connections) +DiagramGriwrm(griwrm2) +``` + + +## Loading reservoir observation time series + +```{r} + +lCfgReservoirs <- jsonlite::read_json(system.file("seine_data", "config_reservoirs.json", package = "griwrm")) + +for(reservoir_id in names(lCfgReservoirs)) { + df <- readr::read_delim( + file = file.path( + "https://stratus.irstea.fr/d/0b18e688851a45478f7a/files/?p=/Reservoir_current_rules", + paste0(lCfgReservoirs[[reservoir_id]]$file, "&dl=1") + ), + delim = "\t", + col_types = readr::cols( + Date = readr::col_character(), + .default = readr::col_double() + ) + ) + df$Date <- format(as.POSIXlt(lubridate::dmy(df$Date)), "%Y-%m-%d") + Qreservoir <- merge(data.frame(Date = format(DatesR, "%Y-%m-%d")), df, by = "Date", all.x = TRUE) + for(connect in names(lCfgReservoirs[[reservoir_id]]$connections)) { + # Replace data gap by zeros (should be interpolated ?) + Qreservoir[is.na(Qreservoir[,lCfgReservoirs[[reservoir_id]]$connections[[connect]]$col]),lCfgReservoirs[[reservoir_id]]$connections[[connect]]$col] <- 0 + if(lCfgReservoirs[[reservoir_id]]$connections[[connect]]$type == "in") { + # Reservoir inlet flow is a withdrawal for the catchment: this is a negative flow to route + Qreservoir[,lCfgReservoirs[[reservoir_id]]$connections[[connect]]$col] <- -Qreservoir[,lCfgReservoirs[[reservoir_id]]$connections[[connect]]$col] + } + Qobs <- cbind( + Qobs, + Qreservoir[,lCfgReservoirs[[reservoir_id]]$connections[[connect]]$col] + ) + names(Qobs)[ncol(Qobs)] <- connect + } +} +# Convert flows from m3/s to m3/day +Qobs[,-1] <- Qobs[,-1] * 86400 + +``` + +## How to handle online reservoir ? The Pannecière lake case. + +There are two possibilities: + +- truncate the system and only simulate the release of the reservoir if we only care about downstream flows +- add a fictive inlet to the reservoir that will store all upstream discharge into the reservoir + +If we know in advance the flow released by the reservoir, upstream flow informations is not usefull for the simulation. But reservoir management simulation would need upstream flow informations for simulating the reservoir state during simulation. The second alternative will be useful for the next phases. + +```{r} +Qobs$PANNEC_P <- -Qobs$CHAUM_07 +``` + + +## Create the InputsModel object + +```{r} +InputsModel2 <- CreateInputsModel(griwrm2, DatesR, Precip, PotEvap, Qobs) +``` + +## Run simulation with naturalised flow parameters + +### Load calibration parameters + +```{r} +# Load RunOptions +load("_cache/V02.RData") +# Load calibrated parameters with Michel's method +load("_cache/V03.RData") +``` + + +### How to handle former upstream sub-basins with upstream flows ? + +A lag parameter is now mandatory for these sub-basins. As no calibration is possible at that stage an arbitrary one will be used. + +```{r} +ParamMichel$STDIZ_04[5] <- 1 +``` + + +### Run simulation + +```{r} +OutputsModels2 <- RunModel( + InputsModel = InputsModel2, + RunOptions = RunOptions, + Param = ParamMichel +) +``` + +## Compare modelled flows with observed flows + +### Load observed flow + +```{r, warning=FALSE, message=FALSE} + +Qobs2 <- NULL + +# Files of observed flows doesn't exist for some basins +NatIds <- c("CHAUM_07", "MONTE_15", "NOGEN_13", "STGER_09", "TRANN_01") + +ids <- griwrm$id[!griwrm$id %in% NatIds] + +for(id in ids) { + url <- + file.path( + "https://stratus.irstea.fr/d/0b18e688851a45478f7a/files/?p=/climaware_hydro/Q_OBS_NAT", + paste0(id, "_BV.txt&dl=1") + ) + ts <- readr::read_delim(file = url, + delim = ";", skip = 24, trim_ws = TRUE) + # Date conversion to POSIX + ts$Date <- as.POSIXlt(lubridate::ymd(ts$Date)) + # Convert Qobs from l/s to mm/time step + ts$Q <- ts$Q * 86.4 / griwrm$area[griwrm$id == id] / 1000 + # Setting data gaps to NA + ts$Q[ts$Q <= 0] <- NA + # Qnat column is merged into Qobs dataframe + Qobs2 <- MergeTS(Qobs2, id, ts[,c("Date", "Q")]) +} +``` + +### Comparison with simulated flows + +```{r, fig.height = 5, fig.width = 8} +ReduceOutputsModel <- function(OutputsModels, IndPeriod) { + items <- names(OutputsModels) + OutputsModelsOut <- sapply(items, function(x) {OutputsModels[[x]] <- OutputsModels[[x]][IndPeriod]}) + OutputsModelsOut$StateEnd <- OutputsModels$StateEnd + class(OutputsModelsOut) <- class(OutputsModels) + return(OutputsModelsOut) +} + +htmltools::tagList(lapply( + ids, + function(x) { + Q2 <- Qobs2[RunOptions[[1]]$IndPeriod_Run,x] + IndPeriod_Obs <- which(!is.na(Q2)) + OutputsModels <- ReduceOutputsModel(OutputsModels2[[x]], IndPeriod_Obs) + plot(OutputsModels, Qobs = Q2[IndPeriod_Obs] , main = x) + } +)) +``` + +