Farquhar Photosynthesis Model

Set Models Parameter Below

Photosynthetic Responses

MODEL DESCRIPTION

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.

How this website works (including all the code!)

This website is controlled using the R package “shiny.” There are five important components:

  • A script (farquhar.R) that contains the enzyme-kinetic model
  • A script (plot.light.response.R) that plots photosynthesis vs light
  • A script (plot.CO2.response.R) that plots photosynthesis vs CO2
  • The user interface (ui.R) that controls the sliders and passes inputs to the model
  • The “server” (server.R) that responds to user interface events in the web browser

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)
}

This is the user interface that actually controls this website

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))

})