This is a very simple model of soil hydraulic properties intended to be used for teaching and learning about land-surface climate.
Soil matric potential (“suction”) and hydraulic conductivity depend strongly on mopisture content as described by Clapp and Hornberger (1978) and on soil texture as described by Cosby et al (1984). This website allows the user to explore those relationsips.
Soil texture is characterized as a percentage content of sand, silt, and clay. Cosby et al (1984) related porosity (\(\theta_{sat}\)), hydraulic conductivity (\(K_{sat}\)) and matric potential at saturation (\(\Psi_{sat}\)) to soil texture. This calculator usses the values rom their Table 4. Following Bonan’s textbook, we find the volumetric water content at field capacity and wilt point by setting soil suction to 1000 mm for field capacity and 150,000 mm for wilt point.
Capp and Hornberger (1978) used power laws to estimate hydraulic conductivity and suction from the saturated values given volumetric water content. Given \(\theta < \theta_{sat}\), \[ \begin{aligned} \Psi = \Psi_{sat} (\frac{\theta}{\theta_{sat}})^{-b} \end{aligned} \] \[ \begin{aligned} k = k_{sat} (\frac{\theta}{\theta_{sat}})^{2b+3} \end{aligned} \]
The program that does the calculation is very simple, and the code that controls this website is surprisingly simple too! It is all written in the programming language R. You can read all about it on the “Website Code” tab to the right.
This website is controlled using the R package “shiny.” There are six important components:
Scroll down or click links in the list above to read all about it!
cosby.R:
cosby <- function(percent.clay=18, percent.sand=43) {
# Calculate soil hydraulic parameters based on Cosby et al (1984)
# (Coefficients are derived from Cosby et al Table 4)
# vwc is volumetric water content (m^3 of water per m^3 of soil)
# suction is matric.potential (soil suction)
# Density of water
rho.water <- 1000. # kg per m^3
# Acceleration of gravity
gravity <- 9.81 # meters per second
# Make sure texture percentages sum to 100
percent.silt <- 100. - (percent.clay + percent.sand)
##################
# Coefficients from Cosby et al (1984) Table 4:
bee.intercept <- 3.10
d.bee.d.clay <- 0.1570
d.bee.d.sand <- -0.003
suction.sat.intercept <- 1.54
dLog.suction.sat.d.sand <- -0.0095
dLog.suction.sat.d.silt <- 0.0063
k.sat.intercept <- -0.60
dLog.k.sat.d.sand <- 0.0126
dLog.k.sat.d.clay <- -0.0064
porosity.intercept <- 50.5
d.porosity.d.clay <- -0.037
d.porosity.d.sand <- -0.142
##################
# Clapp and Hornberger "b" exponent
bee = bee.intercept + d.bee.d.clay * percent.clay + d.bee.d.sand * percent.sand
# Saturated soil water suction psi_s (mm)
suction.sat = 10 * (10^(suction.sat.intercept +
dLog.suction.sat.d.sand * percent.sand +
dLog.suction.sat.d.silt * percent.silt))
# Saturated soil hydraulic conductivity K_s (mm per hour)
hydraulic.cond.sat = 25.4 * 10^(k.sat.intercept +
dLog.k.sat.d.sand * percent.sand +
dLog.k.sat.d.clay * percent.clay)
# Volumetric soil water concentration at saturation point θs (porosity)
vwc.sat = 0.01 * (porosity.intercept +
d.porosity.d.clay * percent.clay +
d.porosity.d.sand * percent.sand)
# Volumetric soil moisture concentration at Field Capacity (Bonan Fig 9.8)
suction.field.capacity <- 1000. # mm
vwc.field.capacity <- vwc.sat * (suction.field.capacity / suction.sat) ^ (-1/bee)
# Volumetric soil moisture concentration at wilting point (Bonan Fig 9.8)
suction.wilt <- 150000 # mm
vwc.wilt <- vwc.sat * (suction.wilt / suction.sat) ^ (-1/bee)
return(list(bee=bee, suction.sat=suction.sat, hydraulic.cond.sat=hydraulic.cond.sat,
vwc.sat=vwc.sat, vwc.field.capacity=vwc.field.capacity, vwc.wilt=vwc.wilt))
}
clapp.and.hornberger.R:
clapp.and.hornberger <- function(vwc=.4, percent.sand=43, percent.clay=18){
# Use the method of Clapp and Hornberger (1978) to estimate the soil
# matric potential ("suction") and hydraulic conductivity of a soil
# given the texture (%sand and %clay) and the volumetric water content (vwc)
# First compute soil properties from texture using Cosby et al (1984)
soil <- cosby(percent.sand=percent.sand, percent.clay=percent.clay)
# soil suction as a function of vwc
suction <- soil$suction.sat * (vwc / soil$vwc.sat)^-soil$bee
# hydraulic conductivity as a function of vwc
hydraulic.cond <- soil$hydraulic.cond.sat *
(vwc / soil$vwc.sat)^(2 * soil$bee + 3)
return(list(vwc=vwc, percent.sand=percent.sand,
percent.silt=(100-(percent.sand+percent.clay)),
percent.clay=percent.clay,
suction=suction, hydraulic.cond=hydraulic.cond,
bee=soil$bee, suction.sat=soil$suction.sat,
hydraulic.cond.sat=soil$hydraulic.cond.sat,
vwc.sat=soil$vwc.sat, vwc.field.capacity=soil$vwc.field.capacity,
vwc.wilt=soil$vwc.wilt))
}
report.output.R:
report.output <- function(soil){
attach(soil)
output.df <- data.frame(Sand=c(format(percent.sand,digits=3), '%'),
Silt=c(format(percent.silt,digits=3), '%'),
Clay=c(format(percent.clay,digits=3), '%'),
VWC=c(format(vwc,digits=3), 'm3/m3'),
Porosity=c(format(100*vwc.sat,digits=3), '%'),
Matric.Potential=c(format(suction,digits=4), 'mm'),
Hydraulic.Conductivity=c(format(hydraulic.cond,digits=4), 'mm/hr'),
b.exponent=c(format(bee,digits=4), ' '),
Matric.Potential.at.Saturation=c(format(suction.sat,digits=4),'mm'),
Hydraulic.Conductivity.at.Saturation=c(format(hydraulic.cond.sat, digits=4), 'mm/hr'),
Field.Capacity=c(format(vwc.field.capacity, digits=3), 'm3/m'),
Wilt.Point=c(format(vwc.wilt, digits=3), 'm3/m'))
detach(soil)
return(t(output.df))
}
plot.soil.R:
plot.soil <- function(vwc=0.4, percent.clay=27, percent.sand=43){
# Plot matric potential and hydraulic conductivity as a function of
# volumetric percent of saturation
# Calculate the osil hydraulic parameters based on texture from Cosby et al (1984)
soil <- cosby(percent.sand=percent.sand, percent.clay=percent.clay)
# Use Clapp and Hornberger power laws to get hydraulic properties vs water content
suction <- soil$suction.sat * seq(.05,1,.05) ^ -soil$bee
cond <- soil$hydraulic.cond.sat * seq(.05,1,.05)^(2 * soil$bee + 3)
# Make two side-to-side plots of properties vs water content
# Remember original plot parameters
orig.par <- par(no.readonly=TRUE)
# Set up a multifigure page with two rows in one column
par(mfrow=c(1,2))
# Plot the matric potential ("suction") as a function of saturation
plot(seq(5,100,5), suction, log='y', typ='b', xlim=c(0,100),
cex.axis=1.3, cex.lab=1.2, lwd=3,
xlab='percent of saturation', ylab='suction (mm)')
# Place a vertical line at the current percent of saturation
percent.sat <- 100 * (vwc / soil$vwc.sat)
abline(v=percent.sat, col='red', lwd=2)
# Plot the hydraulic conductivity as a function of saturation
plot(seq(5,100,5), cond, log='y', typ='b', xlim=c(0,100),
cex.axis=1.3, cex.lab=1.2, lwd=3,
xlab='percent of saturation', ylab='conductivity (mm/s)')
abline(v=percent.sat, col='red', lwd=2)
# Restore original plot parameters
par(orig.par)
}
ui.R:
library(shiny)
library(markdown)
# Define UI for the bucket model of surface hydrology
shinyUI(pageWithSidebar(
# Application title
headerPanel("Soil Hydraulic Properties"),
# Sidebar with sliders that control various available options
sidebarPanel(
img(src='soil.jpg'),
wellPanel(
sliderInput("percent.sand", "Percent Sand:",
min = 0, max = 100, value = 43, step=1),
sliderInput("percent.clay", "Percent Clay:",
min = 0, max = 100, value = 27, step=1),
sliderInput("vwc", "Volumetric Water Content (m3/m3):",
min=0, max=0.5, value=0.35, step=0.01)
)
),
# Main panel consists of a tabset displays model output or model description
mainPanel(
tabsetPanel(
tabPanel("Output",
h3("Hydraulic Properties vs. Water Content"),
plotOutput("hydraulic.properties.plot"),
h4("Properties of Specified Soil"),
tableOutput('soil.properties'),
tags$style(type="text/css", ".tab-content { overflow: visible; }")),
tabPanel("Description",
includeHTML('doc/soil.description.html')),
tabPanel("Website Code",
includeMarkdown('doc/soil.code.md'))
)
)
))
server.R:
library(shiny)
# Source required R scripts
source('model/cosby.R')
source('model/clapp.and.hornberger.R')
source('model/plot.soil.R')
source('model/report.output.R')
shinyServer(function(input, output) {
# Calculate the full suite of hydraulic properties for the soil
output$soil.properties <-
renderTable(expr=report.output(clapp.and.hornberger(
vwc=input$vwc,
percent.sand=input$percent.sand,
percent.clay=input$percent.clay)),
include.colnames=FALSE)
# Plot the values of suction and conductivity vs soil moisture
output$hydraulic.properties.plot <-
renderPlot(
plot.soil(vwc=input$vwc,
percent.sand=input$percent.sand,
percent.clay=input$percent.clay)
)
})