# Import modules
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.special import erfc
67 Soil water retention curves
A soil water retention curve represents the relationship between the amount of water in the soil typically expressed on a volume basis (i.e., volumetric soil water content) and the energy-state of the soil water expressed in units of energy per unit volume (J/m^3), energy per unit mass (J/kg), or units of pressure/tension (kPa). This curve is fundamental for understanding soil water storage and flow in porous media in applications like irrigation management, drainage, ground water recharge, and soil water availability to plants.
The shape of the curve is largely dictated by soil physical properties such as texture, structure, and organic matter that control the surface area and pore size distribution of the soil. Soil water retention curves are usually determined empirically by collecting soil samples and using a series of laboratory instruments to quantify the soil water content at different tension levels. In this exercise we will fit common models used in the scientific literature to a dataset determined in our soil physics lab.
# Read dataset
= pd.read_csv('../datasets/soil_water_retention_curve.csv')
df 'theta'] = df['theta']/100
df[ df.head()
matric | theta | |
---|---|---|
0 | 0.001 | 0.448 |
1 | 0.001 | 0.448 |
2 | 0.056 | 0.448 |
3 | 0.173 | 0.447 |
4 | 0.286 | 0.447 |
Define soil water retetnion models
Brooks and Corey model (1964)
\frac{\theta - \theta_r}{\theta_s - \theta_r} = \Bigg( \frac{\psi_e}{\psi} \Bigg)^\lambda
where:
\theta is the volumetric water content (cm^3/cm^3)
\theta_r is the residual water content (cm^3/cm^3)
\theta_s is the saturation water content (cm^3/cm^3)
\psi is the matric potential (kPa)
\psi_e is the air-entry suction (kPa)
\lambda is a parameter related to the pore-size distribution
def brooks_corey_model(x,alpha,lmd,psi_e,theta_r,theta_s):
"""
Function that computes volumetric water content from soil matric potential
using the Brooks and Corey (1964) model.
"""
= np.minimum(theta_r + (theta_s-theta_r)*(psi_e/x)**(lmd), theta_s)
theta return theta
van Genuchten model (1980)
\frac{\theta - \theta_r}{\theta_s - \theta_r} = [1 + (-\alpha \psi)^n]^{-m}
where:
\alpha is a fitting parameter inversely related to \psi_e (kPa^{-1})
n is a fitting parameter
m is a fitting parameter that is often assumed to be M=1-1/n, but in this example it was left as a free parameter (see article by Groenevelt and Grant (2004) for mo details.
def van_genuchten_model(x,alpha,n,m,theta_r,theta_s):
"""
Function that computes volumetric water content from soil matric potential
using the van Genuchten (1980) model.
"""
= theta_r + (theta_s-theta_r)*(1+(alpha*x)**n)**-(1-1/n)
theta return theta
Kosugi model (1994)
\theta = \theta_r + \frac{1}{2} (\theta_s - \theta_r) \ erfc \Bigg( \frac{ln(\psi/ \psi_m)}{\sigma \sqrt(2)} \Bigg)
where:
erfc is the complementary error function
\sigma is the standard deviation of ln(\psi_{med})
\psi_m is the median matric potential
def kosugi_model(x,hm,sigma,theta_r,theta_s):
"""
Function that computes volumetric water content from soil matric potential
using the Kosugi (1994) model. Function was implemented accoridng
to Eq. 3 in Pollacco et al., 2017.
"""
= theta_r + 1/2*(theta_s-theta_r)*erfc(np.log(x/hm)/(sigma*np.sqrt(2)))
theta return theta
Groenvelt-Grant model (2004)
\theta = k_1 \Bigg[ exp\Bigg( \frac{-k_0}{\big(10^{5.89}\big)^n} \Bigg) - exp\Bigg( \frac{-k_0}{\psi ^n} \Bigg) \Bigg]
where:
k_0, k_1, and n are fitting parameters. The value 10^{5.89} is the matric potential in kPa at oven-dry conditions (105 degrees Celsius)
def groenevelt_grant_model(x,k0,k1,n,theta_s):
"""
Function that computes volumetric water content from soil matric potential
using the Groenevelt-Grant (2004) model.
"""
= k1 * ( np.exp(-k0/ (10**5.89)**n) - np.exp(-k0/(x**n)) ) # Eq. 5 in Groenevelt and Grant, 2004
theta return theta
Define error models
# Error models
= lambda x,y: np.round(np.mean(np.abs(x-y)),3)
mae_fn = lambda x,y: np.round(np.sqrt(np.mean((x-y)**2)),3) rmse_fn
Fit soil water retention models to dataset
# Define variables
= df["matric"]
x_obs = df["theta"]
y_obs = np.logspace(-1.5,5,1000)
x_curve
# Empty list to collect output of each model
= []
output
# van Genuchten model
= [0.02,1.5,1,0.1,0.5]
p0 = ([0.001,1,0,0,0.3], [1,10,25,0.3,0.6])
bounds = curve_fit(van_genuchten_model, x_obs, y_obs, p0=p0, bounds=bounds)
par_opt, par_cov = van_genuchten_model(x_curve, *par_opt)
y_curve 'name':'van Genuchten',
output.append({'y_curve':y_curve,
'mae':mae_fn(van_genuchten_model(x_obs, *par_opt), y_obs),
'rmse':rmse_fn(van_genuchten_model(x_obs, *par_opt), y_obs),
'par_values':par_opt,
'par_names':van_genuchten_model.__code__.co_varnames,
'color':'black'})
# Brooks and Corey
=[0.02, 1, 10, 0.1, 0.5]
p0=([0.001, 0.1, 1, 0, 0.3], [1, 10, 100, 0.3, 0.6])
bounds= curve_fit(brooks_corey_model, x_obs, y_obs, p0=p0, bounds=bounds)
par_opt, par_cov = brooks_corey_model(x_curve, *par_opt)
y_curve 'name':'Brooks and Corey',
output.append({'y_curve':y_curve,
'mae':mae_fn(brooks_corey_model(x_obs, *par_opt), y_obs),
'rmse':rmse_fn(brooks_corey_model(x_obs, *par_opt), y_obs),
'par_values':par_opt,
'par_names':brooks_corey_model.__code__.co_varnames,
'color':'tomato'})
# Kosugi
=[50, 1, 0.1, 0.5]
p0=([1, 1, 0, 0.3], [500, 10, 0.3, 0.6])
bounds= curve_fit(kosugi_model, x_obs, y_obs, p0=p0, bounds=bounds)
par_opt, par_cov = kosugi_model(x_curve, *par_opt)
y_curve 'name':'Kosugi',
output.append({'y_curve':y_curve,
'mae':mae_fn(kosugi_model(x_obs, *par_opt), y_obs),
'rmse':rmse_fn(kosugi_model(x_obs, *par_opt), y_obs),
'par_values':par_opt,
'par_names':kosugi_model.__code__.co_varnames,
'color':'darkgreen'})
# Groenevelt-Grant
=[5, 1, 2, 0.5]
p0=([1, 0.1, 0.1, 0.3], [2000, 10, 5, 0.6])
bounds= curve_fit(groenevelt_grant_model, x_obs, y_obs, p0=p0, bounds=bounds)
par_opt, par_cov = groenevelt_grant_model(x_curve, *par_opt)
y_curve 'name':'Groenevelt-Grant',
output.append({'y_curve':y_curve,
'mae':mae_fn(groenevelt_grant_model(x_obs, *par_opt), y_obs),
'rmse':rmse_fn(groenevelt_grant_model(x_obs, *par_opt), y_obs),
'par_values':par_opt,
'par_names':groenevelt_grant_model.__code__.co_varnames,
'color':'lightblue'})
# Create figure
# Plot results
=(6,4))
plt.figure(figsizefor model in output:
'y_curve'], color=model['color'],
plt.plot(x_curve, model[=1.5, label=model['name'])
linewidth='o', facecolor='w',
plt.scatter(x_obs, y_obs, marker=1, edgecolor='k', zorder=10, label='Observations')
alpha'log')
plt.xscale('$|\psi_m|$ (kPa)', size=12)
plt.xlabel('Volumetric water content (cm$^3$/cm$^3$)', size=12)
plt.ylabel(=12)
plt.xticks(fontsize=12)
plt.yticks(fontsize0.01, 100_000])
plt.xlim([
plt.legend() plt.show()
# Print parameters
for k,model in enumerate(output):
print(model['name'])
for par_name, par_value in list(zip(model['par_names'][1:], model['par_values'])):
print(par_name, '=', par_value)
print('')
van Genuchten
alpha = 0.03982164458016106
n = 1.4290698142534732
m = 15.450501882131682
theta_r = 3.797456144597235e-21
theta_s = 0.4376343934467054
Brooks and Corey
alpha = 0.02
lmd = 0.25592618085795293
psi_e = 10.068459416938797
theta_r = 2.402575753992586e-21
theta_s = 0.430430826679669
Kosugi
hm = 122.18916515441454
sigma = 1.962113379238266
theta_r = 4.989241799635874e-22
theta_s = 0.44660152453875435
Groenevelt-Grant
k0 = 8.104570970997068
k1 = 0.44363145088075745
n = 0.502979559118283
theta_s = 0.5
References
Brooks, R. H. (1965). Hydraulic properties of porous media. Colorado State University.
Groenevelt, P.H. and Grant, C.D., 2004. A new model for the soil‐water retention curve that solves the problem of residual water contents. European Journal of Soil Science, 55(3), pp.479-485.
Kosugi, K. I. (1994). Three‐parameter lognormal distribution model for soil water retention. Water Resources Research, 30(4), 891-901.
Pollacco, J. A. P., Webb, T., McNeill, S., Hu, W., Carrick, S., Hewitt, A., & Lilburne, L. (2017). Saturated hydraulic conductivity model computed from bimodal water retention curves for a range of New Zealand soils. Hydrology and Earth System Sciences, 21(6), 2725-2737.
van Genuchten, M.T., 1980. A closed form equation for predicting hydraulic conductivity of unsaturated soils: Journal of the Soil Science Society of America.