Skip to contents

Aim

The aim of this vignette is to illustrate how to use medfateland (v. 2.5.0) to carry out simulations of forest function and dynamics on a set of forest stands while including lateral water transfer processes. This is done using functions spwb_land(), growth_land() and fordyn_land(); which are counterparts of functions spwb(), growth() and fordyn() in package medfate. We will focus here on function spwb_land(), but the other two functions would be used similarly. The same can be said for functions spwb_land_day() and growth_land_day(), which are counterparts of spwb_day() and growth_day(), respectively.

Preparation

Preparing inputs for watershed simulations can be tedious. Two main inputs need to be assembled, described in the following two sections.

Input sf objects

Here we load a small example watershed included with the package, that can be used to understand the inputs required:

data("example_watershed")
example_watershed
## Simple feature collection with 66 features and 14 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 401430 ymin: 4671870 xmax: 402830 ymax: 4672570
## Projected CRS: WGS 84 / UTM zone 31N
## # A tibble: 66 × 15
##            geometry    id elevation slope aspect land_cover_type
##  *      <POINT [m]> <int>     <dbl> <dbl>  <dbl> <chr>          
##  1 (402630 4672570)     1      1162 11.3   79.2  wildland       
##  2 (402330 4672470)     2      1214 12.4   98.7  agriculture    
##  3 (402430 4672470)     3      1197 10.4  102.   wildland       
##  4 (402530 4672470)     4      1180  8.12  83.3  wildland       
##  5 (402630 4672470)     5      1164 13.9   96.8  wildland       
##  6 (402730 4672470)     6      1146 11.2    8.47 agriculture    
##  7 (402830 4672470)     7      1153  9.26 356.   agriculture    
##  8 (402230 4672370)     8      1237 14.5   75.1  wildland       
##  9 (402330 4672370)     9      1213 13.2   78.7  wildland       
## 10 (402430 4672370)    10      1198  8.56  75.6  agriculture    
## # ℹ 56 more rows
## # ℹ 9 more variables: forest <list>, soil <list>, state <list>,
## #   depth_to_bedrock <dbl>, bedrock_conductivity <dbl>, bedrock_porosity <dbl>,
## #   snowpack <dbl>, aquifer <dbl>, crop_factor <dbl>

Some of the columns like forest, soil, elevation, or state, were also present in the example for spatially-uncoupled simulations, so we will not repeat them. The following describes additional columns that are relevant here.

Land cover type

Simulations over watersheds normally include different land cover types. These are described in column land_cover_type:

table(example_watershed$land_cover_type)
## 
## agriculture        rock    wildland 
##          17           1          48

Local and landscape processes will behave differently depending on the land cover type.

Aquifer and snowpack

Columns aquifer and snowpack are used as state variables to store the water content in the aquifer and snowpack, respectively.

Crop factors

Since the landscape contains agricultural lands, we need to define crop factors, which will determine transpiration flow as a proportion of potential evapotranspiration:

example_watershed$crop_factor = NA
example_watershed$crop_factor[example_watershed$land_cover_type=="agriculture"] = 0.75

Grid topology

Note that the sf structure does not imply a grid per se. Point geometry is used to describe the central coordinates of grid cells, but does not describe the grid. This means that another spatial input is needed to describe the grid topology, which in our case is an object of class SpatRaster from package terra:

r <-terra::rast(xmin = 401380, ymin = 4671820, xmax = 402880, ymax = 4672620, 
                nrow = 8, ncol = 15, crs = "epsg:32631")
r
## class       : SpatRaster 
## dimensions  : 8, 15, 1  (nrow, ncol, nlyr)
## resolution  : 100, 100  (x, y)
## extent      : 401380, 402880, 4671820, 4672620  (xmin, xmax, ymin, ymax)
## coord. ref. : WGS 84 / UTM zone 31N (EPSG:32631)

