The rate of photosynthesis is determined by light, CO2, and the temperature-dependent kinetics of the enzyme whose nickname is rubisco, represented here according to the model first described by Farquahr et al (1980) and described in Denning (1993) and Bonan 2008.
We choose the smallest of three potential rates of carboxylation: \(w_c\) (limited by CO2 and rubisco), \(w_j\) (limited by light and electron transport), and \(w_s\) (limited by accumulating starch at the chloroplast). Following Bonan (2008, page 246), these three limiting rates are written as
\[w_c = \frac{V_{max} (c_i - \Gamma_*)}{c_i+K_c(1+O_i/K_o)}\]
\[w_j = \frac{J(c_i - \Gamma_*)}{4(c_i + 2 \Gamma_*)}\]
\[w_s = V_{max} / 2 \]
where \(c_i\) is the partial pressure (in Pascals, roughly ppmv/10) of \(CO_2\) in the leaf chloroplast or intercellular fluid, \(\Gamma_*\) is the \(CO_2\) compensation point (Pascals), \(O_i\) is the partial pressure of oxygen (assumed to be the same as ambient air, 0.209 \(p_{sfc}\)), and \(K_C\) and \(K_o\) are Michaelis-Menten coefficients (Pascals) for carboxylation and oxidation reactions catalyzed by rubisco.
The potential rate of electron transport (J, \(\mu\) mol electrons m\(^{-2}\) s\(^{-1}\)) depends on the amount of photosynthetically-active radiation absorbed by the leaf (\(\phi\), \(\mu\) mol photons m\(^{-2}\) s\(^{-1}\))) and is determined as the smaller of the two roots of the equation
\[ 0.7 J^2 - (J_{max} + 0.385 \phi)J + 0.385 J_{max} \phi = 0 \]
where \(J_{max}\) is the maximum rate of electron transport (\(\mu\) mol electrons m\(^{-2}\) s\(^{-1}\)), which depends on the amount of leaf chlorophyll. The maximum rate of carboxylation \(V_{max}, \mu\) mol CO\(_2\) m\(^{-2}\) s\(^{-1}\)) is proportional to the amount of rubisco in the leaf. Because both rubisco and chlorophyll contain a lot of nitrogen, the parameters \(V_{max}\) and \(J_{max}\) depend critically on the bioavailable nitrogen obtained by the plant during the growth of the leaves.
All the parameters \(K_C\), \(K_o\), \(V_{max}\), and \(J_{max}\) also depend on temperature.
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, using a web programming package called shiny. 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 five important components:
Scroll down or click links in the list above to read all about it!
farquhar.R:
farquhar <- function(V.max=50, J.max=100, APAR=500, c.i=30, temp.C=25) {
# Model inputs:
# V.max = maximum rubisco-limited rate in micromoles per (m^2 sec)
# J.max = maximum light-limited rate in micromoles per (m^2 sec)
# APAR = absorbed photosynthetically-active radiation in micromoles per (m^2 sec)
# c.i = intercellular CO2 partial pressure in Pascals (roughly ppm/10)
# temp.C = leaf temperature (Celsius)
# Return value = net carbon assimilation (micromoles CO2 per m^2 of leaf area per second)
# Some local parameters we need (adjusted for temperature according to Collatz 91)
p.sfc <- 101325 # surface air pressure (Pascals)
O.i <- 0.209 * p.sfc # oxygen partial pressure in chloroplast
# Adjust some parameters for temperature according to Collatz et al (1991) Table A1
tau <- 2600 * 0.57^((temp.C - 25)/10) # CO2/O2 specificity ratio for rubisco
gamma <- O.i / (2 *tau) # CO2 compensation point (Pascals, Collatz A3)
K.c <- 30 * 2.1^((temp.C - 25)/10) # Michaelis constant for carboxylation (Pascals)
K.o <- 30000 * 1.2^((temp.C - 25)/10) # Michaelis constant for oxidation (Pascals)
# Temp-adjusted maximum carboxylation rate
cold <- 10.
hot <- 40.
slope.cold <- .25
slope.heat <- .4
cold.inhibition <- 1 + exp(slope.cold*(cold - temp.C))
heat.inhibition <- 1 + exp(slope.heat*(temp.C - hot))
V.m <- V.max * 2.1 ^ ((temp.C-25)/10) /
(cold.inhibition * heat.inhibition)
# Temp-adjusted leaf respiration
R.d <- 0.015 * V.m * 2.4 ^ ((temp.C-25)/10) /
(1. + exp((1.3*(temp.C - 55))))
# Solution of quadratic (Bonan 17.8)
a <- 0.7
b <- -(J.max + 0.385 * APAR)
c <- 0.385 * J.max * APAR
J.1 <- (-b + sqrt(b^2 - 4 * a * c) ) / (2 * a)
J.2 <- (-b - sqrt(b^2 - 4 * a * c) ) / (2 * a)
J <- min(J.1, J.2)
# Rubisco-limited rate of photosynthesis
w.c <- V.m * (c.i - gamma) / (c.i + K.c * (1 + O.i/K.o)) # Collatz A5 or Bonan 17.6
# Light-limited rate of photosynthesis
w.j <- J * (c.i - gamma) / (4 * (c.i + 2 * gamma)) # Bonan 17.7 or Collatz A2
# Sink-limited rate of photosynthesis # Collatz A7
w.s <- V.m / 2
# Net assimilation
A.n <- min(w.c, w.j, w.s)-R.d
return(A.n)
}
plot.light.response.R:
plot.light.response <- function(V.max=50, J.max=100, c.i=30) {
A.n <- replicate(100, NA)
light <- replicate(100, NA)
for (i in 1:100) {
light[i] <- 10 * i
A.n[i] <- farquhar(V.max=V.max, J.max=J.max, c.i=30, APAR=light[i])
}
orig.par <- par(no.readonly=TRUE)
par(mar=c(5,5,4,2)+0.1)
plot(light, A.n, main='Light Response of Photosynthesis',
xlab=expression(paste('PPFD (',mu, 'mol photons ',m^{-2},s^{-1},')')),
ylab=expression(paste(mu, 'mol ', CO[2], ' ',m^{-2}, s^{-1},)),
cex.main=2, cex.axis=1.5, cex.lab=1.5)
par(orig.par)
}
plot.CO2.response.R:
plot.CO2.response <- function(V.max=50, J.max=100, APAR=500) {
A.n <- replicate(100, NA)
CO2 <- replicate(100, NA)
for (i in 1:100) {
CO2[i] <- 10 * i
A.n[i] <- farquhar(V.max=V.max, J.max=J.max, c.i=CO2[i]/10, APAR=500)
}
orig.par <- par(no.readonly=TRUE)
par(mar=c(5,5,4,2)+0.1)
plot(CO2, A.n, main='CO2 Response of Photosynthesis', xlab='Intercellular CO2 (ppm)',
ylab=expression(paste(mu, 'mol ', CO[2], ' ',m^{-2}, s^{-1},)),
cex.main=2, cex.axis=1.5, cex.lab=1.5)
par(orig.par)
}
ui.R:
library(shiny)
library(markdown)
library(knitr)
# Define user interface for logistic growth model
shinyUI(pageWithSidebar(
# Application title
headerPanel("Farquhar Photosynthesis Model"),
# Sidebar on the left with controls for the user to specify atmospheric properties
sidebarPanel(
img(src='psoverview.gif'),
h4('Set Models Parameter Below'),
# Set drainage times for each pool
sliderInput("V.max", "V.max: Maximum Rubisco-limited rate (micromoles per (m^2 sec))",
min = 1, max = 150, value = 50, step= 1),
sliderInput("J.max", "J.max: Maximum light-limited rate (micromoles per (m^2 sec))",
min = 1, max = 300, value = 100, step= 1),
sliderInput("temp", "Temperature (Celsius)",
min = 1, max = 50, value = 25, step=1)
),
# Show a tabset in the main panel of the browser that displays model output
mainPanel(
tabsetPanel(
# First tab shows plots of assimilation rate vs light and CO2
tabPanel("Output",
h4('Photosynthetic Responses'),
plotOutput('light.response.plot'),
plotOutput('CO2.response.plot')),
# Second tab displays a brief model description
tabPanel("Model Description",
includeHTML('doc/model.description.html')),
# Third tab displays website code
tabPanel("Website code",
includeMarkdown('doc/website.code.md'))
)
)
))
server.R:
library(shiny)
# Source required R scripts
source('model/farquhar.R')
source('model/plot.light.response.R')
source('model/plot.CO2.response.R')
shinyServer(function(input, output) {
# plot of model output
output$light.response.plot <- renderPlot(
plot.light.response(V.max=input$V.max, J.max=input$J.max))
output$CO2.response.plot <- renderPlot(
plot.CO2.response(V.max=input$V.max, J.max=input$J.max))
})