Terraflux Terraflux
Download

Ecosystem services with InVEST, part 4 of 8

Annual water yield

InVEST's annual water yield model in Terraflux - the water balance of every cell on Budyko's curve, the seasonality constant Z, the yield of each watershed, what people consume, the realized supply and its value as hydropower, and what a land use scenario does to them.

Level
Intermediate
Time
50 min
  1. 11. The ideas
  2. 22. Carbon
  3. 33. Habitat quality
  4. 44. Water yield
  5. 55. Sediment
  6. 66. Nutrients
  7. 77. Seasonal water
  8. 88. Pollination

Of the rain that falls on a watershed in a year, part returns to the air through the leaves and the soil, and the rest leaves as streamflow and groundwater: the water yield, the water a town, a farm or a reservoir downstream can use. Land use changes the split, because forests and crops evaporate different amounts. In this tutorial you will build InVEST's annual water yield model in Terraflux on Ribeirão Claro: the maps of potential and actual evapotranspiration and of yield, the yield of each watershed, the water people consume, the supply that remains and what it is worth to a hydropower plant. At the end you will know how the model splits the rain, which parameter matters most, and what the model cannot say about water.

The idea#

Over a year, and over a whole catchment, the water balance is simple: what falls as precipitation \(P\) either evaporates (from soil, water and leaves, and through the plants: together the actual evapotranspiration, AET) or leaves as yield, \(Y = P - \text{AET}\) (storage in the soil and aquifers changes little from one average year to the next). The hard part is AET. It cannot exceed the water available (\(P\)) nor the energy available to evaporate it, measured as the potential evapotranspiration PET, the evaporation of a well-watered surface. Budyko (1974) noticed that catchments of the world fall on a curve between those two limits: wet climates (PET much smaller than \(P\)) evaporate almost their PET and are energy limited, dry ones (PET larger than \(P\)) evaporate almost all their rain and are water limited.

Fu (1981) and Zhang et al. (2004) wrote the curve with one parameter, \(\omega\), that sets how close a catchment gets to the limits: the larger \(\omega\), the more of its rain the vegetation manages to evaporate. Donohue et al. (2012) linked \(\omega\) to things you can map: the water the soil holds for roots, and how the rain is spread over the year. InVEST's model applies the curve cell by cell.

Key idea: Budyko's curve in Fu and Zhang's form

For a cell \(x\) of a class with vegetation,

\[ \frac{\text{AET}_x}{P_x} = 1 + \frac{\text{PET}_x}{P_x} - \left[1 + \left(\frac{\text{PET}_x}{P_x}\right)^{\omega_x}\right]^{1/\omega_x} \]