The r object must have the same coordinate reference system as the sf object. Moreover, each grid cell can contain up to one point of the sf (typically at the cell center). Some grid cells may be empty, though, so that the actual simulations may be done on an incomplete grid. Note that the raster does not contain data, only the topology is needed (to define neighbors and cell sizes, for example). All relevant attribute data is already included in the sf object.

Combining the r and sf objects allows drawing rasterized maps:

plot_variable(example_watershed, variable = "elevation", r = r)

Watershed control options

Analogously to local-scale simulations with medfate, watershed simulations have overall control parameters. Notably, the user needs to decide which sub-model will be used for lateral water transfer processes (a decision similar to choosing the plant transpiration sub-model in medfate), by default “tetis”:

ws_control <- default_watershed_control("tetis")

Initialization

Simulation model inputs need to be created for the target watershed before launching simulations. This may be done automatically, though, when calling watershed simulation functions, but in many occasions it is practical to perform this step separately. If we plan to use function spwb_land(), watershed initialization would be as follows:

example_init <- initialize_landscape(example_watershed, SpParams = SpParamsMED,
                                     local_control = defaultControl())
##  Creating 65 state objects for model 'spwb'.
##  Creating 65 state objects for model 'spwb'. [18ms]
## 
example_init
## Simple feature collection with 66 features and 14 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 401430 ymin: 4671870 xmax: 402830 ymax: 4672570
## Projected CRS: WGS 84 / UTM zone 31N
## # A tibble: 66 × 15
##            geometry    id elevation slope aspect land_cover_type
##  *      <POINT [m]> <int>     <dbl> <dbl>  <dbl> <chr>          
##  1 (402630 4672570)     1      1162 11.3   79.2  wildland       
##  2 (402330 4672470)     2      1214 12.4   98.7  agriculture    
##  3 (402430 4672470)     3      1197 10.4  102.   wildland       
##  4 (402530 4672470)     4      1180  8.12  83.3  wildland       
##  5 (402630 4672470)     5      1164 13.9   96.8  wildland       
##  6 (402730 4672470)     6      1146 11.2    8.47 agriculture    
##  7 (402830 4672470)     7      1153  9.26 356.   agriculture    
##  8 (402230 4672370)     8      1237 14.5   75.1  wildland       
##  9 (402330 4672370)     9      1213 13.2   78.7  wildland       
## 10 (402430 4672370)    10      1198  8.56  75.6  agriculture    
## # ℹ 56 more rows
## # ℹ 9 more variables: forest <list>, soil <list>, state <list>,
## #   depth_to_bedrock <dbl>, bedrock_conductivity <dbl>, bedrock_porosity <dbl>,
## #   snowpack <dbl>, aquifer <dbl>, crop_factor <dbl>

Here we use function defaultControl() to specify the control parameters for local processes. Function initialize_landscape() makes internal calls to spwbInput() of medfate and defines a column state with the initialized inputs.

At this point is important to learn one option that may speed up calculations. Initialization may be done while simplifying forest structure to the dominant species (see function forest_reduceToDominant() in package medfate). Hence, we can initialize using simplify = TRUE:

example_simplified <- initialize_landscape(example_watershed, SpParams = SpParamsMED,
                                           local_control = defaultControl(),
                                           simplify = TRUE)
