Calibration of potential evapotranspiration methods#
M. Vremec, R.A. Collenteur University of Graz, 2021
In this notebook it is shown how to calibrate various (potential) evapotranspiration (PET) equations, using a linear regression relationship between daily potential evapotranspiration obtained with the FAO-56 equation and daily PET estimates obtained with the alternative methods. This notebook requires Scipy and SkLearn python packages to run.
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import least_squares
from sklearn.metrics import mean_squared_error
import pyet as pyet
1. Load KNMI Data#
data = pd.read_csv("data/example_3/etmgeg_260.txt", skiprows=46, delimiter=",",
skipinitialspace=True, index_col="YYYYMMDD", parse_dates=True).loc["2018",:]
data.head()
| # STN | DDVEC | FHVEC | FG | FHX | FHXH | FHN | FHNH | FXX | FXXH | ... | VVNH | VVX | VVXH | NG | UG | UX | UXH | UN | UNH | EV24 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| YYYYMMDD | |||||||||||||||||||||
| 2018-01-01 | 260 | 225 | 45 | 50 | 90 | 2 | 10 | 18 | 190.0 | 2.0 | ... | 2.0 | 75.0 | 3.0 | 7.0 | 84 | 96 | 17 | 73 | 1 | 3 |
| 2018-01-02 | 260 | 216 | 39 | 45 | 80 | 24 | 20 | 1 | 140.0 | 24.0 | ... | 19.0 | 75.0 | 8.0 | 7.0 | 88 | 96 | 3 | 80 | 8 | 2 |
| 2018-01-03 | 260 | 257 | 82 | 88 | 120 | 11 | 70 | 4 | 290.0 | 3.0 | ... | 1.0 | 75.0 | 4.0 | 8.0 | 73 | 95 | 1 | 65 | 9 | 1 |
| 2018-01-04 | 260 | 238 | 51 | 56 | 90 | 20 | 30 | 14 | 180.0 | 20.0 | ... | 14.0 | 80.0 | 21.0 | 8.0 | 82 | 97 | 14 | 67 | 21 | 2 |
| 2018-01-05 | 260 | 225 | 38 | 40 | 60 | 1 | 20 | 17 | 150.0 | 16.0 | ... | 15.0 | 75.0 | 1.0 | 6.0 | 87 | 96 | 17 | 71 | 3 | 2 |
5 rows × 40 columns
2.Estimation of potential evapotranspiration#
# define meteorological variables needed for PE estimation
meteo = pd.DataFrame({"tmean":data.TG/10, "tmax":data.TX/10, "tmin":data.TN/10, "rh":data.UG, "wind":data.FG/10, "rs":data.Q/100})
tmean, tmax, tmin, rh, wind, rs = [meteo[col] for col in meteo.columns]
pressure = data.PG / 100 # to kPa
wind = data.FG/10 # to m/s
lat = 0.91 # [rad]
elevation = 4
pet_fao56 = pyet.pm_fao56(tmean, wind, rs=rs, elevation=elevation, lat=lat,
tmax=tmax, tmin=tmin, rh=rh) # FAO-56 method
pet_romanenko = pyet.romanenko(tmean, rh) # Romanenko method
pet_abtew = pyet.abtew(tmean, rs) # Abtew method
The model performance of the Abtew and Romanenko method will be evaluated using the root mean square error (RMSE), as implemented in SkLearn’s mean_squared_error method.
pet_fao56.plot()
pet_romanenko.plot()
pet_abtew.plot()
plt.ylabel("PE [mm/day]")
print("RMSE(Romanenko) = {} mm/d".format(mean_squared_error(pet_fao56, pet_romanenko, squared=False)))
print("RMSE(Abtew) = {} mm/d".format(mean_squared_error(pet_fao56, pet_abtew, squared=False)))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[4], line 5
1 pet_fao56.plot()
2 pet_romanenko.plot()
3 pet_abtew.plot()
4 plt.ylabel("PE [mm/day]")
----> 5 print("RMSE(Romanenko) = {} mm/d".format(mean_squared_error(pet_fao56, pet_romanenko, squared=False)))
6 print("RMSE(Abtew) = {} mm/d".format(mean_squared_error(pet_fao56, pet_abtew, squared=False)))
File ~/checkouts/readthedocs.org/user_builds/pyet/envs/dev/lib/python3.11/site-packages/sklearn/utils/_param_validation.py:196, in validate_params.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
193 func_sig = signature(func)
195 # Map *args/**kwargs to the function signature
--> 196 params = func_sig.bind(*args, **kwargs)
197 params.apply_defaults()
199 # ignore self/cls and positional/keyword markers
File ~/.asdf/installs/python/3.11.14/lib/python3.11/inspect.py:3195, in Signature.bind(self, *args, **kwargs)
3190 def bind(self, /, *args, **kwargs):
3191 """Get a BoundArguments object, that maps the passed `args`
3192 and `kwargs` to the function's signature. Raises `TypeError`
3193 if the passed arguments can not be bound.
3194 """
-> 3195 return self._bind(args, kwargs)
File ~/.asdf/installs/python/3.11.14/lib/python3.11/inspect.py:3184, in Signature._bind(self, args, kwargs, partial)
3182 arguments[kwargs_param.name] = kwargs
3183 else:
-> 3184 raise TypeError(
3185 'got an unexpected keyword argument {arg!r}'.format(
3186 arg=next(iter(kwargs))))
3188 return self._bound_arguments_cls(self, arguments)
TypeError: got an unexpected keyword argument 'squared'
3. Calibration of different PE equations#
The least squares approach is applied to estimate the parameters in the PE equations, by minimizing the sum of the residuals between the simulated (Abtew and Romanenko) and observed (FAO-56) data. The minimization of the objective function is done using the Trust Region Reflective algorithm, as implemented in Scipy’s least squares method.
3.1 Romanenko method#
# Define function for computing residuals
def cal_romanenko(k, obs):
return pyet.romanenko(tmean, rh, k)-obs
# estimate k in the Romanenko method
x0 = 4.5 # initial estimate of parameter
res_1 = least_squares(cal_romanenko, x0, args=[pet_fao56])
res_1.x
array([3.87304623])
# Compute RMSE using the calibrated value of k
pet_romanenko_cal = pyet.romanenko(tmean, rh, k=res_1.x)
print("RMSE(Romanenko) = {} mm/d".format(mean_squared_error(pet_fao56, pet_romanenko_cal, squared=False)))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[7], line 3
1 # Compute RMSE using the calibrated value of k
2 pet_romanenko_cal = pyet.romanenko(tmean, rh, k=res_1.x)
----> 3 print("RMSE(Romanenko) = {} mm/d".format(mean_squared_error(pet_fao56, pet_romanenko_cal, squared=False)))
File ~/checkouts/readthedocs.org/user_builds/pyet/envs/dev/lib/python3.11/site-packages/sklearn/utils/_param_validation.py:196, in validate_params.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
193 func_sig = signature(func)
195 # Map *args/**kwargs to the function signature
--> 196 params = func_sig.bind(*args, **kwargs)
197 params.apply_defaults()
199 # ignore self/cls and positional/keyword markers
File ~/.asdf/installs/python/3.11.14/lib/python3.11/inspect.py:3195, in Signature.bind(self, *args, **kwargs)
3190 def bind(self, /, *args, **kwargs):
3191 """Get a BoundArguments object, that maps the passed `args`
3192 and `kwargs` to the function's signature. Raises `TypeError`
3193 if the passed arguments can not be bound.
3194 """
-> 3195 return self._bind(args, kwargs)
File ~/.asdf/installs/python/3.11.14/lib/python3.11/inspect.py:3184, in Signature._bind(self, args, kwargs, partial)
3182 arguments[kwargs_param.name] = kwargs
3183 else:
-> 3184 raise TypeError(
3185 'got an unexpected keyword argument {arg!r}'.format(
3186 arg=next(iter(kwargs))))
3188 return self._bound_arguments_cls(self, arguments)
TypeError: got an unexpected keyword argument 'squared'
RMSE (calibrated) = 0.546 < RMSE (uncalibrated) = 0.694
3.2 Abtew method#
# Define function for computing residuals and initial estimate of parameters
def cal_abtew(k,obs):
return pyet.abtew(tmean, rs, k)-obs
x0 = 0.53
# estimate k in the Romanenko method
res_2 = least_squares(cal_abtew, x0, args=[pet_fao56])
res_2.x
array([0.45748986])
pet_abtew_cali = pyet.abtew(tmean, rs, res_2.x)
print("RMSE(Abtew) = {} mm/d".format(mean_squared_error(pet_fao56, pet_abtew_cali, squared=False)))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[10], line 2
1 pet_abtew_cali = pyet.abtew(tmean, rs, res_2.x)
----> 2 print("RMSE(Abtew) = {} mm/d".format(mean_squared_error(pet_fao56, pet_abtew_cali, squared=False)))
File ~/checkouts/readthedocs.org/user_builds/pyet/envs/dev/lib/python3.11/site-packages/sklearn/utils/_param_validation.py:196, in validate_params.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
193 func_sig = signature(func)
195 # Map *args/**kwargs to the function signature
--> 196 params = func_sig.bind(*args, **kwargs)
197 params.apply_defaults()
199 # ignore self/cls and positional/keyword markers
File ~/.asdf/installs/python/3.11.14/lib/python3.11/inspect.py:3195, in Signature.bind(self, *args, **kwargs)
3190 def bind(self, /, *args, **kwargs):
3191 """Get a BoundArguments object, that maps the passed `args`
3192 and `kwargs` to the function's signature. Raises `TypeError`
3193 if the passed arguments can not be bound.
3194 """
-> 3195 return self._bind(args, kwargs)
File ~/.asdf/installs/python/3.11.14/lib/python3.11/inspect.py:3184, in Signature._bind(self, args, kwargs, partial)
3182 arguments[kwargs_param.name] = kwargs
3183 else:
-> 3184 raise TypeError(
3185 'got an unexpected keyword argument {arg!r}'.format(
3186 arg=next(iter(kwargs))))
3188 return self._bound_arguments_cls(self, arguments)
TypeError: got an unexpected keyword argument 'squared'
RMSE (calibrated) = 0.613 < RMSE (uncalibrated) = 0.741
pet_fao56.plot()
pet_romanenko_cal.plot()
pet_abtew_cali.plot()
plt.ylabel("PET [mm/d]");