with the potential evapotranspiration \(\text{PET}_x = K_c\,\text{ET}_{0,x}\) (the reference evapotranspiration \(\text{ET}_0\) of the climate times the class's crop coefficient \(K_c\)), and

\[ \omega_x = Z\,\frac{\text{AWC}_x}{P_x} + 1.25 \quad (\text{at most } 5) \]
\[ \text{AWC}_x = \min(\text{root depth}_j,\ \text{soil depth}_x) \times \text{PAWC}_x \]

where AWC is the water available to the plants (mm), PAWC the plant available water content of the soil (the share of its volume between field capacity and wilting point), the soil depth is the depth to the layer that stops roots, and \(Z\) the seasonality constant, which grows with the number of rain events in a year. Classes without vegetation (water, towns) evaporate \(\min(K_c\,\text{ET}_0, P)\). The yield is \(Y_x = P_x - \text{AET}_x\).

Budyko's curve for five values of omega
The share of rain evaporated against the dryness of the climate (PET / P) in Fu and Zhang's form. The dashed lines are the limits (all the energy or all the water used). ω = 1.25 is the smallest the model gives (no plant available water), 5 the largest it allows.

A worked cell: rain 1,500 mm, \(\text{ET}_0\) 1,100 mm, soil 1,500 mm deep with a PAWC of 0.15, \(Z = 10\). As forest (\(K_c = 1.0\), roots 5,000 mm), \(\text{AWC} = \min(5000, 1500) \times 0.15 = 225\) mm, \(\omega = 10 \times 225/1500 + 1.25 = 2.75\), \(\text{PET}/P = 0.733\), and the curve gives \(\text{AET}/P = 0.596\): 893 mm evaporate and 607 mm are yielded. As pasture (\(K_c = 0.75\), roots 1,500 mm) the soil still limits the roots (\(\omega = 2.75\)), but \(\text{PET}/P = 0.55\) and \(\text{AET}/P = 0.484\): 775 mm are yielded, 168 mm more.

From supply to service and value#

The yield is a supply. With a demand table (the water each class consumes, in m³ per year and cell) the model subtracts consumption in each watershed to give the realized supply, the water that reaches the outlet. With a valuation table it turns the realized supply into the energy a hydropower plant at the outlet would produce and its net present value:

\[ \varepsilon = 0.00272\;\beta\,\gamma\,h\,V, \qquad \text{NPV} = (p_e\,\varepsilon - TC) \sum_{t=0}^{T-1} \frac{1}{(1 + r/100)^t} \]

where \(V\) is the realized supply (m³ / year), \(\beta\) the turbine efficiency, \(\gamma\) the fraction of the inflow used to generate power, \(h\) the head of the dam (m), 0.00272 the kWh produced by a cubic metre falling one metre, \(p_e\) the price of a kWh, \(TC\) the annual cost of running the plant, \(T\) its remaining life in years and \(r\) the discount rate.

When to use it#

  • Scenarios: how much more (or less) water a watershed yields if its forest becomes pasture, or its pasture plantation. Eucalyptus, with deep roots and a high \(K_c\), is a classic case.
  • Who provides and who uses water: the yield of each sub-watershed against the consumption in it, for water funds and payments for watershed services.
  • Hydropower: the energy and value of the water that reaches a reservoir.

What it is not: the model works with annual averages. It says nothing about floods or dry seasons, about when the water arrives, about groundwater (it does not separate surface and groundwater, nor model their exchanges), or about water quality. A forest that yields less water in a year may still keep more flow in the dry season by letting the rain soak in; the model cannot tell. For daily flows use a process model such as SWAT+ (swat.run). Check its numbers against measured flows where you can: Redhead et al. (2016) found, in 22 British catchments, that the fit depended mostly on the input data (within about 10% of the measured flow for most catchments with national data, much worse with global data). Hamel and Guswa (2015), in a basin of North Carolina, found that the errors that mattered most were those of the climate inputs, above all the annual precipitation, and of \(Z\).

What you need#

All rasters on the grid of the land use map, projected in metres:

Input File Unit Where it comes from
lulc lulc_2020.tif class codes your land use map
precipitation precipitation.tif mm / year interpolated rain gauges, WorldClim, CHIRPS
eto eto.tif mm / year reference evapotranspiration (FAO's Penman-Monteith), from climate data
depth depth.tif mm depth to the layer that stops roots, from a soil map
pawc pawc.tif 0 to 1 plant available water content, from soil texture
watersheds watersheds.gpkg polygons with ws_id delineated from the DEM (hydro.watersheds)

The biophysical table, by class: lucode, lulc_veg (1 for vegetation, which follows Budyko's curve; 0 for water, towns, wetlands), root_depth (mm, used only when lulc_veg is 1) and kc. The values here are illustrative:

lucode class lulc_veg root_depth kc
1 Forest 1 5000 1.0
2 Pasture 1 1500 0.75
3 Cropland 1 1200 0.7
4 Eucalyptus 1 6000 1.0
5 Urban 0 1 0.3
6 Water 0 1 1.0

Optional: sub-watersheds (polygons with subws_id); a demand table (lucode, demand in m³ per year and cell: here 900 for the town, 150 for irrigated cropland, 5 for pasture's cattle, 0 for the rest); a valuation table by watershed (ws_id, efficiency, fraction, height, kw_price, cost, time_span, discount).

Build the model#

Step 1: the water balance#

  1. Add five raster.read nodes: lulc_2020.tif, precipitation.tif, eto.tif, depth.tif, pawc.tif. Rename each after its content.
  2. Add a table.read node for biophysical_wy.csv.
  3. Add services.water_yield (subject Ecosystem services) and connect each map to its input of the same name, and the table to biophysical.
  4. Set seasonality (\(Z\)) to 10 in the Node pane, and run.
lulc (raster.read)1lulcraster.readrasterraster: rasterpath = "lulc_2020.tif"biophysical (table.read)2biophysicaltable.readtabletable: tablepath = "biophysical_wy.csv"precipitation (raster.read)3precipitationraster.readrasterraster: rasterpath = "precipitation.tif"eto (raster.read)4etoraster.readrasterraster: rasterpath = "eto.tif"depth (raster.read)5depthraster.readrasterraster: rasterpath = "depth.tif"pawc (raster.read)6pawcraster.readrasterraster: rasterpath = "pawc.tif"water (services.water_yield)7waterservices.water_yieldlulc: maplulcbiophysical: tablebiophysicalprecipitation: mapprecipitationeto: mapetodepth: mapdepthpawc: mappawcwyieldwyield: mapaetaet: mappetpet: mapfractpfractp: mapdemand_mapdemand_map: mapwatershed_tablewatershed_table: tablesubwatershed_tablesubwatershed_table: tableseasonality = 10
The water balance of 2020: land use, climate, soils and the biophysical table.
Model file (TOML)
format = "terraflux/1"

[[nodes]]
id = "lulc"
op = "raster.read"
params = { path = "lulc_2020.tif" }

[[nodes]]
id = "biophysical"
op = "table.read"
params = { path = "biophysical_wy.csv" }
note = "lucode, lulc_veg, root_depth, kc."

[[nodes]]
id = "precipitation"
op = "raster.read"
params = { path = "precipitation.tif" }

[[nodes]]
id = "eto"
op = "raster.read"
params = { path = "eto.tif" }

[[nodes]]
id = "depth"
op = "raster.read"
params = { path = "depth.tif" }

[[nodes]]
id = "pawc"
op = "raster.read"
params = { path = "pawc.tif" }

[[nodes]]
id = "water"
op = "services.water_yield"
inputs = { lulc = "lulc.raster", biophysical = "biophysical.table", precipitation = "precipitation.raster", eto = "eto.raster", depth = "depth.raster", pawc = "pawc.raster" }
params = { seasonality = 10 }

The node gives four maps, in mm per year: pet (\(K_c\,\text{ET}_0\)), aet, wyield and fractp (AET / P, 0 to 1). Cells with no rain are left out, as InVEST leaves them.

Step 2: watersheds, demand and value#

  1. Add vector.read nodes for watersheds.gpkg and subwatersheds.gpkg and connect them to watersheds and subwatersheds.
  2. Add table.read nodes for demand.csv and valuation.csv and connect them to demand and valuation. The valuation needs the demand (it values the realized supply), and both need the watersheds.
lulc (raster.read)1lulcraster.readrasterraster: rasterpath = "lulc_2020.tif"biophysical (table.read)2biophysicaltable.readtabletable: tablepath = "biophysical_wy.csv"watersheds (vector.read)3watershedsvector.readvectorvector: vectortabletable: tablepath = "watersheds.gpkg"subwatersheds (vector.read)4subwatershedsvector.readvectorvector: vectortabletable: tablepath = "subwatersheds.gpkg"demand (table.read)5demandtable.readtabletable: tablepath = "demand.csv"valuation (table.read)6valuationtable.readtabletable: tablepath = "valuation.csv"water (services.water_yield)7waterservices.water_yieldlulc: maplulcbiophysical: tablebiophysicalwatersheds: vectorwatershedssubwatersheds: vectorsubwatershedsdemand: tabledemandvaluation: tablevaluationwyieldwyield: mapaetaet: mappetpet: mapfractpfractp: mapdemand_mapdemand_map: mapwatershed_tablewatershed_table: tablesubwatershed_tablesubwatershed_table: tableseasonality = 10write (table.write)8writetable.writetable: tabletablepathpath: textpath = "watersheds_2020.csv"
The full model: watersheds and sub-watersheds, the demand and the valuation for hydropower; the table of the watersheds written to a file. The climate and soil readers of step 1 are left out of the drawing.
Model file (TOML)
format = "terraflux/1"

[[nodes]]
id = "lulc"
op = "raster.read"
params = { path = "lulc_2020.tif" }

[[nodes]]
id = "biophysical"
op = "table.read"
params = { path = "biophysical_wy.csv" }

[[nodes]]
id = "watersheds"
op = "vector.read"
params = { path = "watersheds.gpkg" }

[[nodes]]
id = "subwatersheds"
op = "vector.read"
params = { path = "subwatersheds.gpkg" }

[[nodes]]
id = "demand"
op = "table.read"
params = { path = "demand.csv" }

[[nodes]]
id = "valuation"
op = "table.read"
params = { path = "valuation.csv" }

[[nodes]]
id = "water"
op = "services.water_yield"
inputs = { lulc = "lulc.raster", biophysical = "biophysical.table", watersheds = "watersheds.vector", subwatersheds = "subwatersheds.vector", demand = "demand.table", valuation = "valuation.table" }
params = { seasonality = 10 }
note = "precipitation, eto, depth and pawc as in step 1"

[[nodes]]
id = "write"
op = "table.write"
inputs = { table = "water.watershed_table" }
params = { path = "watersheds_2020.csv" }

Now the node also gives demand_map (each cell's consumption, m³ per year), watershed_table and subwatershed_table.

The consumption of water per cell
The output demand_map: the demand table drawn on the land use map, in m³ per year and cell: 900 in the town, 150 in the irrigated cropland, 5 in pasture, 0 elsewhere. Summed over a watershed it is the consumption of the watershed table.

Read the results#

The maps#

ClassRain (mm)AET (mm)Yield (mm)AET / P
Forest1,5638527110.55
Pasture1,4677657020.53
Cropland1,5097048060.47
Eucalyptus1,5039565470.64
Urban1,4183651,0530.26
Water1,3761,2301460.9
Means over the cells of each class. Forest yields about as much as pasture here although it evaporates a larger share of its rain, because it grows on the wetter ridges; compare the shares (AET / P), not the yields, to see the effect of the class. Eucalyptus evaporates the largest share of the vegetation.

This table shows a trap of reading the maps: a class's yield mixes the effect of the class with the effect of where it grows. To isolate the class, compare scenarios on the same climate (below), not classes in one map.

The watersheds#

WatershedRain (mm)AET (mm)Yield (mm)Yield (Mm³)Consumed (Mm³)Realized supply (Mm³)Energy (MWh)NPV (thousands)
11,481.6792.7688.912.590.5612.02627.9401.4
21,506797.7708.38.770.568.21429190.2
The output watershed_table for 2020, volumes in millions of m³ a year (Mm³), energy in MWh a year, the net present value in thousands. Watershed 1 (1,827 ha) yields 12.6 million m³ a year, of which 0.56 million are consumed; at 40 m of head the realized supply would produce 628 MWh a year.

The columns: the means of rain (precip_mn), PET, AET and yield (wyield_mn) over the cells of the watershed, in mm; the volume of yield (wyield_vol, m³: the mean yield times the area of the polygon); with a demand, the consumption (consum_vol, m³; consum_mn, m³ per cell) and the realized supply (rsupply_vl, m³; rsupply_mn, m³ per cell); with a valuation, the energy (hp_energy, kWh per year) and its net present value (hp_val). The table of the sub-watersheds has the same columns without the valuation:

Sub-watershedRain (mm)Yield (mm)Yield (Mm³)Consumed (Mm³)Realized supply (Mm³)
11,564.3760.96.120.185.94
21,416.5632.26.460.386.08
31,572.2758.84.140.153.99
41,453.8668.44.630.44.22
The output subwatershed_table: the upper sub-watersheds (1 and 3) yield more per hectare (more rain) and consume less; the lower ones (2 and 4) hold the town and most of the cropland.

Reporting water yield

Report the sources of \(P\), \(\text{ET}_0\) and the soils, the biophysical table, \(Z\) and how you chose it, and the yield (mm and m³) of each watershed for each scenario. Compare with measured flows where gauges exist, and say how far apart they are.

The scenario of 2040#

Run the same model with lulc_2040.tif:

Change of water yield, 2020 to 2040
The change of water yield from 2020 to 2040 (mm / year): more where forest became pasture or cropland (green), less where pasture and crops became forest along the rivers or plantation (brown).
WatershedQuantity20202040
1Water yield (million m³)12.58513.061
1Consumption (million m³)0.5611.037
1Realized supply (million m³)12.02412.024
1Hydropower (MWh / year)627.935627.967
1Hydropower, net present value (millions)0.4010.401
2Water yield (million m³)8.779.168
2Consumption (million m³)0.5551.145
2Realized supply (million m³)8.2158.024
2Hydropower (MWh / year)429.025419.02
2Hydropower, net present value (millions)0.190.18
The two watersheds in 2020 and 2040. Clearing forest raises the yield by 4%, but the larger town and the irrigated cropland consume almost all of the gain: the realized supply, and the hydropower, barely change (watershed 1) or fall (watershed 2).

This is the value of going from supply to service: the yield alone says the scenario is good for water, the realized supply says the gain goes to the new consumers, not to the reservoir.

Settings that change the answer#

The seasonality constant Z#

\(Z\) is the model's only free parameter, and the one to calibrate. InVEST's guide (Natural Capital Alliance, 2026) gives its typical range as 1 to 30 and, after Donohue et al. (2012), suggests estimating it as \(0.2\,N\), where \(N\) is the number of rain events in a year: a place with 50 rain events would have \(Z \approx 10\). Where gauges exist, choose the \(Z\) whose yield best matches the measured flow.

Seasonality constant Z 1
Seasonality constant Z 3
Seasonality constant Z 5
Seasonality constant Z 10
Seasonality constant Z 15
Seasonality constant Z 20
Seasonality constant Z 30
Water yield of 2020 for Z from 1 to 30, on one colour scale. A larger Z means more of the plant available water is used, more evaporation and less yield. The chart shows the mean yield of forest, pasture and cropland: forest responds most, because its deep roots give it the most available water.

The yield of the landscape halves from \(Z = 1\) to \(Z = 30\), and the difference between classes changes too: at \(Z = 1\) forest yields more than pasture, at \(Z = 30\) much less. So \(Z\) can change not only how much water a scenario yields but whether it yields more or less than another. Report \(Z\), and test a range.

Precision#

InVEST computes with the type of its rasters: 32-bit floating point numbers for float32 files, 64-bit for float64 ones. Terraflux does the same by default (precision = "auto": 64-bit when a raster holds a value that 32 bits cannot), so that its maps are InVEST's bit for bit; float32 or float64 force one. The difference is far below anything that matters: here 0.0003 mm at most.

Common pitfalls#

  • Annual values. \(P\) and \(\text{ET}_0\) are annual totals (mm per year), not monthly means.
  • Depth in millimetres. Soil depth and root depth are in mm; a soil map in cm or m gives a soil 10 or 1,000 times too thin.
  • PAWC as a percentage. It is a fraction from 0 to 1.
  • lulc_veg for towns and water. Mark them 0, or the model sends them along Budyko's curve with their roots.
  • Watersheds without ws_id. The polygons need the attribute (subws_id for the sub-watersheds); the operator says which is missing.
  • Demand per cell. The demand is in m³ per year per cell, so it depends on the size of the cells: a demand per hectare must be multiplied by the area of a cell (0.09 ha for 30 m).
  • Reading more yield as better. More annual yield can mean more erosion and floods, and the model cannot see the dry season.

Going further#

References#

  • Budyko, M. I. (1974). Climate and Life. Academic Press.
  • Donohue, R. J., Roderick, M. L., & McVicar, T. R. (2012). Roots, storms and soil pores: Incorporating key ecohydrological processes into Budyko's hydrological model. Journal of Hydrology, 436–437, 35–50. https://doi.org/10.1016/j.jhydrol.2012.02.033
  • Fu, B. P. (1981). On the calculation of the evaporation from land surface (in Chinese). Scientia Atmospherica Sinica, 5, 23–31.
  • Hamel, P., & Guswa, A. J. (2015). Uncertainty analysis of a spatially explicit annual water-balance model: case study of the Cape Fear basin, North Carolina. Hydrology and Earth System Sciences, 19(2), 839–853. https://doi.org/10.5194/hess-19-839-2015
  • Natural Capital Alliance. (2026). InVEST 3.20.2 User's Guide: Annual water yield. https://storage.googleapis.com/releases.naturalcapitalproject.org/invest-userguide/latest/en/annual_water_yield.html
  • Redhead, J. W., Stratford, C., Sharps, K., Jones, L., Ziv, G., Clarke, D., Oliver, T. H., & Bullock, J. M. (2016). Empirical validation of the InVEST water yield ecosystem service model at a national scale. Science of the Total Environment, 569–570, 1418–1426. https://doi.org/10.1016/j.scitotenv.2016.06.227
  • Zhang, L., Hickel, K., Dawes, W. R., Chiew, F. H. S., Western, A. W., & Briggs, P. R. (2004). A rational function approach for estimating mean annual evapotranspiration. Water Resources Research, 40(2). https://doi.org/10.1029/2003WR002710

Suggest a change to this page