##  Creating 65 state objects for model 'spwb'.
##  Creating 65 state objects for model 'spwb'. [5ms]
## 
example_simplified
## Simple feature collection with 66 features and 14 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 401430 ymin: 4671870 xmax: 402830 ymax: 4672570
## Projected CRS: WGS 84 / UTM zone 31N
## # A tibble: 66 × 15
##            geometry    id elevation slope aspect land_cover_type
##  *      <POINT [m]> <int>     <dbl> <dbl>  <dbl> <chr>          
##  1 (402630 4672570)     1      1162 11.3   79.2  wildland       
##  2 (402330 4672470)     2      1214 12.4   98.7  agriculture    
##  3 (402430 4672470)     3      1197 10.4  102.   wildland       
##  4 (402530 4672470)     4      1180  8.12  83.3  wildland       
##  5 (402630 4672470)     5      1164 13.9   96.8  wildland       
##  6 (402730 4672470)     6      1146 11.2    8.47 agriculture    
##  7 (402830 4672470)     7      1153  9.26 356.   agriculture    
##  8 (402230 4672370)     8      1237 14.5   75.1  wildland       
##  9 (402330 4672370)     9      1213 13.2   78.7  wildland       
## 10 (402430 4672370)    10      1198  8.56  75.6  agriculture    
## # ℹ 56 more rows
## # ℹ 9 more variables: forest <list>, soil <list>, state <list>,
## #   depth_to_bedrock <dbl>, bedrock_conductivity <dbl>, bedrock_porosity <dbl>,
## #   snowpack <dbl>, aquifer <dbl>, crop_factor <dbl>

For computational reasons, we will keep with this simplified initialization in the next sections.

Carrying out simulations

Launching watershed simulations

To speed up calculations, we call function spwb_land() for a single month:

dates <- seq(as.Date("2001-01-01"), as.Date("2001-01-31"), by="day")
res_ws1 <- spwb_land(r, example_simplified,
                    SpParamsMED, examplemeteo, dates = dates, summary_frequency = "month",
                    watershed_control = ws_control, progress = FALSE)

Although simulations are performed using daily temporal steps, parameter summary_frequency allows storing results at coarser temporal scales, to reduce the amount of memory in spatial results.

Structure of simulation outputs

Function spwb_land() and growth_land() return a list with the following elements:

names(res_ws1)
## [1] "watershed_control"      "sf"                     "watershed_balance"     
## [4] "watershed_soil_balance" "outlet_export_m3s"

Where sf is an object of class sf, analogous to those of functions *_spatial():

res_ws1$sf
## Simple feature collection with 66 features and 6 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 401430 ymin: 4671870 xmax: 402830 ymax: 4672570
## Projected CRS: WGS 84 / UTM zone 31N
## # A tibble: 66 × 7
##            geometry state           aquifer snowpack summary  result outlet
##         <POINT [m]> <list>            <dbl>    <dbl> <list>   <list> <lgl> 
##  1 (402630 4672570) <spwbInpt [19]> 127.        3.56 <dbl[…]> <NULL> FALSE 
##  2 (402330 4672470) <aspwbInp [4]>    0.308     3.56 <dbl[…]> <NULL> FALSE 
##  3 (402430 4672470) <spwbInpt [19]>   2.21      3.56 <dbl[…]> <NULL> FALSE 
##  4 (402530 4672470) <spwbInpt [19]>  10.1       2.54 <dbl[…]> <NULL> FALSE 
##  5 (402630 4672470) <spwbInpt [19]> 155.        2.57 <dbl[…]> <NULL> FALSE 
##  6 (402730 4672470) <aspwbInp [4]>  876.        3.56 <dbl[…]> <NULL> TRUE  
##  7 (402830 4672470) <aspwbInp [4]>  410.        3.56 <dbl[…]> <NULL> FALSE 
##  8 (402230 4672370) <spwbInpt [19]>   0.261     2.53 <dbl[…]> <NULL> FALSE 
##  9 (402330 4672370) <spwbInpt [19]>   1.45      2.81 <dbl[…]> <NULL> FALSE 
## 10 (402430 4672370) <aspwbInp [4]>    6.11      3.56 <dbl[…]> <NULL> FALSE 
## # ℹ 56 more rows

Columns state, aquifer and snowpack contain state variables, whereas summary contains temporal water balance summaries for all cells. Column result is empty in this case, but see below.

The next two elements of the simulation result list, namely watershed_balance and watershed_soil_balance, refer to watershed-level results. For example, watershed_balance contains the daily elements of the water balance at the watershed level, including the amount of water exported in mm in the last column.

head(res_ws1$watershed_balance)
##        dates Precipitation      Rain Snow Snowmelt Interception   NetRain
## 1 2001-01-01      4.869109  4.869109    0        0    0.7754218  4.093687
## 2 2001-01-02      2.498292  2.498292    0        0    0.6090627  1.889229
## 3 2001-01-03      0.000000  0.000000    0        0    0.0000000  0.000000
## 4 2001-01-04      5.796973  5.796973    0        0    0.7723790  5.024594
## 5 2001-01-05      1.884401  1.884401    0        0    0.4808826  1.403519
## 6 2001-01-06     13.359801 13.359801    0        0    0.8613997 12.498401
##   Infiltration InfiltrationExcess SaturationExcess  CellRunon CellRunoff
## 1     4.093687         0.00000000         0.000000 0.00000000   0.000000
## 2     1.889229         0.00000000         0.000000 0.00000000   0.000000
## 3     0.000000         0.00000000         0.000000 0.00000000   0.000000
## 4     5.024594         0.00000000         5.399544 0.00000000   5.399544
## 5     1.403519         0.00000000         8.620346 0.00000000   8.620346
## 6    12.498401         0.05090607        14.332811 0.05090607  14.383717
##   DeepDrainage CapillarityRise DeepAquiferLoss SoilEvaporation Transpiration
## 1     2.870060               0               0       0.3874890     0.2846970
## 2     1.766971               0               0       0.4675914     0.5045596
## 3     1.910884               0               0       0.3482575     0.4236524
## 4     2.275585               0               0       0.2006254     0.1904111
## 5     2.175753               0               0       0.2893019     0.5026577
## 6     2.335919               0               0       0.1855165     0.3597430
##   HerbTranspiration InterflowBalance BaseflowBalance AquiferExfiltration
## 1                 0     0.000000e+00    0.000000e+00                   0
## 2                 0    -1.859834e-16    3.364312e-17                   0
## 3                 0    -7.372049e-16    2.523234e-18                   0
## 4                 0     3.462298e-15   -3.953067e-17                   0
## 5                 0    -1.364229e-15    1.261617e-17                   0
## 6                 0     3.606543e-15    5.803439e-17                   0
##   WatershedExport
## 1        0.000000
## 2        0.000000
## 3        0.000000
## 4        5.399544
## 5        8.620346
## 6       14.383717

Values of this output data frame are averages across cells in the landscape. Data frame watershed_soil_balance is similar to watershed_balance but focusing on cells that have a soil (i.e. excluding artificial, rock or water land cover). Finally, outlet_export_m3 contains the volume reaching each outlet cell per day:

head(res_ws1$outlet_export_m3)
##                     6
## 2001-01-01 0.00000000
## 2001-01-02 0.00000000
## 2001-01-03 0.00000000
## 2001-01-04 0.04126980
## 2001-01-05 0.06588704
## 2001-01-06 0.10993764

Accessing and plotting cell summaries

Unlike spwb_spatial() where summaries could be arbitrarily generated a posteriori from simulation results, with spwb_land() the summaries are always fixed and embedded with the simulation result. For example, we can inspect the summaries for a given landscape cell using:

res_ws1$sf$summary[[1]]
##            MinTemperature MaxTemperature      PET     Rain     Snow Snowmelt
## 2001-01-01      -3.203556       2.427977 31.14151 58.09884 16.65065 13.09301
##            Interception  NetRain Infiltration InfiltrationExcess
## 2001-01-01     23.59187 34.50697     47.59998                  0
##            SaturationExcess Runon Runoff DeepDrainage CapillarityRise
## 2001-01-01                0     0      0     55.82266               0
##            DeepAquiferLoss SoilEvaporation Transpiration HerbTranspiration
## 2001-01-01               0        2.792085      9.971227                 0
##            InterflowInput InterflowOutput InterflowBalance BaseflowInput
## 2001-01-01       1040.115        1077.014        -36.89932      246.1273
##            BaseflowOutput BaseflowBalance AquiferExfiltration      SWE  SoilVol
## 2001-01-01       174.5932        71.53411                   0 1.699603 530.3081
##                 RWC WTD      DTA
## 2001-01-01 98.54167  NA 14.32596

Several plots can be drawn from the result of function spwb_land() in a similar way as done for spwb_spatial(). As an example we display a map of the average soil relative water content during the simulated month:

plot_summary(res_ws1$sf, variable = "RWC", date = "2001-01-01", r = r)

Full simulation results for specific cells

The idea of generating summaries arises from the fact that local models can produce a large amount of results, of which only some are of interest at the landscape level. Nevertheless, it is possible to specify those cells for which full daily results are desired. This is done by adding a column result_cell in the input sf object:

# Set request for daily model results in cells number 3 and 9
example_simplified$result_cell <- FALSE
example_simplified$result_cell[c(3,9)] <- TRUE

If we launch the simulations again (omitting progress information):

res_ws1 <- spwb_land(r, example_simplified,
                    SpParamsMED, examplemeteo, dates = dates, summary_frequency = "month",
                    watershed_control = ws_control, progress = FALSE)

We can now retrieve the results of the desired cell, e.g. the third one, in column result of sf:

S <- res_ws1$sf$result[[3]]
class(S)
## [1] "spwb" "list"

This object has class spwb and the same structure returned by function spwb() of medfate. Hence, we can inspect daily results using functions shinyplot() or plot(), for example:

plot(S, "SoilRWC")

Continuing a previous simulation

The result of a simulation includes an element state, which stores the state of soil and stand variables at the end of the simulation. This information can be used to perform a new simulation from the point where the first one ended. In order to do so, we need to update the state variables in spatial object with their values at the end of the simulation, using function update_landscape():

example_watershed_mod <- update_landscape(example_watershed, res_ws1)
names(example_watershed_mod)
##  [1] "geometry"             "id"                   "elevation"           
##  [4] "slope"                "aspect"               "land_cover_type"     
##  [7] "forest"               "soil"                 "state"               
## [10] "depth_to_bedrock"     "bedrock_conductivity" "bedrock_porosity"    
## [13] "snowpack"             "aquifer"              "crop_factor"

Note that a new column state appears in now in the sf object. We can check the effect by drawing the relative water content:

plot_variable(example_watershed_mod, variable = "soil_rwc_curr", r = r)

Now we can continue our simulation, in this case adding an extra month:

dates <- seq(as.Date("2001-02-01"), as.Date("2001-02-28"), by="day")
res_ws3 <- spwb_land(r, example_watershed_mod,
                    SpParamsMED, examplemeteo, dates = dates, summary_frequency = "month",
                    watershed_control = ws_control, progress = FALSE)

The fact that no cell required initialization is an indication that we used an already initialized landscape.

Burn-in periods

Like other distributed hydrological models, watershed simulations with medfateland will normally require a burn-in period to allow soil moisture and aquifer levels to reach a dynamic equilibrium. We recommend users to use at least one or two years of burn-in period, but this will depend on the size of the watershed. In medfate we provide users with a copy of the example watershed, where burn-in period has already been simulated. This can be seen by inspecting the aquifer level:

data("example_watershed_burnin")
plot_variable(example_watershed_burnin, variable = "aquifer", r = r)

If we run a one-month simulation on this data set we can then compare the output before and after the burn-in period to illustrate its importance:

dates <- seq(as.Date("2001-01-01"), as.Date("2001-01-31"), by="day")
res_ws3 <- spwb_land(r, example_watershed_burnin,
                    SpParamsMED, examplemeteo, dates = dates, summary_frequency = "month",
                    watershed_control = ws_control, progress = FALSE)
data.frame("before" = res_ws1$watershed_balance$WatershedExport, 
           "after" = res_ws3$watershed_balance$WatershedExport)
##         before      after
## 1   0.00000000 0.09798985
## 2   0.00000000 0.09650657
## 3   0.00000000 0.09592939
## 4   5.39954370 0.09555589
## 5   8.62034607 0.09459699
## 6  14.38371718 0.12787276
## 7   9.72970344 0.09423899
## 8  17.12959233 0.09402957
## 9  12.80189935 0.09503280
## 10  5.86754069 0.10051038
## 11  6.85004331 0.10256739
## 12 10.04891979 0.10906381
## 13  7.92767125 0.11632381
## 14  7.90721531 0.12063961
## 15  5.75790391 0.12002380
## 16  3.90833523 3.04502260
## 17  3.01041130 3.00683826
## 18  2.89642554 2.33187488
## 19  2.09476284 0.82091581
## 20  4.78444693 0.22336267
## 21  1.66854021 0.26273885
## 22  0.76462819 0.22293633
## 23  0.35263182 0.17877625
## 24  0.44476490 0.17760578
## 25  0.28943777 0.17680596
## 26  0.24380897 0.17536251
## 27  0.22966862 0.17350546
## 28  0.20649455 0.17141156
## 29  0.18310116 0.16901969
## 30  0.08320216 0.16650239
## 31  0.03666808 0.16395547

Simulations of watershed forest dynamics

Running growth_land() is very similar to running spwb_land(). However, a few things change when we want to simulate forest dynamics using fordyn_land(). Regarding the sf input, an additional column management_arguments may be defined to specify the forest management arguments (i.e. silviculture) of cells. Furthermore, the function does not allow choosing the temporal scale of summaries. Strong simplification of forest structure to dominant species will not normally make sense in this kind of simulation, since the focus is on forest dynamics.

A call to fordyn_land() for a single year is given here, as an example, starting from the initial example watershed:

res_ws4 <- fordyn_land(r, example_watershed,
                       SpParamsMED, examplemeteo,
                       watershed_control = ws_control, progress = FALSE)

Simulations using weather interpolation

Large watersheds will have spatial differences in climatic conditions like temperature, precipitation. Specifying a single weather data frame for all the watershed may be not suitable in this case. Specifying a different weather data frame for each watershed cell can also be a problem, if spatial resolution is high, due to the huge data requirements. A solution for this can be using interpolation on the fly, inside watershed simulations. This can be done by supplying an interpolator object (or a list of them), as defined in package meteoland. Here we use the example data provided in the package:

interpolator <- meteoland::with_meteo(meteoland_meteo_example, verbose = FALSE) |>
    meteoland::create_meteo_interpolator(params = defaultInterpolationParams())
##  Creating interpolator...
## • Calculating smoothed variables...
## • Updating intial_Rp parameter with the actual stations mean distance...
##  Interpolator created.

Once we have this object, using it is straightforward:

res_ws5 <- spwb_land(r, example_watershed_burnin, SpParamsMED, 
                     meteo = interpolator, summary_frequency = "month",
                     watershed_control = ws_control, progress = FALSE)

Note that we did not define dates, which are taken from the interpolator data. If we plot the minimum temperature, we will appreciate the spatial variation in climate:

plot_summary(res_ws5$sf, variable = "MinTemperature", date = "2022-04-01", r = r)

For large watersheds and fine spatial resolution interpolation can become slow. One can then specify that interpolation is performed on a coarser grid, by using a watershed control parameter, for example:

ws_control$weather_aggregation_factor <- 3

To illustrate its effect, we repeat the previous simulation and plot the minimum temperature:

res_ws6 <- spwb_land(r, example_watershed_burnin, SpParamsMED, 
                     meteo = interpolator, summary_frequency = "month",
                     watershed_control = ws_control, progress = FALSE)
plot_summary(res_ws6$sf, variable = "MinTemperature", date = "2022-04-01", r = r)