├── .Rbuildignore ├── .gitignore ├── inst ├── extdata │ └── aci-curve.xlsx └── CITATION ├── R ├── utils.R ├── hello.R ├── QUADP.R ├── plantecophys-package.R ├── findCiTransition.R ├── Tresponsefunctions.R ├── fitBBs.R ├── tuzet.R ├── RHtoVPD.R ├── AciC4.R ├── fitacis_methods.R ├── fitBB.R ├── fitaci_methods.R ├── fitacis.R ├── LeafEnergyBalance.R ├── FARAO.R ├── photosyn.R └── fitaci.R ├── man ├── hello.Rd ├── acidata1.Rd ├── manyacidat.Rd ├── findCiTransition.Rd ├── fitBBs.Rd ├── PhotosynTuzet.Rd ├── PhotosynEB.Rd ├── Conversions.Rd ├── AciC4.Rd ├── FARAO.Rd ├── fitBB.Rd ├── fitacis.Rd ├── Photosyn.Rd └── fitaci.Rd ├── R_other └── check_functions.R ├── DESCRIPTION ├── plantecophys2.Rproj ├── README.md └── NAMESPACE /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | -------------------------------------------------------------------------------- /inst/extdata/aci-curve.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhujiedong/plantecophys2/main/inst/extdata/aci-curve.xlsx -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | Warning <- function(...)warning(..., call.=FALSE) 2 | Stop <- function(...)stop(..., call.=FALSE) 3 | 4 | 5 | recycle <- function(x, n){ 6 | rep(x, ceiling(n / length(x)))[1:n] 7 | } 8 | -------------------------------------------------------------------------------- /man/hello.Rd: -------------------------------------------------------------------------------- 1 | \name{hello} 2 | \alias{hello} 3 | \title{Hello, World!} 4 | \usage{ 5 | hello() 6 | } 7 | \description{ 8 | Prints 'Hello, world!'. 9 | } 10 | \examples{ 11 | hello() 12 | } 13 | -------------------------------------------------------------------------------- /R_other/check_functions.R: -------------------------------------------------------------------------------- 1 | library(devtools) 2 | check() 3 | document() 4 | check() 5 | install() 6 | 7 | load_all() 8 | 9 | 10 | df <- xls_read("inst/extdata/aci-curve.xlsx") 11 | 12 | fit_df <- fitaci(df, theta = 1.2, alpha = 1) 13 | 14 | plot(fit_df) 15 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | bibentry(bibtype = "article", 2 | title = "{Plantecophys - {An} {R} {Package} for {Analysing} and {Modelling} {Leaf} {Gas} {Exchange} {Data}}", 3 | volume = "10", 4 | doi = "10.1371/journal.pone.0143346", 5 | number = {11}, 6 | journal = "{PLoS ONE}", 7 | author = "Remko A. Duursma", 8 | year = "2015", 9 | pages = "e0143346") 10 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: plantecophys2 2 | Type: Package 3 | Title: What the Package Does (Title Case) 4 | Version: 0.1.0 5 | Author: Who wrote it 6 | Maintainer: The package maintainer 7 | Description: More about what it does (maybe more than one line) 8 | Use four spaces when indenting paragraphs within the Description. 9 | License: What license is it under? 10 | Encoding: UTF-8 11 | LazyData: true 12 | RoxygenNote: 7.1.1 13 | -------------------------------------------------------------------------------- /plantecophys2.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /R/hello.R: -------------------------------------------------------------------------------- 1 | # Hello, world! 2 | # 3 | # This is an example function named 'hello' 4 | # which prints 'Hello, world!'. 5 | # 6 | # You can learn more about package authoring with RStudio at: 7 | # 8 | # http://r-pkgs.had.co.nz/ 9 | # 10 | # Some useful keyboard shortcuts for package authoring: 11 | # 12 | # Install Package: 'Ctrl + Shift + B' 13 | # Check Package: 'Ctrl + Shift + E' 14 | # Test Package: 'Ctrl + Shift + T' 15 | 16 | hello <- function() { 17 | print("Hello, world!") 18 | } 19 | -------------------------------------------------------------------------------- /man/acidata1.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plantecophys-package.R 3 | \docType{data} 4 | \name{acidata1} 5 | \alias{acidata1} 6 | \title{An example A-Ci curve} 7 | \format{ 8 | \describe{ 9 | \item{CO2S}{CO2 concentration in cuvette (ppm)} 10 | \item{Ci}{Intercellular CO2 concentration (ppm)} 11 | \item{Tleaf}{Leaf temperature (deg C)} 12 | \item{Photo}{Net photosynthesis rate (mu mol m-2 s-1)} 13 | } 14 | } 15 | \description{ 16 | CO2 response of leaf photosynthesis, as measured with a Licor6400. 17 | } 18 | -------------------------------------------------------------------------------- /man/manyacidat.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plantecophys-package.R 3 | \docType{data} 4 | \name{manyacidat} 5 | \alias{manyacidat} 6 | \title{An example dataset with multiple A-Ci curves} 7 | \format{ 8 | \describe{ 9 | \item{Curve}{An identifier for the A-Ci curve (28 curves in total, 13-14 points per curve)} 10 | \item{Ci}{Intercellular CO2 concentration (ppm)} 11 | \item{Photo}{Net photosynthesis rate (mu mol m-2 s-1)} 12 | \item{Tleaf}{Leaf temperature (deg C)} 13 | \item{PPFD}{Photosynthetic photon flux density (mu mol m-2 s-1)} 14 | } 15 | } 16 | \description{ 17 | CO2 response of leaf photosynthesis, as measured with a Licor6400, for multiple leaves. 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Citation 2 | 3 | this is a revised version of Duursma, 2015: 4 | 5 | > Duursma, R.A., 2015. Plantecophys - An R Package for Analysing and Modelling Leaf Gas Exchange Data. PLoS ONE 10, e0143346. [doi:10.1371/journal.pone.0143346](). 6 | 7 | the main purpose of the `planecophys2` is to revise the `plantecophys` for LI-6800. 8 | 9 | 1. revise the varname for LI-6800 10 | 11 | 2. Use TleafCond for Tleaf according to the LI-6800 user's manual 12 | 13 | 3. print the summary(nlsfit) directly to check the results of nonlinear fittings 14 | 15 | 4. some small changes for the plot method 16 | 17 | this is very beginning version, it is the same with `plantecophys` except the above small changes. As the LI-6400 is end of life, and the LI-6800 has more advanced functions like RACiR, it is necessay to fit the Aci curve and RACiR with less typing. 18 | 19 | 20 | -------------------------------------------------------------------------------- /R/QUADP.R: -------------------------------------------------------------------------------- 1 | # solves quadratic equation given by y = a + b*x + c*x^2 2 | # Based on MAESTRA equivalent (B. Medlyn) 3 | 4 | # - larger root 5 | QUADP <- function(A,B,C){ 6 | 7 | if((B^2 - 4*A*C) < 0){ 8 | warning("IMAGINARY ROOTS IN QUADRATIC") 9 | return(0) 10 | } 11 | 12 | if(identical(A,0)){ 13 | if(identical(B,0)){ 14 | return(0) 15 | } else { 16 | return(-C/B) 17 | } 18 | } else { 19 | return((- B + sqrt(B^2 - 4*A*C)) / (2*A)) 20 | } 21 | 22 | } 23 | 24 | # - smaller root 25 | QUADM <- function(A,B,C){ 26 | 27 | if((B^2 - 4*A*C) < 0){ 28 | warning("IMAGINARY ROOTS IN QUADRATIC") 29 | return(0) 30 | } 31 | 32 | if(identical(A,0)){ 33 | if(identical(B,0)){ 34 | return(0) 35 | } else { 36 | return(-C/B) 37 | } 38 | } else { 39 | return((- B - sqrt(B^2 - 4*A*C)) / (2*A)) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /man/findCiTransition.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/findCiTransition.R 3 | \name{findCiTransition} 4 | \alias{findCiTransition} 5 | \title{Calculate transition points for fitted A-Ci curves} 6 | \usage{ 7 | findCiTransition(object, ...) 8 | } 9 | \arguments{ 10 | \item{object}{Either an object returned by \code{fitaci}, or a copy of the \code{Photosyn} function.} 11 | 12 | \item{\dots}{Further arguments passed to the \code{Photosyn} function.} 13 | } 14 | \description{ 15 | Calculates the Ci at the transition points between Ac & Aj (point 1), and Aj and Ap (point 2). The latter is not NA only when TPU was estimated (and estimable), see \code{\link{fitaci}}, argument \code{fitTPU}. 16 | } 17 | \details{ 18 | This function is also used by \code{fitaci}, the results are stored in elements \code{Ci_transition} and \code{Ci_transition2}. 19 | } 20 | -------------------------------------------------------------------------------- /R/plantecophys-package.R: -------------------------------------------------------------------------------- 1 | #' An example A-Ci curve 2 | #' @description CO2 response of leaf photosynthesis, as measured with a Licor6400. 3 | #' @name acidata1 4 | #' @docType data 5 | #' @format 6 | #' \describe{ 7 | #' \item{CO2S}{CO2 concentration in cuvette (ppm)} 8 | #' \item{Ci}{Intercellular CO2 concentration (ppm)} 9 | #' \item{Tleaf}{Leaf temperature (deg C)} 10 | #' \item{Photo}{Net photosynthesis rate (mu mol m-2 s-1)} 11 | #' } 12 | NULL 13 | 14 | #' An example dataset with multiple A-Ci curves 15 | #' @description CO2 response of leaf photosynthesis, as measured with a Licor6400, for multiple leaves. 16 | #' @name manyacidat 17 | #' @docType data 18 | #' @format 19 | #' \describe{ 20 | #' \item{Curve}{An identifier for the A-Ci curve (28 curves in total, 13-14 points per curve)} 21 | #' \item{Ci}{Intercellular CO2 concentration (ppm)} 22 | #' \item{Photo}{Net photosynthesis rate (mu mol m-2 s-1)} 23 | #' \item{Tleaf}{Leaf temperature (deg C)} 24 | #' \item{PPFD}{Photosynthetic photon flux density (mu mol m-2 s-1)} 25 | #' } 26 | NULL 27 | -------------------------------------------------------------------------------- /man/fitBBs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitBBs.R 3 | \name{fitBBs} 4 | \alias{fitBBs} 5 | \title{Fit Ball-Berry type models of stomatal conductance to many groups at once} 6 | \usage{ 7 | fitBBs(data, group, ...) 8 | } 9 | \arguments{ 10 | \item{data}{Input dataframe, containing all variables needed to fit the model.} 11 | 12 | \item{group}{Name of the grouping variable in the dataframe (quoted), the model will be fit for each group defined by this variable.} 13 | 14 | \item{\dots}{Further parameters passed to \code{\link{fitBB}}, see there for a full description.} 15 | } 16 | \description{ 17 | A batch utility for the \code{\link{fitBB}} function, to fit the model for each group in a dataframe. 18 | } 19 | \examples{ 20 | \dontrun{ 21 | # If you have a factor variable in your dataset called 'species', and you 22 | # want to fit the Ball-Berry model for each of the species: 23 | myfits <- fitBBs(mydata, "species", model="BallBerry") 24 | 25 | # A dataframe with coefficients is returned by coef() 26 | coef(myfits) 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /man/PhotosynTuzet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tuzet.R 3 | \name{PhotosynTuzet} 4 | \alias{PhotosynTuzet} 5 | \title{Coupled leaf gas exchange model with Tuzet stomatal conductance} 6 | \usage{ 7 | PhotosynTuzet(g1 = 8, Ca = 400, psis = 0, kl = 2, sf = 3, psif = -2, ...) 8 | } 9 | \arguments{ 10 | \item{g1}{The slope parameter. Note that the default value should be much higher than that used in the Medlyn et al. (2011) model to give comparable predictions.} 11 | 12 | \item{Ca}{Atmospheric CO2 concentration.} 13 | 14 | \item{psis}{Soil water potential (MPa). Note that soil-to-root hydraulic conductance is not implemented.} 15 | 16 | \item{kl}{Leaf-specific hydraulic conductance (mmol m-2 s-1 MPa-1)} 17 | 18 | \item{sf}{Shape parameter (-) of sigmoidal function of leaf water potential (see Tuzet et al. 2003)} 19 | 20 | \item{psif}{Leaf water potential at which stomatal conductance is 50\% of maximum (MPa).} 21 | 22 | \item{\dots}{All other arguments in \code{\link{Photosyn}}} 23 | } 24 | \description{ 25 | An implementation of the coupled photosynthesis - stomatal conductance model, using the Tuzet et al. (2003) model of stomatal conductance. Accepts all arguments of \code{\link{Photosyn}} (except \code{gsmodel}, of course). 26 | } 27 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(coef,BBfit) 4 | S3method(coef,BBfits) 5 | S3method(coef,acifit) 6 | S3method(coef,acifits) 7 | S3method(fitted,acifit) 8 | S3method(plot,acifit) 9 | S3method(plot,acifits) 10 | S3method(print,BBfit) 11 | S3method(print,BBfits) 12 | S3method(print,acifit) 13 | S3method(print,acifits) 14 | S3method(summary,acifit) 15 | export(Aci) 16 | export(AciC4) 17 | export(DewtoVPD) 18 | export(FARAO) 19 | export(FARAO2) 20 | export(FindTleaf) 21 | export(Photosyn) 22 | export(PhotosynEB) 23 | export(PhotosynTuzet) 24 | export(RHairToLeaf) 25 | export(RHleafToAir) 26 | export(RHtoVPD) 27 | export(VPDairToLeaf) 28 | export(VPDleafToAir) 29 | export(VPDtoDew) 30 | export(VPDtoRH) 31 | export(esat) 32 | export(findCiTransition) 33 | export(fitBB) 34 | export(fitBBs) 35 | export(fitaci) 36 | export(fitacis) 37 | importFrom(grDevices,palette) 38 | importFrom(grDevices,rainbow) 39 | importFrom(graphics,abline) 40 | importFrom(graphics,legend) 41 | importFrom(graphics,points) 42 | importFrom(graphics,text) 43 | importFrom(stats,coef) 44 | importFrom(stats,lm) 45 | importFrom(stats,median) 46 | importFrom(stats,nls) 47 | importFrom(stats,nls.control) 48 | importFrom(stats,optimize) 49 | importFrom(stats,residuals) 50 | importFrom(stats,uniroot) 51 | importFrom(utils,setTxtProgressBar) 52 | importFrom(utils,txtProgressBar) 53 | -------------------------------------------------------------------------------- /R/findCiTransition.R: -------------------------------------------------------------------------------- 1 | #' Calculate transition points for fitted A-Ci curves 2 | #'@description Calculates the Ci at the transition points between Ac & Aj (point 1), and Aj and Ap (point 2). The latter is not NA only when TPU was estimated (and estimable), see \code{\link{fitaci}}, argument \code{fitTPU}. 3 | #'@param object Either an object returned by \code{fitaci}, or a copy of the \code{Photosyn} function. 4 | #'@param \dots Further arguments passed to the \code{Photosyn} function. 5 | #'@details This function is also used by \code{fitaci}, the results are stored in elements \code{Ci_transition} and \code{Ci_transition2}. 6 | #'@export 7 | findCiTransition <- function(object, ...){ 8 | 9 | if(is.function(object)){ 10 | photofun <- object 11 | } else { 12 | photofun <- object$Photosyn 13 | } 14 | 15 | O <- function(Ci, photofun, point, ...){ 16 | x <- photofun(Ci=Ci, ...) 17 | if(point == 1){ 18 | Ac <- x$Ac - x$Rd 19 | Aj <- x$Aj - x$Rd 20 | return((Ac - Aj)^2) 21 | } 22 | if(point == 2){ 23 | Aj <- x$Aj - x$Rd 24 | Ap <- x$Ap - x$Rd 25 | return((Aj - Ap)^2) 26 | } 27 | } 28 | p1 <- optimize(O, c(25,2000), photofun=photofun, point=1, ...)$minimum 29 | 30 | r <- photofun(Ci=500, ...) 31 | 32 | if(r$Ap >= 1000 | is.na(r$Ap)){ 33 | p2 <- NA 34 | } else { 35 | p2 <- optimize(O, c(400,2000), photofun=photofun, point=2, ...)$minimum 36 | } 37 | 38 | return(c(Ac_Aj=p1, Aj_Ap=p2)) 39 | } 40 | -------------------------------------------------------------------------------- /R/Tresponsefunctions.R: -------------------------------------------------------------------------------- 1 | .Rgas <- function()8.314 2 | Tk <- function(x)x+273.15 3 | 4 | # Arrhenius 5 | arrh <- function(Tleaf, Ea){ 6 | exp((Ea * (Tk(Tleaf) - 298.15)) / (298.15 * .Rgas() * Tk(Tleaf))) 7 | } 8 | 9 | TGammaStar <- function(Tleaf, Patm, 10 | Egamma=37830.0, 11 | value25=42.75){ 12 | 13 | value25*arrh(Tleaf,Egamma)*Patm/100 14 | } 15 | 16 | TKm <- function(Tleaf, Patm, 17 | Oi = 210, # O2 concentration (mmol mol-1) 18 | Ec = 79430.0, # activation energy for Kc 19 | Eo = 36380.0, # activation energy for Ko 20 | Kc25 = 404.9, # Kc at 25C 21 | Ko25 = 278.4 # Ko at 25C 22 | ){ 23 | 24 | Oi <- Oi * Patm / 100 25 | 26 | Ko <- Ko25*arrh(Tleaf, Eo) 27 | Kc <- Kc25*arrh(Tleaf, Ec) 28 | Km <- Kc * (1.0 + Oi / Ko) 29 | 30 | return(Km) 31 | } 32 | 33 | # Vcmax temperature response (Arrhenius) 34 | TVcmax <- function(Tleaf, EaV, delsC, EdVC){ 35 | 36 | if(EdVC > 0){ 37 | V1 <- 1+exp((delsC*(25 + 273.15)-EdVC)/(.Rgas()*(25 + 273.15))) 38 | V2 <- 1+exp((delsC*(Tleaf+273.15)-EdVC)/(.Rgas()*(Tk(Tleaf)))) 39 | f <- V1/V2 40 | } else f <- 1 41 | 42 | exp((Tleaf-25)*EaV/(.Rgas()*Tk(Tleaf)*Tk(25))) * f 43 | } 44 | 45 | # Jmax temperature response (Arrhenius) 46 | TJmax <- function(Tleaf, EaJ, delsJ, EdVJ){ 47 | J1 <- 1+exp((298.15*delsJ-EdVJ)/.Rgas()/298.15) 48 | J2 <- 1+exp((Tk(Tleaf)*delsJ-EdVJ)/.Rgas()/Tk(Tleaf)) 49 | exp(EaJ/.Rgas()*(1/298.15 - 1/Tk(Tleaf)))*J1/J2 50 | } 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /R/fitBBs.R: -------------------------------------------------------------------------------- 1 | #' Fit Ball-Berry type models of stomatal conductance to many groups at once 2 | #'@description A batch utility for the \code{\link{fitBB}} function, to fit the model for each group in a dataframe. 3 | #'@param data Input dataframe, containing all variables needed to fit the model. 4 | #'@param group Name of the grouping variable in the dataframe (quoted), the model will be fit for each group defined by this variable. 5 | #'@param \dots Further parameters passed to \code{\link{fitBB}}, see there for a full description. 6 | #'@export 7 | #'@examples 8 | #'\dontrun{ 9 | #'# If you have a factor variable in your dataset called 'species', and you 10 | #'# want to fit the Ball-Berry model for each of the species: 11 | #'myfits <- fitBBs(mydata, "species", model="BallBerry") 12 | #' 13 | #'# A dataframe with coefficients is returned by coef() 14 | #'coef(myfits) 15 | #' 16 | #'} 17 | fitBBs <- function(data, group, ...){ 18 | 19 | 20 | datasp <- split(data, data[,group]) 21 | 22 | fits <- lapply(datasp, fitBB, ...) 23 | 24 | class(fits) <- "BBfits" 25 | 26 | return(fits) 27 | } 28 | 29 | #' @method coef BBfits 30 | #' @export 31 | coef.BBfits <- function(object, ...){ 32 | 33 | p <- do.call(rbind,lapply(object, "[[", "coef")) 34 | dfrout <- cbind(data.frame(group=rownames(p)), p) 35 | rownames(dfrout) <- NULL 36 | 37 | return(dfrout) 38 | } 39 | 40 | #' @method print BBfits 41 | #' @export 42 | print.BBfits <- function(x, ...){ 43 | 44 | cat("Result of fitBBs.\n") 45 | cat("Fitted", x[[1]]$gsmodel, "model to", length(x), "groups\n") 46 | 47 | if(x[[1]]$fitg0){ 48 | cat("Both g0 and g1 were estimated.\n\n") 49 | } else { 50 | cat("Only g1 was estimated (g0 = 0).\n\n") 51 | } 52 | 53 | cat("To return dataframe with coefficients, do: coef(myfit).\n") 54 | cat("(where myfit is the name of the object returned by fitBBs)\n") 55 | } 56 | -------------------------------------------------------------------------------- /man/PhotosynEB.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/LeafEnergyBalance.R 3 | \name{PhotosynEB} 4 | \alias{PhotosynEB} 5 | \alias{FindTleaf} 6 | \title{Coupled leaf gas exchange model with energy balance} 7 | \usage{ 8 | PhotosynEB( 9 | Tair = 25, 10 | VPD = 1.5, 11 | Wind = 2, 12 | Wleaf = 0.02, 13 | StomatalRatio = 1, 14 | LeafAbs = 0.86, 15 | RH = NULL, 16 | ... 17 | ) 18 | 19 | FindTleaf(gs, Tair, ...) 20 | } 21 | \arguments{ 22 | \item{Tair}{Air temperature (C)} 23 | 24 | \item{VPD}{The vapour pressure deficit of the air (i.e. not the leaf-to-air VPD) (kPa).} 25 | 26 | \item{Wind}{Wind speed (m s-1)} 27 | 28 | \item{Wleaf}{Leaf width (m)} 29 | 30 | \item{StomatalRatio}{The stomatal ratio (cf. Licor6400 terminology), if it is 1, leaves have stomata only on one side (hypostomatous), 2 for leaves with stomata on both sides (amphistomatous).} 31 | 32 | \item{LeafAbs}{Leaf absorptance of solar radiation (0-1).} 33 | 34 | \item{RH}{The relative humidity of the air (i.e. not calculated with leaf temperature) (in percent).} 35 | 36 | \item{\dots}{Further parameters passed to \code{\link{Photosyn}}. Note that Tleaf is not allowed as an input, since that is calculated by \code{PhotosynEB} from energy balance.} 37 | 38 | \item{gs}{For \code{FindTleaf}, the stomatal conductance (mol m-2 s-1).} 39 | } 40 | \description{ 41 | As \code{\link{Photosyn}}, but calculates the leaf temperature based on the leaf's energy balance. Including sensible and long-wave heat loss, latent heat loss from evaporation, and solar radiation input. 42 | 43 | #'\strong{Warning:}Do not provide GS as an input to \code{PhotosynEB} directly; the results will not be as expected (filed as issue #27) 44 | } 45 | \details{ 46 | Uses the Penman-Monteith equation to calculate the leaf transpiration rate, and finds Tleaf by solving the leaf energy balance iteratively. In the solution, it is accounted for that stomatal conductance (via the dependence of photosynthesis on Tleaf) and net radiation depend on Tleaf. 47 | 48 | Also included is the function \code{FindTleaf}, which calculates the leaf temperature if the stomatal conductance is known. The \strong{limitation} to this function is that input stomatal conductance (gs) is not vectorized, i.e. you can only provide one value at a time. 49 | } 50 | -------------------------------------------------------------------------------- /man/Conversions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/RHtoVPD.R 3 | \name{RHtoVPD} 4 | \alias{RHtoVPD} 5 | \alias{VPDtoRH} 6 | \alias{esat} 7 | \alias{VPDtoDew} 8 | \alias{DewtoVPD} 9 | \alias{VPDleafToAir} 10 | \alias{VPDairToLeaf} 11 | \alias{RHleafToAir} 12 | \alias{RHairToLeaf} 13 | \title{Conversions between relative humidity, vapour pressure deficit and dewpoint} 14 | \usage{ 15 | RHtoVPD(RH, TdegC, Pa = 101) 16 | 17 | VPDtoRH(VPD, TdegC, Pa = 101) 18 | 19 | esat(TdegC, Pa = 101) 20 | 21 | VPDtoDew(VPD, TdegC, Pa = 101) 22 | 23 | DewtoVPD(Tdew, TdegC, Pa = 101) 24 | 25 | VPDleafToAir(VPD, Tleaf, Tair, Pa = 101) 26 | 27 | VPDairToLeaf(VPD, Tair, Tleaf, Pa = 101) 28 | 29 | RHleafToAir(RH, Tleaf, Tair, Pa = 101) 30 | 31 | RHairToLeaf(RH, Tair, Tleaf, Pa = 101) 32 | } 33 | \arguments{ 34 | \item{RH}{Relative humidity (\%)} 35 | 36 | \item{TdegC}{Temperature (degrees C) (either leaf or air)} 37 | 38 | \item{Pa}{Atmospheric pressure (kPa)} 39 | 40 | \item{VPD}{Vapour pressure deficit (kPa)} 41 | 42 | \item{Tdew}{Dewpoint temperature (degrees C)} 43 | 44 | \item{Tleaf}{Leaf temperature (degrees C)} 45 | 46 | \item{Tair}{Air temperature (degrees C)} 47 | } 48 | \description{ 49 | A collection of functions to convert between relative humidity (RH) (\%), 50 | vapour pressure deficit (VPD) (kPa), 51 | dew point temperature, and leaf- or air temperature-based VPD or RH. To convert from 52 | relative humidity to VPD, 53 | use the \code{RHtoVPD} function, use \code{VPDtoRH} for the other way around. The water 54 | vapor saturation pressure is 55 | calculated with \code{esat}. Use \code{DewtoVPD} to 56 | convert from dewpoint temperature to VPD. The functions \code{VPDleafToAir} and \code{VPDairToLeaf} 57 | convert VPD from a leaf temperature to an air-temperature basis and vice versa. The 58 | functions \code{RHleafToAir} a \code{RHairToLeaf} do the same for relative humidity. 59 | } 60 | \details{ 61 | The function describing saturated vapor pressure with temperature is taken from 62 | Jones (1992). All other calculations follow directly from the standard definitions, for 63 | which Jones (1992) may also be consulted. 64 | } 65 | \references{ 66 | Jones, H.G. 1992. Plants and microclimate: a quantitative approach to 67 | environmental plant physiology. 2nd Edition., 2nd Edn. Cambridge University Press, Cambridge. 428 p. 68 | } 69 | \author{ 70 | Remko Duursma 71 | } 72 | -------------------------------------------------------------------------------- /man/AciC4.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/AciC4.R 3 | \name{AciC4} 4 | \alias{AciC4} 5 | \title{C4 Photosynthesis} 6 | \usage{ 7 | AciC4( 8 | Ci, 9 | PPFD = 1500, 10 | Tleaf = 25, 11 | VPMAX25 = 120, 12 | JMAX25 = 400, 13 | Vcmax = 60, 14 | Vpr = 80, 15 | alpha = 0, 16 | gbs = 0.003, 17 | O2 = 210, 18 | x = 0.4, 19 | THETA = 0.7, 20 | Q10 = 2.3, 21 | RD0 = 1, 22 | RTEMP = 25, 23 | TBELOW = 0, 24 | DAYRESP = 1, 25 | Q10F = 2, 26 | FRM = 0.5, 27 | ... 28 | ) 29 | } 30 | \arguments{ 31 | \item{Ci}{Intercellular CO2 concentration (ppm)} 32 | 33 | \item{PPFD}{Photosynthetic photon flux density (mu mol m-2 s-1)} 34 | 35 | \item{Tleaf}{Leaf temperature (C)} 36 | 37 | \item{VPMAX25}{The maximum rate of PEP carboxylation (mu mol m-2 s-1)} 38 | 39 | \item{JMAX25}{Maximum electron transport rate (at 25C)} 40 | 41 | \item{Vcmax}{Maximum rate of carboxylation (mu mol m-2 s-1) (at 25C)} 42 | 43 | \item{Vpr}{PEP regeneration (mu mol m-2 s-1)} 44 | 45 | \item{alpha}{Fraction of PSII activity in the bundle sheath (-)} 46 | 47 | \item{gbs}{Bundle sheath conductance (mol m-2 s-1)} 48 | 49 | \item{O2}{Mesophyll O2 concentration} 50 | 51 | \item{x}{Partitioning factor for electron transport} 52 | 53 | \item{THETA}{Shape parameter of the non-rectangular hyperbola} 54 | 55 | \item{Q10}{T-dependence parameter for Michaelis-Menten coefficients.} 56 | 57 | \item{RD0}{Respiration at base temperature (RTEMP) (mu mol m-2 s-1)} 58 | 59 | \item{RTEMP}{Base leaf temperature for respiration (C)} 60 | 61 | \item{TBELOW}{Below this T, respiration is zero.} 62 | 63 | \item{DAYRESP}{Fraction respiration in the light vs. that measured in the dark} 64 | 65 | \item{Q10F}{T-dependence parameter of respiration} 66 | 67 | \item{FRM}{Fraction of day respiration that is mesophyll respiration (Rm)} 68 | 69 | \item{\dots}{Further arguments (currently ignored).} 70 | } 71 | \description{ 72 | An implementation of the A-Ci curve for C4 plants, based on von Caemmerer et al. (2000) 73 | } 74 | \details{ 75 | Note that the temperature response parameters have been hardwired 76 | in this function, and are based on von Caemmerer (2000). 77 | 78 | Note that it is not (yet) possible to fit this curve to observations of 79 | photosynthesis (see \code{\link{fitaci}} to fit the C3 model of photosynthesis). 80 | } 81 | \examples{ 82 | # Simulate a C4 A-Ci curve. 83 | aci <- AciC4(Ci=seq(5,600, length=101)) 84 | with(aci, plot(Ci, ALEAF, type='l', ylim=c(0,max(ALEAF)))) 85 | } 86 | \references{ 87 | Caemmerer, S.V., 2000. Biochemical Models of Leaf Photosynthesis. Csiro Publishing. 88 | } 89 | \author{ 90 | Rhys Whitley 91 | } 92 | -------------------------------------------------------------------------------- /R/tuzet.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | psil_e <- function(ELEAF, kl, psis){ 4 | 5 | psil <- psis - ELEAF / kl 6 | 7 | psil[!is.finite(psil)] <- psis 8 | 9 | psil 10 | } 11 | 12 | fsig_tuzet <- function(psil, sf=3.2, psif=-1.9){ 13 | (1+exp(sf*psif))/(1+exp(sf*(psif-psil))) 14 | } 15 | 16 | 17 | 18 | PhotosynTuzet_f <- function(g1=4, 19 | Ca=400, 20 | psis=0, 21 | kl=2, 22 | sf=3, 23 | psif=-2, 24 | ...){ 25 | 26 | vn <- as.list(match.call())[-1] 27 | if("gsmodel" %in% vn){ 28 | Stop("Cannot define gsmodel with PhotosynTuzet.") 29 | } 30 | 31 | O <- function(psil, psis, kl, sf, psif, g1, Ca, ...){ 32 | 33 | p <- Photosyn(g1=g1, Ca=Ca, gsmodel="BBdefine", BBmult=(g1/Ca)*fsig_tuzet(psil, sf, psif), ...) 34 | psilout <- psil_e(p$ELEAF, kl, psis) 35 | 36 | psil - psilout # objective function: psil in = psil out. 37 | } 38 | 39 | topt <- uniroot(O, c(-20,0), psis=psis, kl=kl, sf=sf, psif=psif, g1=g1, Ca=Ca, ...) 40 | 41 | p <- Photosyn(g1=g1, Ca=Ca, gsmodel="BBdefine", BBmult=(g1/Ca)*fsig_tuzet(topt$root, sf, psif), ...) 42 | p <- cbind(p, data.frame(PSIL=topt$root)) 43 | 44 | return(p) 45 | } 46 | 47 | #' Coupled leaf gas exchange model with Tuzet stomatal conductance 48 | #' @description An implementation of the coupled photosynthesis - stomatal conductance model, using the Tuzet et al. (2003) model of stomatal conductance. Accepts all arguments of \code{\link{Photosyn}} (except \code{gsmodel}, of course). 49 | #' @param g1 The slope parameter. Note that the default value should be much higher than that used in the Medlyn et al. (2011) model to give comparable predictions. 50 | #' @param Ca Atmospheric CO2 concentration. 51 | #' @param psis Soil water potential (MPa). Note that soil-to-root hydraulic conductance is not implemented. 52 | #' @param kl Leaf-specific hydraulic conductance (mmol m-2 s-1 MPa-1) 53 | #' @param sf Shape parameter (-) of sigmoidal function of leaf water potential (see Tuzet et al. 2003) 54 | #' @param psif Leaf water potential at which stomatal conductance is 50\% of maximum (MPa). 55 | #' @param \dots All other arguments in \code{\link{Photosyn}} 56 | #'@export 57 | PhotosynTuzet <- function(g1=8, 58 | Ca=400, 59 | psis=0, 60 | kl=2, 61 | sf=3, 62 | psif=-2, 63 | ...){ 64 | m <- mapply(PhotosynTuzet_f, g1=g1, Ca=Ca, psis=psis, kl=kl, sf=sf, psif=psif, 65 | ..., SIMPLIFY=FALSE) 66 | do.call(rbind, m) 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /man/FARAO.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/FARAO.R 3 | \name{FARAO} 4 | \alias{FARAO} 5 | \alias{FARAO2} 6 | \title{FARquhar And Opti} 7 | \usage{ 8 | FARAO( 9 | lambda = 0.002, 10 | Ca = 400, 11 | VPD = 1, 12 | photo = c("BOTH", "VCMAX", "JMAX"), 13 | energybalance = FALSE, 14 | C4 = FALSE, 15 | Tair = 25, 16 | Wind = 2, 17 | Wleaf = 0.02, 18 | StomatalRatio = 1, 19 | LeafAbs = 0.86, 20 | ... 21 | ) 22 | 23 | FARAO2(lambda = 0.002, Ca = 400, energybalance = FALSE, ...) 24 | } 25 | \arguments{ 26 | \item{lambda}{The marginal cost of water (mol mol-1)} 27 | 28 | \item{Ca}{The CO2 concentration.} 29 | 30 | \item{VPD}{Vapor pressure deficit (kPa)} 31 | 32 | \item{photo}{Which photosynthesis rate should stomata respond to? Defaults to 'BOTH', i.e. 33 | the minimum of Vcmax and Jmax limited rates.} 34 | 35 | \item{energybalance}{If TRUE (Default = FALSE), calculates leaf temperature from energy balance 36 | (and its effects on photosynthesis as well as leaf transpiration), using \code{\link{PhotosynEB}}.} 37 | 38 | \item{C4}{If TRUE, uses the C4 photosynthesis routine (\code{\link{AciC4}})} 39 | 40 | \item{Tair}{Air temperature (deg C)} 41 | 42 | \item{Wind}{Wind speed (m s-1) (only used if energybalance=TRUE)} 43 | 44 | \item{Wleaf}{Leaf width (m) (only used if energybalance=TRUE)} 45 | 46 | \item{StomatalRatio}{The stomatal ratio (see \code{\link{PhotosynEB}}) (only used if 47 | energybalance=TRUE)} 48 | 49 | \item{LeafAbs}{Leaf absorptance (see \code{\link{PhotosynEB}}) (only used if 50 | energybalance=TRUE)} 51 | 52 | \item{...}{All other parameters are passed to \code{\link{Aci}}} 53 | } 54 | \description{ 55 | The numerical solution of the optimal stomatal conductance model, coupled with 56 | the Farquhar model of photosynthesis. The model of Medlyn et al. (2011) is an approximation 57 | to this full numeric solution. 58 | } 59 | \details{ 60 | This model finds the Ci that maximizes A - lambda*E (Cowan & Farquhar 1977, 61 | see also Medlyn et al. 2011). The new function FARAO2 is a much simpler (and probably 62 | more stable) implementation, based on Buckley et al. 2014 (P,C&E). Both functions 63 | are provided, as FARAO has a few more options than FARAO2, at the moment. 64 | } 65 | \references{ 66 | Buckley, T.N., Martorell, S., Diaz-Espejo, A., Tomas, M., Medrano, H., 2014. Is stomatal 67 | conductance optimized over both time and space in plant crowns? A field test in 68 | grapevine (Vitis vinifera). Plant Cell Environ doi:10.1111/pce.12343 69 | 70 | Cowan, I. and G.D. Farquhar. 1977. Stomatal function in relation to leaf metabolism 71 | and environment. Symposia of the Society for Experimental Biology. 31:471-505. 72 | 73 | Medlyn, B.E., R.A. Duursma, D. Eamus, D.S. Ellsworth, I.C. Prentice, C.V.M. Barton, 74 | K.Y. Crous, P. De Angelis, M. Freeman and L. Wingate. 2011. Reconciling the optimal 75 | and empirical approaches to modelling stomatal conductance. Global Change Biology. 17:2134-2144. 76 | } 77 | \author{ 78 | Remko Duursma 79 | } 80 | -------------------------------------------------------------------------------- /R/RHtoVPD.R: -------------------------------------------------------------------------------- 1 | #' Conversions between relative humidity, vapour pressure deficit and dewpoint 2 | #' 3 | #' @description A collection of functions to convert between relative humidity (RH) (\%), 4 | #' vapour pressure deficit (VPD) (kPa), 5 | #' dew point temperature, and leaf- or air temperature-based VPD or RH. To convert from 6 | #' relative humidity to VPD, 7 | #' use the \code{RHtoVPD} function, use \code{VPDtoRH} for the other way around. The water 8 | #' vapor saturation pressure is 9 | #' calculated with \code{esat}. Use \code{DewtoVPD} to 10 | #' convert from dewpoint temperature to VPD. The functions \code{VPDleafToAir} and \code{VPDairToLeaf} 11 | #' convert VPD from a leaf temperature to an air-temperature basis and vice versa. The 12 | #' functions \code{RHleafToAir} a \code{RHairToLeaf} do the same for relative humidity. 13 | #' @details The function describing saturated vapor pressure with temperature is taken from 14 | #' Jones (1992). All other calculations follow directly from the standard definitions, for 15 | #' which Jones (1992) may also be consulted. 16 | #' @param RH Relative humidity (\%) 17 | #' @param TdegC Temperature (degrees C) (either leaf or air) 18 | #' @param Tair Air temperature (degrees C) 19 | #' @param Tleaf Leaf temperature (degrees C) 20 | #' @param VPD Vapour pressure deficit (kPa) 21 | #' @param Pa Atmospheric pressure (kPa) 22 | #' @param Tdew Dewpoint temperature (degrees C) 23 | #' @export RHtoVPD VPDtoRH esat DewtoVPD VPDtoDew VPDleafToAir VPDairToLeaf 24 | #' @rdname Conversions 25 | #' @references Jones, H.G. 1992. Plants and microclimate: a quantitative approach to 26 | #' environmental plant physiology. 2nd Edition., 2nd Edn. Cambridge University Press, Cambridge. 428 p. 27 | #' @author Remko Duursma 28 | RHtoVPD <- function(RH, TdegC, Pa=101){ 29 | esatval <- esat(TdegC, Pa) 30 | e <- (RH/100) * esatval 31 | VPD <- (esatval - e)/1000 32 | return(VPD) 33 | } 34 | #' @rdname Conversions 35 | VPDtoRH <- function(VPD, TdegC, Pa=101){ 36 | esatval <- esat(TdegC, Pa) 37 | e <- pmax(0, esatval - VPD*1000) 38 | RH <- 100 * e/esatval 39 | return(RH) 40 | } 41 | #' @rdname Conversions 42 | esat <- function(TdegC, Pa=101){ 43 | a <- 611.21 44 | b <- 17.502 45 | c <- 240.97 46 | f <- 1.0007 + 3.46 * 10^-8 * Pa * 1000 47 | esatval <- f * a * (exp(b * TdegC/(c + TdegC))) 48 | return(esatval) 49 | } 50 | 51 | # inverse of esat (calc T given a saturation vapor pressure) 52 | T_esat <- function(sat, Pa=101){ 53 | a <- 611.21 54 | b <- 17.502 55 | c <- 240.97 56 | f <- 1.0007 + 3.46 * 10^-8 * Pa * 1000 57 | 58 | phi <- log(sat/(f*a)) 59 | (c*phi)/(b-phi) 60 | } 61 | #' @rdname Conversions 62 | VPDtoDew <- function(VPD, TdegC, Pa=101){ 63 | 64 | esatval <- esat(TdegC, Pa) 65 | e <- pmax(0, esatval - VPD*1000) 66 | T_esat(e, Pa) 67 | } 68 | 69 | #' @rdname Conversions 70 | DewtoVPD <- function(Tdew, TdegC, Pa=101){ 71 | 72 | # Actual vapor pressure. 73 | e <- esat(Tdew, Pa) 74 | 75 | # saturated: 76 | esatval <- esat(TdegC) 77 | 78 | return((esatval - e)/1000) 79 | } 80 | #' @rdname Conversions 81 | VPDleafToAir <- function(VPD, Tleaf, Tair, Pa=101){ 82 | 83 | e <- esat(Tleaf, Pa) - VPD*1000 84 | vpd <- esat(Tair, Pa) - e 85 | 86 | return(vpd/1000) 87 | } 88 | #' @rdname Conversions 89 | VPDairToLeaf <- function(VPD, Tair, Tleaf, Pa=101){ 90 | 91 | e <- esat(Tair, Pa) - VPD*1000 92 | vpd <- esat(Tleaf, Pa) - e 93 | 94 | return(vpd/1000) 95 | } 96 | #' @rdname Conversions 97 | #' @export 98 | RHleafToAir <- function(RH, Tleaf, Tair, Pa=101){ 99 | 100 | e <- (RH/100)*esat(Tleaf, Pa) 101 | rh <- e/esat(Tair, Pa) 102 | 103 | return(rh*100) 104 | } 105 | #' @rdname Conversions 106 | #' @export 107 | RHairToLeaf <- function(RH, Tair, Tleaf, Pa=101){ 108 | 109 | e <- (RH/100)*esat(Tair, Pa) 110 | rh <- e/esat(Tleaf, Pa) 111 | 112 | return(rh*100) 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /man/fitBB.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitBB.R 3 | \name{fitBB} 4 | \alias{fitBB} 5 | \title{Fit Ball-Berry type models of stomatal conductance} 6 | \usage{ 7 | fitBB( 8 | data, 9 | varnames = list(ALEAF = "Photo", GS = "Cond", VPD = "VpdL", Ca = "CO2S", RH = "RH"), 10 | gsmodel = c("BBOpti", "BBLeuning", "BallBerry", "BBOptiFull"), 11 | fitg0 = FALSE 12 | ) 13 | } 14 | \arguments{ 15 | \item{data}{Input dataframe, containing all variables needed to fit the model.} 16 | 17 | \item{varnames}{List of names of variables in the input dataframe. Relative humidity (RH) is only 18 | needed when the original Ball-Berry model is to be fit.} 19 | 20 | \item{gsmodel}{One of BBOpti (Medlyn et al. 2011), BBLeuning (Leuning 1995), BallBerry (Ball et al. 1987), or BBOptiFull (Medlyn et al. 2011 but with an extra parameter gk, see Duursma et al. 2013)} 21 | 22 | \item{fitg0}{If TRUE, also fits the intercept term (g0, the 'residual conductance'). Default is FALSE.} 23 | } 24 | \value{ 25 | A list with several components, most notably \code{fit}, the object returned by \code{nls}. If the user needs more information on the goodness of fit etc, please further analyze this object. For example, use the \pkg{broom} package for quick summaries. Or use \code{\link{confint}} to calculate confidence intervals on the fitted parameters. 26 | } 27 | \description{ 28 | Fits one of three versions of the Ball-Berry type stomatal conductance models to 29 | observations of stomatal conductance (gs), photosynthesis (A), atmospheric CO2 concentration (Ca) 30 | and vapour pressure deficit (VPD). 31 | } 32 | \details{ 33 | Note that unlike in some publications (e.g. Leuning et al. 1995), the models fit here do not include the CO2 compensation point. This correction may be necessary but can be added by the user (by replacing Ca with the corrected term). 34 | 35 | Note that all models use atmospheric CO2 concentration (Ca) instead of, as sometimes argued, intercellular CO2 concentration (Ci). Using the latter makes these models far more difficult to use in practice, and we have found no benefit of using Ci instead of Ca (and Ca arises from an optimization argument, see Medlyn et al. 2011). The idea that we should use Ci because 'stomata sense Ci, not Ca' is probably not valid (or at least, not sufficient), and note that Ci plays a central role in the steady-state solution to stomatal conductance anyway (see \code{\link{Photosyn}}). 36 | 37 | To fit the Ball-Berry models for each group in a dataframe, for example species, see the \code{\link{fitBBs}} function. 38 | } 39 | \examples{ 40 | 41 | \dontrun{ 42 | # If 'mydfr' is a dataframe with 'Photo', 'Cond', 'VpdL' and 'CO2S', you can do: 43 | myfit <- fitBB(mydfr, gsmodel = "BBOpti") 44 | 45 | # Coefficients and a message: 46 | myfit 47 | 48 | # Coefficients only 49 | coef(myfit) 50 | 51 | # If you have a species variable, and would like to fit the model for each species, 52 | # use fitBBs (see its help page ?fitBBs) 53 | myfits <- fitBBs(mydfr, "species") 54 | } 55 | } 56 | \references{ 57 | Ball, J.T., Woodrow, I.E., Berry, J.A., 1987. A model predicting stomatal conductance and its contribution to the control of photosynthesis under different environmental conditions., in: Biggins, J. (Ed.), Progress in Photosynthesis Research. Martinus-Nijhoff Publishers, Dordrecht, the Netherlands, pp. 221-224. 58 | 59 | Leuning, R. 1995. A critical-appraisal of a combined stomatal-photosynthesis model for C-3 plants. Plant Cell and Environment. 18:339-355. 60 | 61 | Medlyn, B.E., R.A. Duursma, D. Eamus, D.S. Ellsworth, I.C. Prentice, C.V.M. Barton, K.Y. Crous, P. De Angelis, M. Freeman and L. Wingate. 2011. Reconciling the optimal and empirical approaches to modelling stomatal conductance. Global Change Biology. 17:2134-2144. 62 | 63 | Duursma, R.A., Payton, P., Bange, M.P., Broughton, K.J., Smith, R.A., Medlyn, B.E., Tissue, D.T., 2013. Near-optimal response of instantaneous transpiration efficiency to vapour pressure deficit, temperature and [CO2] in cotton (Gossypium hirsutum L.). Agricultural and Forest Meteorology 168, 168-176. doi:10.1016/j.agrformet.2012.09.005 64 | } 65 | -------------------------------------------------------------------------------- /R/AciC4.R: -------------------------------------------------------------------------------- 1 | #' C4 Photosynthesis 2 | #' 3 | #' @description An implementation of the A-Ci curve for C4 plants, based on von Caemmerer et al. (2000) 4 | #' @author Rhys Whitley 5 | #' @param Ci Intercellular CO2 concentration (ppm) 6 | #' @param PPFD Photosynthetic photon flux density (mu mol m-2 s-1) 7 | #' @param Tleaf Leaf temperature (C) 8 | #' @param VPMAX25 The maximum rate of PEP carboxylation (mu mol m-2 s-1) 9 | #' @param Vcmax Maximum rate of carboxylation (mu mol m-2 s-1) (at 25C) 10 | #' @param JMAX25 Maximum electron transport rate (at 25C) 11 | #' @param Vpr PEP regeneration (mu mol m-2 s-1) 12 | #' @param alpha Fraction of PSII activity in the bundle sheath (-) 13 | #' @param gbs Bundle sheath conductance (mol m-2 s-1) 14 | #' @param O2 Mesophyll O2 concentration 15 | #' @param x Partitioning factor for electron transport 16 | #' @param THETA Shape parameter of the non-rectangular hyperbola 17 | #' @param Q10 T-dependence parameter for Michaelis-Menten coefficients. 18 | #' @param RD0 Respiration at base temperature (RTEMP) (mu mol m-2 s-1) 19 | #' @param RTEMP Base leaf temperature for respiration (C) 20 | #' @param TBELOW Below this T, respiration is zero. 21 | #' @param DAYRESP Fraction respiration in the light vs. that measured in the dark 22 | #' @param Q10F T-dependence parameter of respiration 23 | #' @param FRM Fraction of day respiration that is mesophyll respiration (Rm) 24 | #' @param \dots Further arguments (currently ignored). 25 | #' @details Note that the temperature response parameters have been hardwired 26 | #' in this function, and are based on von Caemmerer (2000). 27 | #' 28 | #' Note that it is not (yet) possible to fit this curve to observations of 29 | #' photosynthesis (see \code{\link{fitaci}} to fit the C3 model of photosynthesis). 30 | #' @references Caemmerer, S.V., 2000. Biochemical Models of Leaf Photosynthesis. Csiro Publishing. 31 | #' @examples 32 | #' # Simulate a C4 A-Ci curve. 33 | #' aci <- AciC4(Ci=seq(5,600, length=101)) 34 | #' with(aci, plot(Ci, ALEAF, type='l', ylim=c(0,max(ALEAF)))) 35 | #' @export 36 | AciC4 <- function(Ci, 37 | PPFD=1500, 38 | Tleaf = 25, 39 | VPMAX25=120, 40 | JMAX25=400, 41 | Vcmax=60, 42 | Vpr=80, 43 | alpha=0.0, 44 | gbs=3e-3, 45 | O2=210, 46 | x=0.4, 47 | THETA=0.7, 48 | Q10 = 2.3, 49 | RD0=1, 50 | RTEMP=25, 51 | TBELOW=0, 52 | DAYRESP=1, 53 | Q10F=2, 54 | FRM=0.5 ,...){ 55 | 56 | TK <- Tleaf+273.15 57 | 58 | # Temperature effects on Vcmax, Vpmax and Jmax (Massad et al. 2007) 59 | # This function returns value between 0 and 1. 60 | Arrhenius <- function(TK, Ea, Hd, DS){ 61 | exp( Ea*(TK-298)/(298*.Rgas()*TK) ) * 62 | (1+exp( (298*DS-Hd)/(298*.Rgas()) )) / 63 | (1+exp( (TK*DS-Hd)/(TK*.Rgas()) )) 64 | } 65 | 66 | # Half the reciprocal for Rubisco specificity (NOT CO2 compensation point) 67 | low_gammastar <- 1.93e-4 68 | 69 | # Michaelis-Menten coefficients for CO2 (Kc, mu mol mol-1) and 70 | # O (Ko, mmol mol-1) and combined (K) 71 | Kc <- 650*Q10^((Tleaf-25)/10) 72 | Kp <- 80*Q10^((Tleaf-25)/10) 73 | Ko <- 450*Q10^((Tleaf-25)/10) 74 | K <- Kc*(1+O2/Ko) 75 | 76 | # T effects according to Massad et al. (2007) 77 | Vcmax <- Vcmax*Arrhenius(TK, 67294, 144568, 472) 78 | Vpmax <- VPMAX25*Arrhenius(TK, 70373, 117910, 376) 79 | Jmax <- JMAX25*Arrhenius(TK, 77900, 191929, 627) 80 | 81 | # Day leaf respiration, umol m-2 s-1 82 | if (Tleaf > TBELOW) { 83 | Rd <- RD0 * Q10^((Tleaf-RTEMP)/10) * DAYRESP 84 | } else { 85 | Rd <- 0.0 86 | } 87 | Rm <- FRM*Rd 88 | 89 | # PEP carboxylation rate 90 | Vp <- pmin(Ci*Vpmax/(Ci+Kp),Vpr) 91 | 92 | # Quadratic solution for enzyme limited C4 assimilation 93 | a.c <- 1 - (alpha*Kc)/(0.047*Ko) 94 | b.c <- -( (Vp-Rm+gbs*Ci) + (Vcmax-Rd) + gbs*K + 95 | alpha*low_gammastar/0.047*( low_gammastar*Vcmax+Rd*Kc/Ko ) ) 96 | c.c <- (Vcmax-Rd)*(Vp-Rm+gbs*Ci) - (Vcmax*gbs*low_gammastar*O2 + Rd*gbs*K) 97 | 98 | A.enzyme <- (-b.c - sqrt(b.c^2 - 4*a.c*c.c)) / (2*a.c) 99 | 100 | # Non-rectangular hyperbola describing light effect on electron transport rate (J) 101 | Qp2 <- PPFD*(1-0.15)/2 102 | J <- (1/(2*THETA))*(Qp2+Jmax - sqrt((Qp2+Jmax)^2-4*THETA*Qp2*Jmax)) 103 | 104 | # Quadratic solution for light-limited C4 assimilation 105 | a.j <- 1 - 7*low_gammastar*alpha/(3*0.047) 106 | b.j <- -( (x*J/2-Rm+gbs*Ci) + ((1-x)*J/3-Rd) + gbs*(7*low_gammastar*O2/3) 107 | + alpha*low_gammastar/0.047*((1-x)*J/3+Rd) ) 108 | c.j <- ( (x*J/2-Rm+gbs*Ci)*((1-x)*J/3-Rd) - gbs*low_gammastar*O2*((1-x)*J/3-7*Rd/3) ) 109 | 110 | A.light <- (-b.j - sqrt(b.j^2 - 4*a.j*c.j)) / (2*a.j) 111 | 112 | # Actual assimilation rate 113 | An <- pmin(A.enzyme,A.light) 114 | Ac <- A.enzyme 115 | Aj <- A.light 116 | 117 | # Hyperbolic minimum (Buckley), to avoid discontinuity at transition from Ac to Aj 118 | shape2 <- 0.999 119 | Ad <- (Ac+Aj - sqrt((Ac+Aj)^2-4*shape2*Ac*Aj))/(2*shape2) - Rd 120 | Ac <- Ac - Rd 121 | Aj <- Aj - Rd 122 | 123 | return(data.frame(Ci=Ci, ALEAF=Ad, An=An, Ac=Ac, Aj=Aj, 124 | Vp=Vp, Rd=Rd, Tleaf=Tleaf, PPFD=PPFD)) 125 | } 126 | -------------------------------------------------------------------------------- /R/fitacis_methods.R: -------------------------------------------------------------------------------- 1 | #' @method plot acifits 2 | #' @export 3 | #' @rdname fitacis 4 | #' @importFrom grDevices palette rainbow 5 | plot.acifits <- function(x, how=c("manyplots","oneplot"), 6 | highlight=NULL, 7 | ylim=NULL, 8 | xlim=NULL, 9 | add=FALSE, 10 | what=c("model","data","none"), 11 | colour_by_id = FALSE, 12 | id_legend=TRUE, 13 | linecol = "grey", 14 | linecol_highlight = "black", 15 | lty=1, 16 | ...){ 17 | 18 | how <- match.arg(how) 19 | what <- match.arg(what) 20 | 21 | if(colour_by_id){ 22 | 23 | if(is.null(x[[1]]$id)){ 24 | Stop("To colour curves by id, fit with id argument (see ?fitacis).") 25 | } 26 | 27 | id_fac <- sapply(x, function(fit)unique(fit$df[,fit$id])) 28 | if(nlevels(id_fac) > length(palette())){ 29 | pal <- rainbow(nlevels(id_fac)) 30 | Warning("Not enough colours in palette, using rainbow().", 31 | "\nSet your colours with palette() first") 32 | line_cols <- pal[id_fac] 33 | } else { 34 | pal <- palette() 35 | line_cols <- pal[id_fac] 36 | } 37 | 38 | } else { 39 | line_cols <- recycle(linecol, length(x)) 40 | } 41 | 42 | # Set axis limits 43 | if(is.null(ylim)){ 44 | amax <- max(sapply(x, function(x)max(x$df$Amodel))) 45 | amin <- max(sapply(x, function(x)min(x$df$Amodel))) 46 | ylim <- c(amin,amax) 47 | } 48 | if(is.null(xlim)){ 49 | cimax <- max(sapply(x, function(x)max(x$df$Ci))) 50 | cimin <- min(sapply(x, function(x)min(x$df$Ci))) 51 | xlim <- c(cimin,cimax) 52 | } 53 | 54 | # Set line types 55 | lty <- recycle(lty, length(x)) 56 | 57 | if(how == "manyplots"){ 58 | if(add)Warning("Argument 'add' ignored when making multiple plots.") 59 | 60 | for(i in seq_along(x)){ 61 | plot.acifit(x[[i]],main=names(x)[i],xlim=xlim,ylim=ylim,...) 62 | } 63 | } 64 | 65 | if(how == "oneplot"){ 66 | 67 | if(!is.null(highlight)){ 68 | if(!highlight %in% names(x)) 69 | stop("Curve ID not found.") 70 | 71 | hi <- which(names(x) == highlight) 72 | 73 | if(!add){ 74 | plot.acifit(x[[1]], what="none", 75 | ylim=ylim, xlim=xlim, 76 | whichA="Amin", 77 | ...) 78 | } 79 | 80 | for(i in seq_along(x)[-hi]){ 81 | plot.acifit(x[[i]], what=what, whichA="Amin", add=TRUE, 82 | linecols = line_cols[i], lty=lty[i], ...) 83 | } 84 | plot.acifit(x[[hi]], what=what, whichA="Amin", add=TRUE, 85 | linecols = linecol_highlight, lty=lty[hi], ...) 86 | 87 | } else { 88 | if(!add) 89 | plot.acifit(x[[1]], what="none", ylim=ylim, xlim=xlim, 90 | addlegend=FALSE, 91 | whichA="Amin", ...) 92 | 93 | for(i in seq_along(x)) 94 | plot.acifit(x[[i]], what=what, whichA="Amin", add=TRUE, 95 | linecols=line_cols[i], lty=lty[i], ...) 96 | 97 | } 98 | 99 | if(colour_by_id && id_legend){ 100 | legend("topleft", levels(id_fac), lty=1, col=pal, cex=0.8, lwd=2) 101 | } 102 | 103 | } 104 | } 105 | 106 | 107 | #' @method coef acifits 108 | #' @export 109 | coef.acifits <- function(object,...){ 110 | 111 | get_pars <- function(object){ 112 | if(all(is.na(object))) NA else as.vector(object$pars) 113 | } 114 | 115 | f <- lapply(object, get_pars) 116 | 117 | # Find objects without result (could not be fitted, even with bilinear), 118 | # and replace with contents of another fit, but all set to NA. 119 | # (This way, names and structure of coefficients is the same). 120 | ok <- sapply(f, function(x)!all(is.na(x))) 121 | if(any(!ok)){ 122 | f[[which(!ok)]] <- f[[which(ok)[1]]] 123 | f[[which(!ok)]][] <- NA 124 | } 125 | 126 | pars <- as.data.frame(do.call(rbind,f)) 127 | rn <- rownames(object[[which(ok)[1]]]$pars) 128 | nm <- c(rn, paste0(rn,"_SE")) 129 | names(pars) <- nm 130 | 131 | d <- data.frame(group=names(object)) 132 | names(d) <- attr(object,"group") 133 | pars <- cbind(d,pars) 134 | rownames(pars) <- NULL 135 | 136 | if(!is.null(object[[1]]$id)){ 137 | get_id <- function(x){ 138 | res <- x$df[x$id] 139 | res1 <- res[1,,drop=FALSE] 140 | as.data.frame(lapply(res1, as.character)) 141 | } 142 | ids <- do.call(rbind,lapply(object, get_id)) 143 | pars <- cbind(pars, ids) 144 | } 145 | 146 | return(pars) 147 | } 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | #'@export 159 | #'@method print acifits 160 | print.acifits <- function(x, ...){ 161 | 162 | cat("Result of fitacis.\n\n") 163 | p <- coef(x) 164 | 165 | cat("Fitted", nrow(p), "curves by", attr(x, "groupname"), "grouping variable.") 166 | 167 | cat("\nRange in estimated Vcmax:", round(min(p$Vcmax, na.rm=TRUE),2), "-", round(max(p$Vcmax),2)) 168 | cat("\nRange in estimated Jmax:", round(min(p$Jmax, na.rm=TRUE),2), "-", round(max(p$Jmax),2)) 169 | cat("\nUse coef() on the object to see all fitted coefficients.") 170 | 171 | } 172 | 173 | -------------------------------------------------------------------------------- /man/fitacis.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitacis.R, R/fitacis_methods.R 3 | \name{fitacis} 4 | \alias{fitacis} 5 | \alias{plot.acifits} 6 | \title{Fit multiple A-Ci curves at once} 7 | \usage{ 8 | fitacis( 9 | data, 10 | group, 11 | fitmethod = c("default", "bilinear"), 12 | progressbar = TRUE, 13 | quiet = FALSE, 14 | id = NULL, 15 | ... 16 | ) 17 | 18 | \method{plot}{acifits}( 19 | x, 20 | how = c("manyplots", "oneplot"), 21 | highlight = NULL, 22 | ylim = NULL, 23 | xlim = NULL, 24 | add = FALSE, 25 | what = c("model", "data", "none"), 26 | colour_by_id = FALSE, 27 | id_legend = TRUE, 28 | linecol = "grey", 29 | linecol_highlight = "black", 30 | lty = 1, 31 | ... 32 | ) 33 | } 34 | \arguments{ 35 | \item{data}{Dataframe with Ci, Photo, Tleaf, PPFD (the last two are optional). For \code{fitacis}, 36 | also requires a grouping variable.} 37 | 38 | \item{group}{The name of the grouping variable in the dataframe (an A-Ci curve will be fit for each group separately).} 39 | 40 | \item{fitmethod}{Method to fit the A-Ci curve. Either 'default' (Duursma 2015), or 'bilinear'. See Details.} 41 | 42 | \item{progressbar}{Display a progress bar (default is TRUE).} 43 | 44 | \item{quiet}{If TRUE, no messages are written to the screen.} 45 | 46 | \item{id}{Names of variables (quoted, can be a vector) in the original dataset to return as part of 47 | the coef() statement. Useful for keeping track of species names, treatment levels, etc. See Details and Examples.} 48 | 49 | \item{\dots}{Further arguments passed to \code{\link{fitaci}} (in the case of \code{fitacis}), or 50 | \code{\link{plot.acifit}} (in the case of \code{plot.acifits}).} 51 | 52 | \item{x}{For \code{plot.acifits}, an object returned from \code{fitacis}} 53 | 54 | \item{how}{If 'manyplots', produces a single plot for each A-Ci curve. If 'oneplot' overlays all of them.} 55 | 56 | \item{highlight}{If a name of a curve is given (check names(object), where object is returned by acifits), 57 | all curves are plotted in grey, with the highlighted one on top.} 58 | 59 | \item{xlim, ylim}{The X and Y axis limits.} 60 | 61 | \item{add}{If TRUE, adds the plots to a current plot.} 62 | 63 | \item{what}{What to plot, either 'model' (the fitted curve), 'data' or 'none'. See examples.} 64 | 65 | \item{colour_by_id}{If TRUE, uses the 'id' argument to colour the curves in the standard plot (only works when \code{how = 'oneplot'}, see Examples)} 66 | 67 | \item{id_legend}{If \code{colour_by_id} is set, place a legend (topleft) or not.} 68 | 69 | \item{linecol}{Colour(s) to use for the non-highlighted curves (can be a vector).} 70 | 71 | \item{linecol_highlight}{Colour to use for the 'highlighted' curve.} 72 | 73 | \item{lty}{Line type(s), can be a vector (one for each level of the factor, will be recycled).} 74 | } 75 | \description{ 76 | A convenient function to fit many curves at once, by calling \code{\link{fitaci}} for 77 | every group in the dataset. The data provided must include a variable that uniquely identifies each A-Ci curve. 78 | } 79 | \details{ 80 | \strong{Troubleshooting - } When using the default fitting method (see \code{\link{fitaci}}), it is common that 81 | some curves cannot be fit. Usually this indicates that the curve is poor quality and should not be used to 82 | estimate photosynthetic capacity, but there are exceptions. The \code{fitacis} function now refits the 83 | non-fitting curves with the 'bilinear' method (see \code{fitaci}), which will always return parameter estimates 84 | (for better or worse). 85 | 86 | \strong{Summarizing and plotting - } Like \code{fitaci}, the batch utility \code{fitacis} also has a standard 87 | plotting method. By default, it will make a single plot for every curve that you fit (thus generating many plots). 88 | Alternatively, use the setting \code{how="oneplot"} (see Examples below) for a single plot. The fitted 89 | \strong{coefficients} are extracted with \code{coef}, which gives a dataframe where each row represents 90 | a fitted curve (the grouping label is also included). 91 | 92 | \strong{Adding identifying variables - } after fitting multiple curves, the most logical next step is to 93 | analyze the coefficient by some categorical variable (species, treatment, location). You can use the 94 | \code{id} argument to store variables from the original dataset in the output. It is important that the 95 | 'id' variables take only one value per fitted curve, if this is not the case only the first value of the 96 | curve will be stored (this will be rarely useful). See examples. 97 | } 98 | \examples{ 99 | 100 | \dontrun{ 101 | # Fit many curves (using an example dataset) 102 | # The bilinear method is much faster, but compare using 'default'! 103 | fits <- fitacis(manyacidat, "Curve", fitmethod="bilinear") 104 | with(coef(fits), plot(Vcmax, Jmax)) 105 | 106 | # The resulting object is a list, with each component an object as returned by fitaci 107 | # So, we can extract one curve: 108 | fits[[1]] 109 | plot(fits[[1]]) 110 | 111 | # Plot all curves in separate figures with plot(fits) 112 | # Or, in one plot: 113 | plot(fits, how="oneplot") 114 | 115 | # Note that parameters can be passed to plot.acifit. For example, 116 | plot(fits, how="oneplot", what="data", col="blue") 117 | plot(fits, how="oneplot", add=TRUE, what="model", lwd=c(1,1)) 118 | 119 | # Other elements can be summarized with sapply. For example, look at the RMSE: 120 | rmses <- sapply(fits, "[[", "RMSE") 121 | plot(rmses, type='h', ylab="RMSE", xlab="Curve nr") 122 | 123 | # And plot the worst-fitting curve: 124 | plot(fits[[which.max(rmses)]]) 125 | 126 | # It is very straightforward to summarize the coefficients by a factor variable 127 | # that was contained in the original data. In manyacidat, there is a factor variable 128 | # 'treatment'. 129 | # We first have to refit the curves, using the 'id' argument: 130 | fits <- fitacis(manyacidat, "Curve", fitmethod="bilinear", id="treatment") 131 | 132 | # And now use this to plot Vcmax by treatment. 133 | boxplot(Vcmax ~ treatment, data=coef(fits), ylim=c(0,130)) 134 | 135 | # As of package version 1.4-2, you can also use the id variable for colouring curves, 136 | # when plotting all fitted curves in one plot. 137 | # Set colours to be used. Also note that the 'id' variable has to be a factor, 138 | # colours will be set in order of the levels of the factor. 139 | # Set palette of colours: 140 | palette(rainbow(8)) 141 | 142 | # Use colours, add legend. 143 | plot(fits, how="oneplot", colour_by_id = TRUE, id_legend=TRUE) 144 | 145 | } 146 | 147 | } 148 | \references{ 149 | Duursma, R.A., 2015. Plantecophys - An R Package for Analysing and Modelling Leaf Gas Exchange Data. 150 | PLoS ONE 10, e0143346. doi:10.1371/journal.pone.0143346 151 | } 152 | -------------------------------------------------------------------------------- /R/fitBB.R: -------------------------------------------------------------------------------- 1 | #' Fit Ball-Berry type models of stomatal conductance 2 | #' @description Fits one of three versions of the Ball-Berry type stomatal conductance models to 3 | #' observations of stomatal conductance (gs), photosynthesis (A), atmospheric CO2 concentration (Ca) 4 | #' and vapour pressure deficit (VPD). 5 | #' @details Note that unlike in some publications (e.g. Leuning et al. 1995), the models fit here do not include the CO2 compensation point. This correction may be necessary but can be added by the user (by replacing Ca with the corrected term). 6 | #' 7 | #' Note that all models use atmospheric CO2 concentration (Ca) instead of, as sometimes argued, intercellular CO2 concentration (Ci). Using the latter makes these models far more difficult to use in practice, and we have found no benefit of using Ci instead of Ca (and Ca arises from an optimization argument, see Medlyn et al. 2011). The idea that we should use Ci because 'stomata sense Ci, not Ca' is probably not valid (or at least, not sufficient), and note that Ci plays a central role in the steady-state solution to stomatal conductance anyway (see \code{\link{Photosyn}}). 8 | #' 9 | #' To fit the Ball-Berry models for each group in a dataframe, for example species, see the \code{\link{fitBBs}} function. 10 | #' @return A list with several components, most notably \code{fit}, the object returned by \code{nls}. If the user needs more information on the goodness of fit etc, please further analyze this object. For example, use the \pkg{broom} package for quick summaries. Or use \code{\link{confint}} to calculate confidence intervals on the fitted parameters. 11 | #' 12 | #' @param data Input dataframe, containing all variables needed to fit the model. 13 | #' @param varnames List of names of variables in the input dataframe. Relative humidity (RH) is only 14 | #' needed when the original Ball-Berry model is to be fit. 15 | #' @param gsmodel One of BBOpti (Medlyn et al. 2011), BBLeuning (Leuning 1995), BallBerry (Ball et al. 1987), or BBOptiFull (Medlyn et al. 2011 but with an extra parameter gk, see Duursma et al. 2013) 16 | #' @param fitg0 If TRUE, also fits the intercept term (g0, the 'residual conductance'). Default is FALSE. 17 | #' @export 18 | #' @references 19 | #' Ball, J.T., Woodrow, I.E., Berry, J.A., 1987. A model predicting stomatal conductance and its contribution to the control of photosynthesis under different environmental conditions., in: Biggins, J. (Ed.), Progress in Photosynthesis Research. Martinus-Nijhoff Publishers, Dordrecht, the Netherlands, pp. 221-224. 20 | #' 21 | #' Leuning, R. 1995. A critical-appraisal of a combined stomatal-photosynthesis model for C-3 plants. Plant Cell and Environment. 18:339-355. 22 | #' 23 | #' Medlyn, B.E., R.A. Duursma, D. Eamus, D.S. Ellsworth, I.C. Prentice, C.V.M. Barton, K.Y. Crous, P. De Angelis, M. Freeman and L. Wingate. 2011. Reconciling the optimal and empirical approaches to modelling stomatal conductance. Global Change Biology. 17:2134-2144. 24 | #' 25 | #' Duursma, R.A., Payton, P., Bange, M.P., Broughton, K.J., Smith, R.A., Medlyn, B.E., Tissue, D.T., 2013. Near-optimal response of instantaneous transpiration efficiency to vapour pressure deficit, temperature and [CO2] in cotton (Gossypium hirsutum L.). Agricultural and Forest Meteorology 168, 168-176. doi:10.1016/j.agrformet.2012.09.005 26 | #' 27 | #' @importFrom stats nls 28 | #' @importFrom stats coef 29 | #' @importFrom stats residuals 30 | #' @importFrom stats median 31 | #' @importFrom stats nls.control 32 | #' @rdname fitBB 33 | #' @examples 34 | #' 35 | #' \dontrun{ 36 | #' # If 'mydfr' is a dataframe with 'Photo', 'Cond', 'VpdL' and 'CO2S', you can do: 37 | #' myfit <- fitBB(mydfr, gsmodel = "BBOpti") 38 | #' 39 | #' # Coefficients and a message: 40 | #' myfit 41 | #' 42 | #' # Coefficients only 43 | #' coef(myfit) 44 | #' 45 | #' # If you have a species variable, and would like to fit the model for each species, 46 | #' # use fitBBs (see its help page ?fitBBs) 47 | #' myfits <- fitBBs(mydfr, "species") 48 | #' } 49 | fitBB <- function(data, 50 | varnames=list(ALEAF="Photo", GS="Cond", VPD="VpdL", Ca="CO2S",RH="RH"), 51 | gsmodel=c("BBOpti","BBLeuning","BallBerry","BBOptiFull"), 52 | fitg0=FALSE){ 53 | 54 | gsmodel <- match.arg(gsmodel) 55 | 56 | check_has <- function(this, there){ 57 | if(!this %in% names(data)){ 58 | Stop(this, " column not in data provided.") 59 | } 60 | } 61 | invisible(sapply(varnames, check_has)) 62 | 63 | gs <- data[,varnames$GS] 64 | vpd <- data[,varnames$VPD] 65 | aleaf <- data[,varnames$ALEAF] 66 | ca <- data[,varnames$Ca] 67 | 68 | if(gsmodel == "BallBerry"){ 69 | 70 | if(!("RH" %in% names(varnames))){ 71 | Stop("To fit Ball-Berry you must include RH and specify it in varnames.") 72 | } 73 | 74 | rh <- data[,varnames$RH] 75 | if(max(rh, na.rm=TRUE) > 1){ 76 | message("RH provided in % converted to relative units.") 77 | rh <- rh / 100 78 | } 79 | } 80 | 81 | if(gsmodel == "BBOpti"){ 82 | if(!fitg0){ 83 | fit <- try(nls(gs ~ 1.6*(1 + g1/sqrt(vpd))*(aleaf/ca), start=list(g1=4)) ) 84 | } else { 85 | fit <- try(nls(gs ~ g0 + 1.6*(1 + g1/sqrt(vpd))*(aleaf/ca), start=list(g1=4, g0=0.005)) ) 86 | } 87 | } 88 | if(gsmodel == "BBOptiFull"){ 89 | if(!fitg0){ 90 | fit <- try(nls(gs ~ 1.6*(1 + g1/vpd^(1-gk))*(aleaf/ca), start=list(g1=4, gk=0.5)) ) 91 | } else { 92 | fit <- try(nls(gs ~ g0 + 1.6*(1 + g1/vpd^(1-gk))*(aleaf/ca), start=list(g1=4, g0=0.005, gk=0.5)) ) 93 | } 94 | } 95 | if(gsmodel == "BBLeuning"){ 96 | if(!fitg0){ 97 | fit <- try(nls(gs ~ aleaf*g1/ca/(1 + vpd/D0), start=list(g1=4, D0=1.5))) 98 | } else { 99 | fit <- try(nls(gs ~ g0 + aleaf*g1/ca/(1 + vpd/D0), start=list(g1=4, D0=1.5, g0=0.005))) 100 | } 101 | } 102 | if(gsmodel == "BallBerry"){ 103 | if(!fitg0){ 104 | fit <- try(nls(gs ~ g1*aleaf*rh/ca, start=list(g1=4))) 105 | } else { 106 | fit <- try(nls(gs ~ g0 + g1*aleaf*rh/ca, start=list(g1=4, g0=0.005))) 107 | } 108 | } 109 | 110 | l <- list() 111 | l$gsmodel <- gsmodel 112 | l$varnames <- varnames 113 | l$fitg0 <- fitg0 114 | l$data <- data 115 | l$success <- !inherits(fit, "try-error") 116 | l$coef <- if(l$success){ 117 | if(fitg0){ 118 | rev(coef(fit)) 119 | } else { 120 | c(g0=0, coef(fit)) 121 | } 122 | } 123 | else { 124 | NA 125 | } 126 | l$fit <- fit 127 | l$n <- length(residuals(fit)) 128 | class(l) <- "BBfit" 129 | 130 | return(l) 131 | } 132 | 133 | 134 | 135 | #' @method coef BBfit 136 | #' @export 137 | coef.BBfit <- function(object, ...){ 138 | 139 | object$coef 140 | 141 | } 142 | 143 | #' @method print BBfit 144 | #' @export 145 | print.BBfit <- function(x, ...){ 146 | 147 | cat("Result of fitBB.\n") 148 | cat("Model : ", x$gsmodel, "\n") 149 | if(x$fitg0){ 150 | cat("Both g0 and g1 were estimated.\n\n") 151 | } else { 152 | cat("Only g1 was estimated (g0 = 0).\n\n") 153 | } 154 | 155 | if(x$gsmodel != "BBOptiFull"){ 156 | cat("Coefficients:\n") 157 | cat("g0 g1\n") 158 | cat(signif(coef(x)[1],3), signif(coef(x)[2],3), "\n") 159 | } else { 160 | cat("Coefficients:\n") 161 | cat("g0 g1 gk\n") 162 | cat(signif(coef(x)[1],3), signif(coef(x)[2],3), signif(coef(x)[3],3), "\n") 163 | } 164 | cat("\nFor more details of the fit, look at summary(myfit$fit)\n") 165 | cat("To return coefficients, do coef(myfit).\n") 166 | cat("(where myfit is the name of the object returned by fitBB)\n") 167 | } 168 | 169 | 170 | -------------------------------------------------------------------------------- /R/fitaci_methods.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #'@export 4 | #'@method print acifit 5 | 6 | print.acifit <- function(x, include = c("data","rmse","parameters","method","citransition", 7 | "settings","gstar"), ...){ 8 | 9 | cat("Result of fitaci.\n\n") 10 | 11 | if("data" %in% include){ 12 | cat("Data and predictions:\n") 13 | print(x$df) 14 | } 15 | 16 | if("rmse" %in% include){ 17 | cat("\nRoot mean squared error: ", x$RMSE, "\n") 18 | } 19 | 20 | if("parameters" %in% include){ 21 | cat("\nEstimated parameters:\n") 22 | 23 | print(x$pars) 24 | if(x$Tcorrect) 25 | cat("Note: Vcmax, Jmax are at 25C, Rd is at measurement T.\n") 26 | else 27 | cat("Note: Vcmax, Jmax, Rd are at measurement T.\n") 28 | 29 | if(!is.na(x$gmeso)){ 30 | cat("Note: Mesophyll conductance was input, Vcmax and Jmax are Cc-based rates.\n") 31 | } 32 | 33 | if(x$Rd_measured) 34 | cat("Note: measured Rd was provided, only Vcmax and Jmax were fit.\n") 35 | } 36 | 37 | if("method" %in% include){ 38 | cat("\nCurve was fit using method: ", x$fitmethod, "\n") 39 | } 40 | 41 | 42 | if("citransition" %in% include){ 43 | if(!is.na(x$citransition)){ 44 | cat("\nCi transition was constrained to be: ", x$citransition, "\n") 45 | cat("Actual fitted Ci transition: ", x$Ci_transition,"\n") 46 | } 47 | } 48 | 49 | if("settings" %in% include){ 50 | cat("\nParameter settings:\n") 51 | fm <- formals(x$Photosyn) 52 | pars <- c("Patm","alpha","theta","EaV","EdVC","delsC","EaJ","EdVJ","delsJ") 53 | fm <- unlist(fm[pars]) 54 | cat(paste0(pars," = ", fm,"\n")) 55 | } 56 | 57 | if("gstar" %in% include){ 58 | if(!x$gstarinput | !x$kminput){ 59 | cat("\nEstimated from Tleaf (shown at mean Tleaf):\n") 60 | if(!x$gstarinput)cat("GammaStar = ",x$GammaStar,"\n") 61 | if(!x$kminput)cat("Km = ",x$Km,"\n") 62 | } 63 | 64 | if(x$gstarinput | x$kminput){ 65 | cat("\nSet by user:\n") 66 | if(x$gstarinput)cat("GammaStar = ",x$GammaStar,"\n") 67 | if(x$kminput)cat("Km = ",x$Km,"\n") 68 | } 69 | } 70 | 71 | } 72 | 73 | 74 | #' @method summary acifit 75 | #' @export 76 | summary.acifit <- function(object,...){ 77 | 78 | print.acifit(object, ...) 79 | 80 | } 81 | 82 | 83 | 84 | #' @method coef acifit 85 | #' @export 86 | coef.acifit <- function(object, ...){ 87 | v <- unname(object$pars[,1]) 88 | names(v) <- rownames(object$pars) 89 | return(v) 90 | } 91 | 92 | 93 | 94 | 95 | 96 | 97 | #' @method fitted acifit 98 | #' @export 99 | fitted.acifit <- function(object,...){ 100 | 101 | object$df$Amodel 102 | 103 | } 104 | 105 | 106 | 107 | #' @method plot acifit 108 | #' @export 109 | #' @param x For plot.acifit, an object returned by \code{fitaci} 110 | #' @param xlim Limits for the X axis, if left blank estimated from data 111 | #' @param ylim Limits for the Y axis, if left blank estimated from data 112 | #' @param whichA By default all photosynthetic rates are plotted (Aj=Jmax-limited 113 | #' (blue), Ac=Vcmax-limited (red), Hyperbolic minimum (black)), TPU-limited rate 114 | #' (Ap, if estimated in the fit). Or, specify one or two of them. 115 | #' @param what The default is to plot both the data and the model fit, or specify 'data' or 116 | #' 'model' to plot one of them, or 'none' for neither (only the plot region is set up) 117 | #' @param add If TRUE, adds to the current plot 118 | #' @param pch The plotting symbol for the data 119 | #' @param addzeroline If TRUE, the default, adds a dashed line at y=0 120 | #' @param addlegend If TRUE, adds a legend (by default does not add a legend if add=TRUE) 121 | #' @param legendbty Box type for the legend, passed to argument bty in \code{\link{legend}}. 122 | #' @param transitionpoint For plot.acifit, whether to plot a symbol at the transition point. 123 | #' @param linecols Vector of three colours for the lines (limiting rate, Ac, Aj), if one value 124 | #' provided it is used for all three. 125 | #' @param lwd Line widths, can be a vector of length 2 (first element for Ac and Aj, second one 126 | #' for the limiting rate). 127 | #' @param lty Line type (only for Amin - the limiting rate). 128 | #' @rdname fitaci 129 | #' @importFrom graphics points 130 | #' @importFrom graphics abline 131 | #' @importFrom graphics legend 132 | #' @importFrom graphics text 133 | plot.acifit <- function(x, what=c("data","model","none"), xlim=NULL, ylim=NULL, 134 | whichA=c("Ac","Aj","Amin","Ap"), add=FALSE, pch=19, 135 | addzeroline=TRUE, addlegend=!add, legendbty='n', 136 | transitionpoint=TRUE, 137 | linecols=c("#E41A1C", "#377EB8", "#4DAF4A"), 138 | lwd=c(2.5,2.5), lty=1, 139 | ...){ 140 | 141 | # Note that Ci on the X-axis is in molar units! 142 | if(is.null(ylim))ylim <- with(x$df, c(min(Ameas), 1.1*max(Ameas))) 143 | if(is.null(xlim))xlim <- with(x$df,c(0, max(Ci_original))) 144 | if(length(lwd)==1)lwd <- c(lwd,lwd) 145 | if(length(linecols)==1)linecols <- rep(linecols,3) 146 | 147 | # Vector of Ci values at which to evaluate fitted ACi curve. 148 | Ci <- with(x$df, seq(min(Ci_original), max(Ci_original), length=101)) 149 | 150 | # Exact model used to fit the A-Ci curve was saved in the object. 151 | # (parameter settings etc. are preserved) 152 | pcor <- mean(x$df$Patm)/100 153 | 154 | pred <- x$Photosyn(Ci=Ci * pcor) 155 | pred$Ci_original <- pred$Ci / pcor 156 | 157 | # If TPU could not be estimated, Ap will be NA for Ci > 400. 158 | pred$Ap[is.na(pred$Ap)] <- 1000 159 | 160 | # Is there a TPU limitation? 161 | TPUlimit <- any(pred$Ap < pred$Aj) 162 | 163 | if(!add){ 164 | with(x$df, plot(Ci_original, Ameas, type='n', 165 | ylim=ylim, 166 | xlim=xlim, 167 | xlab=expression(italic(C)[i]~~(ppm)), 168 | ylab=expression(italic(A)[n]~~(mu*mol~m^-2~s^-1)), 169 | ... 170 | )) 171 | } 172 | if("data" %in% what)with(x$df, points(Ci_original, Ameas, pch=pch,...)) 173 | 174 | if("model" %in% what){ 175 | if("Aj" %in% whichA)with(pred, lines(Ci_original, Aj-Rd, col=linecols[2],lwd=lwd[1])) 176 | if("Ac" %in% whichA)with(pred, lines(Ci_original, Ac-Rd, col=linecols[3],lwd=lwd[1])) 177 | if("Ap" %in% whichA & TPUlimit){ 178 | predp <- pred[pred$Ci_original > 400,] 179 | with(predp, lines(Ci_original, Ap-Rd, col="darkgrey", lty=5, lwd=lwd[1])) 180 | } 181 | if("Amin" %in% whichA)with(pred, lines(Ci_original, ALEAF, col=linecols[1], lwd=lwd[2], lty=lty)) 182 | } 183 | 184 | if(transitionpoint && "model" %in% what){ 185 | points(x$Ci_transition / pcor, x$Photosyn(Ci=x$Ci_transition)$ALEAF, 186 | pch=19, col="#984EA3", cex=1.1) 187 | 188 | points((x$Ci_transition / pcor) * 0.17, (x$Photosyn(Ci=x$Ci_transition)$ALEAF) * 1.1, 189 | pch=19, col="#984EA3", cex=0.8) 190 | 191 | text( 192 | (x$Ci_transition / pcor) * 0.18, 193 | (x$Photosyn(Ci = x$Ci_transition)$ALEAF) * 1.1, 194 | paste0("Ci transition = ", round(x$Ci_transition / pcor, 2)), 195 | cex = 0.6, pos = 4 196 | ) 197 | } 198 | 199 | if(addzeroline){ 200 | abline(h=0, lty=3) 201 | } 202 | 203 | if(addlegend & ! TPUlimit){ 204 | legend("bottomright", c(expression(italic(A)[c]), 205 | expression(italic(A)[j]), 206 | "Limiting rate"), lty=1, lwd=c(lwd[1],lwd[1],lwd[2]), 207 | col= linecols[3:1], bty=legendbty, bg="white", cex = 0.7) 208 | } 209 | if(addlegend & TPUlimit){ 210 | legend("bottomright", c(expression(italic(A)[c]), 211 | expression(italic(A)[j]), 212 | expression(italic(A)[p]), 213 | "Limiting rate" 214 | ), lty=1, lwd=c(rep(lwd[1],3), lwd[2]), 215 | col=c(linecols[3:2],"darkgrey",linecols[1]), bty=legendbty, bg="white",cex = 0.7) 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /R/fitacis.R: -------------------------------------------------------------------------------- 1 | #' Fit multiple A-Ci curves at once 2 | #' 3 | #' @description A convenient function to fit many curves at once, by calling \code{\link{fitaci}} for 4 | #' every group in the dataset. The data provided must include a variable that uniquely identifies each A-Ci curve. 5 | #' 6 | #' @param data Dataframe with Ci, Photo, Tleaf, PPFD (the last two are optional). For \code{fitacis}, 7 | #' also requires a grouping variable. 8 | #' @param group The name of the grouping variable in the dataframe (an A-Ci curve will be fit for each group separately). 9 | #' @param fitmethod Method to fit the A-Ci curve. Either 'default' (Duursma 2015), or 'bilinear'. See Details. 10 | #' @param progressbar Display a progress bar (default is TRUE). 11 | #' @param quiet If TRUE, no messages are written to the screen. 12 | #' @param id Names of variables (quoted, can be a vector) in the original dataset to return as part of 13 | #' the coef() statement. Useful for keeping track of species names, treatment levels, etc. See Details and Examples. 14 | #' @param x For \code{plot.acifits}, an object returned from \code{fitacis} 15 | #' @param xlim,ylim The X and Y axis limits. 16 | #' @param add If TRUE, adds the plots to a current plot. 17 | #' @param how If 'manyplots', produces a single plot for each A-Ci curve. If 'oneplot' overlays all of them. 18 | #' @param highlight If a name of a curve is given (check names(object), where object is returned by acifits), 19 | #' all curves are plotted in grey, with the highlighted one on top. 20 | #' @param linecol Colour(s) to use for the non-highlighted curves (can be a vector). 21 | #' @param linecol_highlight Colour to use for the 'highlighted' curve. 22 | #' @param lty Line type(s), can be a vector (one for each level of the factor, will be recycled). 23 | #' @param colour_by_id If TRUE, uses the 'id' argument to colour the curves in the standard plot (only works when \code{how = 'oneplot'}, see Examples) 24 | #' @param id_legend If \code{colour_by_id} is set, place a legend (topleft) or not. 25 | #' @param what What to plot, either 'model' (the fitted curve), 'data' or 'none'. See examples. 26 | #' @param \dots Further arguments passed to \code{\link{fitaci}} (in the case of \code{fitacis}), or 27 | #' \code{\link{plot.acifit}} (in the case of \code{plot.acifits}). 28 | #' 29 | #' @details 30 | #' \strong{Troubleshooting - } When using the default fitting method (see \code{\link{fitaci}}), it is common that 31 | #' some curves cannot be fit. Usually this indicates that the curve is poor quality and should not be used to 32 | #' estimate photosynthetic capacity, but there are exceptions. The \code{fitacis} function now refits the 33 | #' non-fitting curves with the 'bilinear' method (see \code{fitaci}), which will always return parameter estimates 34 | #' (for better or worse). 35 | #' 36 | #' \strong{Summarizing and plotting - } Like \code{fitaci}, the batch utility \code{fitacis} also has a standard 37 | #' plotting method. By default, it will make a single plot for every curve that you fit (thus generating many plots). 38 | #' Alternatively, use the setting \code{how="oneplot"} (see Examples below) for a single plot. The fitted 39 | #' \strong{coefficients} are extracted with \code{coef}, which gives a dataframe where each row represents 40 | #' a fitted curve (the grouping label is also included). 41 | #' 42 | #' \strong{Adding identifying variables - } after fitting multiple curves, the most logical next step is to 43 | #' analyze the coefficient by some categorical variable (species, treatment, location). You can use the 44 | #' \code{id} argument to store variables from the original dataset in the output. It is important that the 45 | #' 'id' variables take only one value per fitted curve, if this is not the case only the first value of the 46 | #' curve will be stored (this will be rarely useful). See examples. 47 | #' 48 | #' @references 49 | #' Duursma, R.A., 2015. Plantecophys - An R Package for Analysing and Modelling Leaf Gas Exchange Data. 50 | #' PLoS ONE 10, e0143346. doi:10.1371/journal.pone.0143346 51 | #' 52 | #' @examples 53 | #' 54 | #' \dontrun{ 55 | #' # Fit many curves (using an example dataset) 56 | #' # The bilinear method is much faster, but compare using 'default'! 57 | #' fits <- fitacis(manyacidat, "Curve", fitmethod="bilinear") 58 | #' with(coef(fits), plot(Vcmax, Jmax)) 59 | #' 60 | #' # The resulting object is a list, with each component an object as returned by fitaci 61 | #' # So, we can extract one curve: 62 | #' fits[[1]] 63 | #' plot(fits[[1]]) 64 | #' 65 | #' # Plot all curves in separate figures with plot(fits) 66 | #' # Or, in one plot: 67 | #' plot(fits, how="oneplot") 68 | #' 69 | #' # Note that parameters can be passed to plot.acifit. For example, 70 | #' plot(fits, how="oneplot", what="data", col="blue") 71 | #' plot(fits, how="oneplot", add=TRUE, what="model", lwd=c(1,1)) 72 | #' 73 | #' # Other elements can be summarized with sapply. For example, look at the RMSE: 74 | #' rmses <- sapply(fits, "[[", "RMSE") 75 | #' plot(rmses, type='h', ylab="RMSE", xlab="Curve nr") 76 | #' 77 | #' # And plot the worst-fitting curve: 78 | #' plot(fits[[which.max(rmses)]]) 79 | #' 80 | #' # It is very straightforward to summarize the coefficients by a factor variable 81 | #' # that was contained in the original data. In manyacidat, there is a factor variable 82 | #' # 'treatment'. 83 | #' # We first have to refit the curves, using the 'id' argument: 84 | #' fits <- fitacis(manyacidat, "Curve", fitmethod="bilinear", id="treatment") 85 | #' 86 | #' # And now use this to plot Vcmax by treatment. 87 | #' boxplot(Vcmax ~ treatment, data=coef(fits), ylim=c(0,130)) 88 | #' 89 | #' # As of package version 1.4-2, you can also use the id variable for colouring curves, 90 | #' # when plotting all fitted curves in one plot. 91 | #' # Set colours to be used. Also note that the 'id' variable has to be a factor, 92 | #' # colours will be set in order of the levels of the factor. 93 | #' # Set palette of colours: 94 | #' palette(rainbow(8)) 95 | #' 96 | #' # Use colours, add legend. 97 | #' plot(fits, how="oneplot", colour_by_id = TRUE, id_legend=TRUE) 98 | #' 99 | #' } 100 | #' 101 | #' @export 102 | #' @importFrom utils setTxtProgressBar 103 | #' @importFrom utils txtProgressBar 104 | fitacis <- function(data, group, fitmethod=c("default","bilinear"), 105 | progressbar=TRUE, quiet=FALSE, id=NULL, ...){ 106 | 107 | fitmethod <- match.arg(fitmethod) 108 | 109 | if(!group %in% names(data)) 110 | Stop("group variable must be in the dataframe.") 111 | 112 | if(quiet)progressbar <- FALSE 113 | 114 | data$group <- data[,group] 115 | tb <- table(data$group) 116 | 117 | if(any(tb == 0)){ 118 | Stop("Some levels of your group variable have zero observations.", 119 | "\nUse droplevels() or fix data otherwise!") 120 | } 121 | 122 | d <- split(data, data[,"group"]) 123 | ng <- length(d) 124 | fits <- do_fit_bygroup(d, 1:ng, progressbar, fitmethod, id=id, ...) 125 | 126 | if(any(!fits$success)){ 127 | if(!quiet){ 128 | group_fail <- names(d)[!fits$success] 129 | message("The following groups could not be fit with fitmethod='default':") 130 | message(paste(group_fail,collapse="\n")) 131 | } 132 | 133 | # Refit bad curves using the 'bilinear' method 134 | if(fitmethod == "default"){ 135 | if(!quiet)message("Fitting those curves with fitmethod='bilinear'.") 136 | refits <- do_fit_bygroup(d, which(!fits$success), progressbar=FALSE, fitmethod="bilinear", ...) 137 | 138 | fits$fits[!fits$success] <- refits$fits[!fits$success] 139 | } 140 | } 141 | 142 | l <- fits$fits 143 | class(l) <- "acifits" 144 | attributes(l)$groupname <- group 145 | 146 | return(l) 147 | } 148 | 149 | 150 | do_fit_bygroup <- function(d, which, progressbar, fitmethod, ...){ 151 | 152 | ng <- length(d) 153 | success <- vector("logical", length(which)) 154 | 155 | if(progressbar){ 156 | wp <- txtProgressBar(title = "Fitting A-Ci curves", 157 | label = "", min = 0, max = ng, initial = 0, 158 | width = 50, style=3) 159 | } 160 | 161 | fits <- list() 162 | for(i in which){ 163 | f <- try(fitaci(d[[i]], quiet=TRUE, fitmethod=fitmethod, ...), silent=TRUE) 164 | success[i] <- !inherits(f, "try-error") 165 | 166 | fits[[i]] <- if(success[i]) f else NA 167 | if(progressbar)setTxtProgressBar(wp, i) 168 | } 169 | if(progressbar)close(wp) 170 | 171 | names(fits) <- names(d)[which] 172 | 173 | l <- list(fits=fits, success=success) 174 | } 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /R/LeafEnergyBalance.R: -------------------------------------------------------------------------------- 1 | #' Coupled leaf gas exchange model with energy balance 2 | #' @description As \code{\link{Photosyn}}, but calculates the leaf temperature based on the leaf's energy balance. Including sensible and long-wave heat loss, latent heat loss from evaporation, and solar radiation input. 3 | #' 4 | #' #'\strong{Warning:}Do not provide GS as an input to \code{PhotosynEB} directly; the results will not be as expected (filed as issue #27) 5 | #'@details Uses the Penman-Monteith equation to calculate the leaf transpiration rate, and finds Tleaf by solving the leaf energy balance iteratively. In the solution, it is accounted for that stomatal conductance (via the dependence of photosynthesis on Tleaf) and net radiation depend on Tleaf. 6 | #' 7 | #'Also included is the function \code{FindTleaf}, which calculates the leaf temperature if the stomatal conductance is known. The \strong{limitation} to this function is that input stomatal conductance (gs) is not vectorized, i.e. you can only provide one value at a time. 8 | #' 9 | #'@param Tair Air temperature (C) 10 | #'@param VPD The vapour pressure deficit of the air (i.e. not the leaf-to-air VPD) (kPa). 11 | #'@param Wind Wind speed (m s-1) 12 | #'@param Wleaf Leaf width (m) 13 | #'@param StomatalRatio The stomatal ratio (cf. Licor6400 terminology), if it is 1, leaves have stomata only on one side (hypostomatous), 2 for leaves with stomata on both sides (amphistomatous). 14 | #'@param LeafAbs Leaf absorptance of solar radiation (0-1). 15 | #'@param RH The relative humidity of the air (i.e. not calculated with leaf temperature) (in percent). 16 | #'@param gs For \code{FindTleaf}, the stomatal conductance (mol m-2 s-1). 17 | #'@param \dots Further parameters passed to \code{\link{Photosyn}}. Note that Tleaf is not allowed as an input, since that is calculated by \code{PhotosynEB} from energy balance. 18 | #'@export PhotosynEB 19 | #'@rdname PhotosynEB 20 | PhotosynEB <- function(Tair=25, 21 | VPD=1.5, 22 | Wind = 2, 23 | Wleaf = 0.02, 24 | StomatalRatio = 1, # 2 for amphistomatous 25 | LeafAbs = 0.86, 26 | RH=NULL, 27 | ...){ 28 | 29 | 30 | if(!is.null(RH))VPD <- RHtoVPD(RH,Tair) 31 | 32 | # Non-vectorized function declared here. 33 | PhotosynEBfun <- function(Tair, 34 | VPD, 35 | Wind, 36 | Wleaf, 37 | StomatalRatio, # 2 for amphistomatous 38 | LeafAbs, 39 | ...){ # passed to Photosyn 40 | 41 | 42 | m <- match.call(expand.dots=TRUE) 43 | if("Tleaf" %in% names(m)) 44 | Stop("Cannot pass Tleaf to PhotosynEB - it is calculated from energy balance.") 45 | 46 | # Wrapper for Photosyn to find gs only 47 | gsfun <- function(...)Photosyn(...)$GS 48 | 49 | # Find Tleaf. Here, we take into account that Tleaf as solved from 50 | # energy balance affects gs, so this is the second loop to solve for Tleaf. 51 | fx <- function(x, Tair, Wind, VPD, Wleaf, StomatalRatio, LeafAbs, ...){ 52 | newx <- FindTleaf(Tair=Tair, gs=gsfun(Tleaf=x, VPD=VPD, ...), 53 | Wind=Wind, Wleaf=Wleaf, 54 | StomatalRatio=StomatalRatio, LeafAbs=LeafAbs) 55 | (newx - x)^2 56 | } 57 | 58 | Tleaf <- try(optimize(fx, interval=c(max(1, Tair-15), Tair+15), Tair=Tair, Wind=Wind, Wleaf=Wleaf, 59 | VPD=VPD, StomatalRatio=StomatalRatio, LeafAbs=LeafAbs, ...)$minimum) 60 | 61 | failed <- FALSE 62 | if(inherits(Tleaf, "try-error")){ 63 | Tleaf <- Tair 64 | failed <- TRUE 65 | } 66 | 67 | # Now run Photosyn 68 | p <- Photosyn(Tleaf=Tleaf, VPD=VPD, ...) 69 | 70 | # And energy balance components 71 | e <- LeafEnergyBalance(Tleaf=Tleaf, Tair=Tair, gs=p$GS, 72 | PPFD=p$PPFD, VPD=p$VPD, Patm=p$Patm, 73 | Wind=Wind, Wleaf=Wleaf, 74 | StomatalRatio=StomatalRatio, LeafAbs=LeafAbs, 75 | returnwhat="fluxes") 76 | res <- cbind(p,e) 77 | 78 | # Replace ELEAF with energy-balance one. 79 | res$ELEAF <- res$ELEAFeb 80 | res$ELEAFeb <- NULL 81 | res$VPDleaf <- VPDairToLeaf(res$VPD, Tair, res$Tleaf) 82 | res$Tair <- Tair 83 | res$Wind <- Wind 84 | res$failed <- failed 85 | 86 | return(res) 87 | } 88 | 89 | m <- t(mapply(PhotosynEBfun, Tair=Tair, 90 | VPD=VPD, 91 | Wind=Wind, Wleaf=Wleaf, StomatalRatio=StomatalRatio, 92 | LeafAbs=LeafAbs, ..., SIMPLIFY=FALSE)) 93 | 94 | 95 | return(as.data.frame(do.call(rbind, m))) 96 | } 97 | 98 | # The net leaf energy balance, given that we know Tleaf, gs 99 | LeafEnergyBalance <- function(Tleaf = 21.5, Tair = 20, 100 | gs = 0.15, 101 | PPFD = 1500, VPD = 2, Patm = 101, 102 | Wind = 2, Wleaf = 0.02, 103 | StomatalRatio = 1, # 2 for amphistomatous 104 | LeafAbs = 0.5, # in shortwave range, much less than PAR 105 | returnwhat=c("balance","fluxes")){ 106 | 107 | 108 | returnwhat <- match.arg(returnwhat) 109 | 110 | # Constants 111 | Boltz <- 5.67 * 10^-8 # w M-2 K-4 112 | Emissivity <- 0.95 # - 113 | LatEvap <- 2.54 # MJ kg-1 114 | CPAIR <- 1010.0 # J kg-1 K-1 115 | 116 | H2OLV0 <- 2.501e6 # J kg-1 117 | H2OMW <- 18e-3 # J kg-1 118 | AIRMA <- 29.e-3 # mol mass air (kg/mol) 119 | AIRDENS <- 1.204 # kg m-3 120 | UMOLPERJ <- 4.57 121 | DHEAT <- 21.5e-6 # molecular diffusivity for heat 122 | 123 | 124 | 125 | # Density of dry air 126 | AIRDENS <- Patm*1000/(287.058 * Tk(Tair)) 127 | 128 | # Latent heat of water vapour at air temperature (J mol-1) 129 | LHV <- (H2OLV0 - 2.365E3 * Tair) * H2OMW 130 | 131 | # Const s in Penman-Monteith equation (Pa K-1) 132 | SLOPE <- (esat(Tair + 0.1) - esat(Tair)) / 0.1 133 | 134 | # Radiation conductance (mol m-2 s-1) 135 | Gradiation <- 4.*Boltz*Tk(Tair)^3 * Emissivity / (CPAIR * AIRMA) 136 | 137 | # See Leuning et al (1995) PC&E 18:1183-1200 Appendix E 138 | # Boundary layer conductance for heat - single sided, forced convection 139 | CMOLAR <- Patm*1000 / (8.314 * Tk(Tair)) # .Rgas() in package... 140 | Gbhforced <- 0.003 * sqrt(Wind/Wleaf) * CMOLAR 141 | 142 | # Free convection 143 | GRASHOF <- 1.6E8 * abs(Tleaf-Tair) * (Wleaf^3) # Grashof number 144 | Gbhfree <- 0.5 * DHEAT * (GRASHOF^0.25) / Wleaf * CMOLAR 145 | 146 | # Total conductance to heat (both leaf sides) 147 | Gbh <- 2*(Gbhfree + Gbhforced) 148 | 149 | # Heat and radiative conductance 150 | Gbhr <- Gbh + 2*Gradiation 151 | 152 | # Boundary layer conductance for water (mol m-2 s-1) 153 | Gbw <- StomatalRatio * 1.075 * Gbh # Leuning 1995 154 | gw <- gs*Gbw/(gs + Gbw) 155 | 156 | # Longwave radiation 157 | # (positive flux is heat loss from leaf) 158 | Rlongup <- Emissivity*Boltz*Tk(Tleaf)^4 159 | 160 | # Rnet 161 | Rsol <- 2*PPFD/UMOLPERJ # W m-2 162 | Rnet <- LeafAbs*Rsol - Rlongup # full 163 | 164 | # Isothermal net radiation (Leuning et al. 1995, Appendix) 165 | ea <- esat(Tair) - 1000*VPD 166 | ema <- 0.642*(ea/Tk(Tair))^(1/7) 167 | Rnetiso <- LeafAbs*Rsol - (1 - ema)*Boltz*Tk(Tair)^4 # isothermal net radiation 168 | 169 | # Isothermal version of the Penmon-Monteith equation 170 | GAMMA <- CPAIR*AIRMA*Patm*1000/LHV 171 | ET <- (1/LHV) * (SLOPE * Rnetiso + 1000*VPD * Gbh * CPAIR * AIRMA) / (SLOPE + GAMMA * Gbhr/gw) 172 | 173 | # Latent heat loss 174 | lambdaET <- LHV * ET 175 | 176 | # Heat flux calculated using Gradiation (Leuning 1995, Eq. 11) 177 | Y <- 1/(1 + Gradiation/Gbh) 178 | H2 <- Y*(Rnetiso - lambdaET) 179 | 180 | # Heat flux calculated from leaf-air T difference. 181 | # (positive flux is heat loss from leaf) 182 | H <- -CPAIR * AIRDENS * (Gbh/CMOLAR) * (Tair - Tleaf) 183 | 184 | # Leaf-air temperature difference recalculated from energy balance. 185 | # (same equation as above!) 186 | Tleaf2 <- Tair + H2/(CPAIR * AIRDENS * (Gbh/CMOLAR)) 187 | 188 | # Difference between input Tleaf and calculated, this will be minimized. 189 | EnergyBal <- Tleaf - Tleaf2 190 | 191 | if(returnwhat == "balance")return(EnergyBal) 192 | 193 | if(returnwhat == "fluxes"){ 194 | 195 | l <- data.frame(ELEAFeb=1000*ET, Gradiation=Gradiation, Rsol=Rsol, Rnetiso=Rnetiso, Rlongup=Rlongup, H=H, lambdaET=lambdaET, gw=gw, Gbh=Gbh, H2=H2, Tleaf2=Tleaf2) 196 | return(l) 197 | } 198 | } 199 | 200 | 201 | #' @export 202 | #' @rdname PhotosynEB 203 | #' @importFrom stats uniroot 204 | FindTleaf <- function(gs, Tair, ...){ 205 | 206 | Tleaf <- try(uniroot(LeafEnergyBalance, interval=c(Tair-15, Tair+15), 207 | gs=gs, Tair=Tair, ...)$root) 208 | # Tleaf <- optimize(LeafEnergyBalance, interval=c(Tair-15, Tair+15), 209 | # gs=gs, Tair=Tair,...)$minimum 210 | 211 | return(Tleaf) 212 | } 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /R/FARAO.R: -------------------------------------------------------------------------------- 1 | #' FARquhar And Opti 2 | #' @description The numerical solution of the optimal stomatal conductance model, coupled with 3 | #' the Farquhar model of photosynthesis. The model of Medlyn et al. (2011) is an approximation 4 | #' to this full numeric solution. 5 | #' @param lambda The marginal cost of water (mol mol-1) 6 | #' @param Ca The CO2 concentration. 7 | #' @param VPD Vapor pressure deficit (kPa) 8 | #' @param photo Which photosynthesis rate should stomata respond to? Defaults to 'BOTH', i.e. 9 | #' the minimum of Vcmax and Jmax limited rates. 10 | #' @param energybalance If TRUE (Default = FALSE), calculates leaf temperature from energy balance 11 | #' (and its effects on photosynthesis as well as leaf transpiration), using \code{\link{PhotosynEB}}. 12 | #' @param C4 If TRUE, uses the C4 photosynthesis routine (\code{\link{AciC4}}) 13 | #' @param Tair Air temperature (deg C) 14 | #' @param Wind Wind speed (m s-1) (only used if energybalance=TRUE) 15 | #' @param Wleaf Leaf width (m) (only used if energybalance=TRUE) 16 | #' @param StomatalRatio The stomatal ratio (see \code{\link{PhotosynEB}}) (only used if 17 | #' energybalance=TRUE) 18 | #' @param LeafAbs Leaf absorptance (see \code{\link{PhotosynEB}}) (only used if 19 | #' energybalance=TRUE) 20 | #' @param ... All other parameters are passed to \code{\link{Aci}} 21 | #' @author Remko Duursma 22 | #' @details This model finds the Ci that maximizes A - lambda*E (Cowan & Farquhar 1977, 23 | #' see also Medlyn et al. 2011). The new function FARAO2 is a much simpler (and probably 24 | #' more stable) implementation, based on Buckley et al. 2014 (P,C&E). Both functions 25 | #' are provided, as FARAO has a few more options than FARAO2, at the moment. 26 | #' @references 27 | #' Buckley, T.N., Martorell, S., Diaz-Espejo, A., Tomas, M., Medrano, H., 2014. Is stomatal 28 | #' conductance optimized over both time and space in plant crowns? A field test in 29 | #' grapevine (Vitis vinifera). Plant Cell Environ doi:10.1111/pce.12343 30 | #' 31 | #' Cowan, I. and G.D. Farquhar. 1977. Stomatal function in relation to leaf metabolism 32 | #' and environment. Symposia of the Society for Experimental Biology. 31:471-505. 33 | #' 34 | #' Medlyn, B.E., R.A. Duursma, D. Eamus, D.S. Ellsworth, I.C. Prentice, C.V.M. Barton, 35 | #' K.Y. Crous, P. De Angelis, M. Freeman and L. Wingate. 2011. Reconciling the optimal 36 | #' and empirical approaches to modelling stomatal conductance. Global Change Biology. 17:2134-2144. 37 | #' @export 38 | #' @importFrom stats optimize 39 | #' @rdname FARAO 40 | FARAO <- function(lambda=0.002, Ca=400, VPD=1, 41 | photo=c("BOTH","VCMAX","JMAX"), 42 | energybalance=FALSE, 43 | C4=FALSE, 44 | Tair=25, 45 | Wind=2, 46 | Wleaf=0.02, 47 | StomatalRatio=1, 48 | LeafAbs=0.86, 49 | ...){ 50 | 51 | photo <- match.arg(photo) 52 | 53 | # Non-vectorized form. 54 | FARAOfun <- function(lambda, Ca, VPD, 55 | photo, 56 | energybalance, C4, 57 | Tair, 58 | Wind, 59 | Wleaf, 60 | StomatalRatio, 61 | LeafAbs, 62 | ...){ 63 | 64 | 65 | if(!energybalance){ 66 | fx <- function(Ca,...)optimize(OPTfun, interval=c(0,Ca), 67 | maximum=TRUE,Ca=Ca, 68 | ...)$maximum 69 | optimalcis <- mapply(fx,Ca=Ca,lambda=lambda, photo=photo, C4=C4, VPD=VPD,...) 70 | 71 | res <- as.data.frame(OPTfun(Ci=optimalcis, retobjfun=FALSE, 72 | Ca=Ca, photo=photo, C4=C4, VPD=VPD,...)) 73 | } else { 74 | fx <- function(Ca,...)optimize(OPTfunEB, interval=c(0,Ca), 75 | maximum=TRUE,Ca=Ca, 76 | ...)$maximum 77 | optimalcis <- mapply(fx,Ca=Ca,lambda=lambda, photo=photo, C4=C4, VPD=VPD, 78 | Tair=Tair, 79 | Wind=Wind, 80 | Wleaf=Wleaf, 81 | StomatalRatio=StomatalRatio, 82 | LeafAbs=LeafAbs, 83 | ...) 84 | 85 | res <- as.data.frame(OPTfunEB(Ci=optimalcis, retobjfun=FALSE, 86 | Ca=Ca, photo=photo, C4=C4, VPD=VPD, 87 | Tair=Tair, 88 | Wind=Wind, 89 | Wleaf=Wleaf, 90 | StomatalRatio=StomatalRatio, 91 | LeafAbs=LeafAbs,...)) 92 | } 93 | 94 | return(res) 95 | } 96 | 97 | f <- t(mapply(FARAOfun, lambda=lambda, Ca=Ca, VPD=VPD, 98 | photo=photo, 99 | energybalance=energybalance, C4=C4, 100 | Tair=Tair, 101 | Wind=Wind, 102 | Wleaf=Wleaf, 103 | StomatalRatio=StomatalRatio, 104 | LeafAbs=LeafAbs, ..., SIMPLIFY=FALSE)) 105 | 106 | return(as.data.frame(do.call(rbind,f))) 107 | 108 | } 109 | 110 | # This function returns the 'objective function' A - lambda*E 111 | # This is to be optimized by the next function by varying ci. 112 | OPTfun <- function(Ci, # mu mol mol-1 113 | lambda=0.002, # mol mol-1 114 | Ca=400, # mu mol mol-1 115 | VPD=1, # kPa 116 | Patm=101, # ambient pressure, kPa 117 | photo=c("BOTH","VCMAX","JMAX"), 118 | energybalance=FALSE, 119 | retobjfun=TRUE, # if false, returns A, g and E (otherwise sum(A-lambda*E)) 120 | C4=FALSE, 121 | ...){ 122 | 123 | GCtoGW <- 1.57 124 | VPDmol <- VPD/Patm 125 | 126 | photo <- match.arg(photo) 127 | 128 | # Given a Ci, calculate photosynthetic rate 129 | if(!C4) 130 | # note that VPD does not do anything, just for consistency in I/O 131 | run <- Aci(Ci=Ci, VPD=VPD, ...) 132 | else 133 | run <- AciC4(Ci, VPD=VPD, ...) 134 | 135 | if(photo == "BOTH")A <- run$ALEAF 136 | if(photo == "VCMAX")A <- run$Ac 137 | if(photo == "JMAX")A <- run$Aj 138 | 139 | # Given Ci and A, calculate gs (diffusion constraint) 140 | gs <- GCtoGW * A / (Ca - Ci) 141 | 142 | # Transpiration rate 143 | E <- gs*VPDmol 144 | 145 | 146 | # Objective function to be maximized (Cowan-Farquhar condition) 147 | objfun <- 10^-6*A - lambda*E 148 | 149 | if(retobjfun)return(objfun) 150 | 151 | if(!retobjfun)return(list( Ci=Ci, ALEAF=A, GS=gs, ELEAF=E*1000, Ac=run$Ac, Aj=run$Aj, 152 | Rd=run$Rd, VPD=VPD, Tleaf=run$Tleaf, Ca=Ca, PPFD=run$PPFD )) 153 | } 154 | 155 | 156 | OPTfunEB <- function(Ci, # mu mol mol-1 157 | lambda=0.002, # mol mol-1 158 | Ca=400, # mu mol mol-1 159 | VPD=1.5, # AIR VPD! kPa 160 | Patm=101, # ambient pressure, kPa 161 | Tair=25, 162 | Wind=2, 163 | Wleaf=0.02, 164 | StomatalRatio=1, 165 | LeafAbs=0.86, 166 | photo=c("BOTH","VCMAX","JMAX"), 167 | retobjfun=TRUE, # if false, returns A, g and E (otherwise sum(A-lambda*E)) 168 | C4=FALSE, 169 | ...){ 170 | 171 | GCtoGW <- 1.57 172 | VPDmol <- VPD/Patm 173 | 174 | photo <- match.arg(photo) 175 | 176 | gsfun <- function(Ci, VPD, returnwhat=c("gs","all"), ...){ 177 | 178 | returnwhat <- match.arg(returnwhat) 179 | 180 | # Given a Ci, calculate photosynthetic rate 181 | if(!C4) 182 | # note that VPD does not do anything, just for consistency in I/O 183 | run <- Aci(Ci, VPD=VPD, ...) 184 | else 185 | run <- AciC4(Ci, VPD=VPD, ...) 186 | 187 | if(photo == "BOTH")A <- run$ALEAF 188 | if(photo == "VCMAX")A <- run$Ac 189 | if(photo == "JMAX")A <- run$Aj 190 | 191 | # Given Ci and A, calculate gs (diffusion constraint) 192 | gs <- GCtoGW * A / (Ca - Ci) 193 | 194 | if(returnwhat == "gs")return(gs) 195 | if(returnwhat == "all")return(list(run=run, GS=gs, A=A)) 196 | } 197 | 198 | # Find Tleaf. Here, we take into account that Tleaf as solved from 199 | # energy balance affects gs (because it affects) 200 | fx <- function(x, Ci, Tair, Wind, VPD, Wleaf, StomatalRatio, LeafAbs, ...){ 201 | newx <- FindTleaf(Tair=Tair, gs=gsfun(Ci=Ci, Tleaf=x, VPD=VPD, ...), 202 | Wind=Wind, Wleaf=Wleaf, 203 | StomatalRatio=StomatalRatio, LeafAbs=LeafAbs) 204 | (newx - x)^2 205 | } 206 | 207 | Tleaf <- optimize(fx, interval=c(Tair-10, Tair+10), Ci=Ci, Tair=Tair, 208 | Wind=Wind, VPD=VPD, Wleaf=Wleaf, 209 | StomatalRatio=StomatalRatio, LeafAbs=LeafAbs, ...)$minimum 210 | 211 | z <- gsfun(Ci=Ci, Tleaf=Tleaf, VPD=VPD, returnwhat="all",...) 212 | GS <- z$GS 213 | A <- z$A 214 | 215 | # And energy balance components 216 | e <- LeafEnergyBalance(Tleaf=Tleaf, Tair=Tair, gs=GS, 217 | PPFD=z$run$PPFD, VPD=VPD, Patm=z$run$Patm, 218 | Wind=Wind, Wleaf=Wleaf, 219 | StomatalRatio=StomatalRatio, LeafAbs=LeafAbs, 220 | returnwhat="fluxes") 221 | 222 | E <- e$ELEAFeb 223 | 224 | # Objective function to be maximized (Cowan-Farquhar) 225 | objfun <- 10^-6*A - lambda*E/1000 226 | 227 | if(retobjfun)return(objfun) 228 | 229 | if(!retobjfun)return(list( Ci=Ci, ALEAF=A, GS=GS, ELEAF=E, Ac=z$run$Ac, Aj=z$run$Aj, 230 | Rd=z$run$Rd, VPD=VPD, Tleaf=Tleaf, Ca=Ca, PPFD=z$run$PPFD )) 231 | } 232 | 233 | 234 | getdAdE <- function(Ci,...,energybalance=FALSE, 235 | returnwhat=c("dAdE","both")){ 236 | 237 | returnwhat <- match.arg(returnwhat) 238 | delta <- 1e-03 239 | 240 | if(energybalance){ 241 | r1 <- PhotosynEB(Ci=Ci, ...) 242 | r2 <- PhotosynEB(Ci=Ci+delta, ...) 243 | } else { 244 | r1 <- Photosyn(Ci=Ci, ...) 245 | r2 <- Photosyn(Ci=Ci+delta, ...) 246 | } 247 | 248 | dA <- r2$ALEAF - r1$ALEAF 249 | dE <- r2$ELEAF - r1$ELEAF 250 | 251 | if(returnwhat == "dAdE") 252 | return(dA/dE) 253 | else 254 | return(c(dA=dA, dE=dE)) 255 | 256 | } 257 | 258 | #' @rdname FARAO 259 | #' @export 260 | FARAO2 <- function(lambda=0.002, Ca=400, energybalance=FALSE, ...){ 261 | 262 | 263 | faraofun <- function(lambda,Ca,energybalance,...){ 264 | f <- function(x, ...)(getdAdE(x, energybalance=energybalance, ...) - lambda*1000)^2 265 | 266 | CI <- try(optimize(f, c(80, Ca-0.1), ...)$minimum) 267 | if(inherits(CI, "try-error"))return(NULL) 268 | 269 | if(energybalance) 270 | p <- PhotosynEB(Ci=CI, ...) 271 | else 272 | p <- Photosyn(Ci=CI, ...) 273 | 274 | return(p) 275 | } 276 | 277 | m <- mapply(faraofun, lambda=lambda, Ca=Ca, 278 | energybalance=energybalance, ..., SIMPLIFY=FALSE) 279 | 280 | return(do.call(rbind, m)) 281 | } 282 | 283 | 284 | -------------------------------------------------------------------------------- /man/Photosyn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/photosyn.R 3 | \name{Photosyn} 4 | \alias{Photosyn} 5 | \alias{Aci} 6 | \title{Coupled leaf gas exchange model} 7 | \usage{ 8 | Photosyn( 9 | VPD = 1.5, 10 | Ca = 400, 11 | PPFD = 1500, 12 | Tleaf = 25, 13 | Patm = 100, 14 | RH = NULL, 15 | gsmodel = c("BBOpti", "BBLeuning", "BallBerry", "BBdefine"), 16 | g1 = 4, 17 | g0 = 0, 18 | gk = 0.5, 19 | vpdmin = 0.5, 20 | D0 = 5, 21 | GS = NULL, 22 | BBmult = NULL, 23 | alpha = 0.24, 24 | theta = 0.85, 25 | Jmax = 100, 26 | Vcmax = 50, 27 | gmeso = NULL, 28 | TPU = 1000, 29 | alphag = 0, 30 | Rd0 = 0.92, 31 | Q10 = 1.92, 32 | Rd = NULL, 33 | TrefR = 25, 34 | Rdayfrac = 1, 35 | EaV = 58550, 36 | EdVC = 2e+05, 37 | delsC = 629.26, 38 | EaJ = 29680, 39 | EdVJ = 2e+05, 40 | delsJ = 631.88, 41 | GammaStar = NULL, 42 | Km = NULL, 43 | Ci = NULL, 44 | Tcorrect = TRUE, 45 | returnParsOnly = FALSE, 46 | whichA = c("Ah", "Amin", "Ac", "Aj") 47 | ) 48 | 49 | Aci(Ci, ...) 50 | } 51 | \arguments{ 52 | \item{VPD}{Vapour pressure deficit (kPa) (not needed when RH provided)} 53 | 54 | \item{Ca}{Atmospheric CO2 concentration (ppm)} 55 | 56 | \item{PPFD}{Photosynthetic photon flux density ('PAR') (mu mol m-2 s-1)} 57 | 58 | \item{Tleaf}{Leaf temperature (degrees C)} 59 | 60 | \item{Patm}{Atmospheric pressure (kPa) (but see warning below!)} 61 | 62 | \item{RH}{Relative humidity (in \%) (not needed when VPD provided)} 63 | 64 | \item{gsmodel}{One of BBOpti (Medlyn et al. 2011), BBLeuning (Leuning 1995), 65 | BallBerry (Ball et al. 1987), or BBdefine (for full control; see Details).} 66 | 67 | \item{g0, g1}{Parameters of Ball-Berry type stomatal conductance models.} 68 | 69 | \item{gk}{Optional, exponent of VPD in gs model (Duursma et al. 2013)} 70 | 71 | \item{vpdmin}{Below vpdmin, VPD=vpdmin, to avoid very high gs.} 72 | 73 | \item{D0}{Parameter for the BBLeuning stomatal conductance model.} 74 | 75 | \item{GS}{Optionally, stomatal conductance (to H2O). If provided, \code{Photosyn} 76 | calculates Ci and photosynthesis. See Details.} 77 | 78 | \item{BBmult}{Optional, only used when \code{gsmodel = "BBdefine"}, see Details.} 79 | 80 | \item{alpha}{Quantum yield of electron transport (mol mol-1)} 81 | 82 | \item{theta}{Shape of light response curve.} 83 | 84 | \item{Jmax}{Maximum rate of electron transport at 25 degrees C (mu mol m-2 s-1)} 85 | 86 | \item{Vcmax}{Maximum carboxylation rate at 25 degrees C (mu mol m-2 s-1)} 87 | 88 | \item{gmeso}{Mesophyll conductance (mol m-2 s-1). If not NULL (the default), Vcmax and 89 | Jmax are chloroplastic rates.} 90 | 91 | \item{TPU}{Triose-phosphate utilization rate (mu mol m-2 s-1); optional.} 92 | 93 | \item{alphag}{Fraction of glycolate not returned to the chloroplast; parameter in 94 | TPU-limited photosynthesis (optional, only to be used when TPU is provided) (0 - 1)} 95 | 96 | \item{Rd0}{Day respiration rate at reference temperature (\code{TrefR}). Must be a positive value.} 97 | 98 | \item{Q10}{Temperature sensitivity of Rd.} 99 | 100 | \item{Rd}{Day respiration rate (mu mol m-2 s-1), optional (if not provided, calculated 101 | from Tleaf, Rd0, Q10 and TrefR). Must be a positive value (an error occurs when a 102 | negative value is supplied).} 103 | 104 | \item{TrefR}{Reference temperature for Rd (Celcius).} 105 | 106 | \item{Rdayfrac}{Ratio of Rd in the light vs. in the dark.} 107 | 108 | \item{EaV, EdVC, delsC}{Vcmax temperature response parameters} 109 | 110 | \item{EaJ, EdVJ, delsJ}{Jmax temperature response parameters} 111 | 112 | \item{Km, GammaStar}{Optionally, provide Michaelis-Menten coefficient for Farquhar 113 | model, and Gammastar. If not provided, they are calculated with a built-in function 114 | of leaf temperature.} 115 | 116 | \item{Ci}{Optional, intercellular CO2 concentration (ppm). If not provided, 117 | calculated via gs model.} 118 | 119 | \item{Tcorrect}{If TRUE, corrects input Vcmax and Jmax for actual Tleaf (if FALSE, 120 | assumes the provided Vcmax and Jmax are at the Tleaf provided). \strong{Warning} : since package 121 | version 1.4, the default parameters have been adjusted (see Details).} 122 | 123 | \item{returnParsOnly}{If TRUE, returns calculated Vcmax,Jmax,Km and GammaStar based on 124 | leaf temperature.} 125 | 126 | \item{whichA}{Which assimilation rate does gs respond to?} 127 | 128 | \item{\dots}{Further arguments passed to \code{Photosyn}} 129 | } 130 | \value{ 131 | Returns a dataframe. 132 | } 133 | \description{ 134 | A coupled photosynthesis - stomatal conductance model, based on the 135 | Farquhar model of photosynthesis, and a Ball-Berry type model of stomatal conductance. 136 | Includes options for temperature sensitivity of photosynthetic parameters, day respiration 137 | (optionally calculated from leaf temperature), and mesophyll conductance. 138 | } 139 | \details{ 140 | The coupled photosynthesis - stomatal conductance model finds the intersection between 141 | the supply of CO2 by diffusion, and the demand for CO2 by photosynthesis. See Farquhar and 142 | Sharkey (1982) for basic description of this type of model, Duursma (2015) for more details 143 | on the implementation in the \code{plantecophys} package, and Duursma et al. (2014) for an 144 | example application (that uses this implementation). 145 | 146 | \strong{Photosynthesis model and temperature response} - 147 | The model of Farquhar et al. (1980) is used to estimate the dependence of leaf net 148 | photosynthesis rate (ALEAF) on intercellular CO2 concentration (Ci), accounting for all 149 | three limitations (electron transport, carboxylation, and TPU limitation). The equations 150 | for the temperature response of photosynthetic parameters, including Vcmax, Jmax, 151 | Gammastar, and Km follow Medlyn et al. (2002). However, \strong{note that the default 152 | temperature response parameter values are not taken from Medlyn, and likely will have to 153 | be adjusted for your situation}. \strong{Warning} : since package version 1.4, the default 154 | parameters have been adjusted. The new parameter values (EaV, EdVJ, delSJ, etc.) were based 155 | on a comprehensive literature review. See vignette("new_T_responses") or the article on 156 | remkoduursma.github.io/plantecophys. 157 | 158 | #' By default, the \code{Photosyn} function returns the hyperbolic minimum of Vcmax and 159 | Jmax-limited photosynthetic rates, as well as the hyperbolic minimum of Jmax-limited and 160 | TPU-limited rates. This approach avoids the discontinuity at the transition between the 161 | two rates (thus allowing use of \code{Photosyn} and \code{fitaci} in optimization or 162 | fitting routines). The individual rates (Ac, Aj and Ap) are also returned as output 163 | should they be needed. Note that those rates are output as gross photosynthetic rates 164 | (leaf respiration has to be subtracted to give net leaf photosynthesis). 165 | 166 | \strong{Coupled leaf gas exchange} 167 | When Ci is not provided, Ci is calculated from the intersection between the 'supply' and 168 | 'demand', where 'demand' is given by the Farquhar model of photosynthesis (A=f(Ci)), and 169 | supply by the stomatal conductance. The latter is, by default, estimated using the 170 | stomatal conductance model of Medlyn et al. (2011), but two other models are provided 171 | as well (Ball-Berry and Leuning, see \code{gsmodel} argument). Otherwise, stomatal 172 | conductance may be directly provided via the \code{GS} argument. 173 | 174 | \strong{Stomatal conductance models} - 175 | At the moment, three stomatal conductance models are implemented. The 'BBOpti' model 176 | is a slightly more general form of the model of Medlyn et al. 2011 (see Duursma et al. 177 | 2013). It is given by (in notation of the parameters and output 178 | variables of \code{Photosyn}), 179 | 180 | \deqn{GS = g0 + 1.6*(1 + g1/D^(1-gk))*ALEAF/CA} 181 | 182 | where \code{gk = 0.5} if stomata behave optimally (cf. Medlyn et al. 2011). 183 | 184 | The 'BBLeuning' model is that of Leuning (1995). It is given by, 185 | 186 | \deqn{GS = g0 + g1*ALEAF/(Ca * (1 + VPD/D0))} 187 | 188 | Note that this model also uses the g1 parameter, but it needs to be set to 189 | a much higher value to be comparable in magnitude to the BBOpti model. 190 | 191 | The 'BallBerry' model is that of Ball et al. (1987). It is given by, 192 | 193 | \deqn{GS = g0 + g1*RH*ALEAF/Ca} 194 | 195 | Where RH is relative humidity. Again, the g1 value is not comparable to that 196 | used in the previous two models. 197 | 198 | Finally, \code{Photosyn} provides a very flexible Ball-Berry model, where 199 | the multiplier has to be specified by the user, the model is: 200 | 201 | \deqn{GS = g0 + BBmult*ALEAF} 202 | 203 | This interface can be used to quickly simulate what happens if stomata do 204 | not respond to humidity at all (in which case BBmult=g1/Ca, or ca. 5/400), or 205 | to use the Tuzet model of stomatal conductance inside another model that provides 206 | the leaf water potential function. 207 | 208 | For the full numerical solution to the Cowan-Farquhar optimization, use 209 | the \code{\link{FARAO}} function (which was used in Medlyn et al. 2011 for 210 | comparison to the approximation there presented). See Duursma (2015) for more details. 211 | 212 | \strong{Mesophyll conductance} - 213 | 214 | If the mesophyll conductance \code{gmeso} is provided as an input, it is assumed 215 | that Vcmax and Jmax are the chloroplastic rates, and leaf photosynthesis is 216 | calculated following the equations from Ethier and Livingston (2004). When very 217 | low mesophyll conductance rates are input, the model may return poor solutions 218 | (and sometimes they may not exist). 219 | 220 | \strong{Simulating A-Ci curves} 221 | 222 | If Ci is provided as an input, this function calculates an A-Ci curve. For 223 | example, you may do \code{Photosyn(Ci=300)}, for which the function \code{Aci} 224 | is included as a shortcut (\code{Aci(300)}). 225 | 226 | \strong{Atmospheric pressure} - 227 | 228 | A correction for atmospheric pressure (Patm) is implemented in \code{\link{fitaci}}, 229 | but \strong{not in Photosyn}. In \code{fitaci}, the necessary corrections are applied 230 | so that estimated Vcmax and Jmax are expressed at standard pressure (Patm=100kPa). 231 | In Photosyn, however, the corrections are much more complicated and tend to be very 232 | small, because effects of Patm on partial pressures are largely offset by increases 233 | in diffusivity (Terashima et al. 1995, Gale 1973). 234 | 235 | Note that Patm is an argument to the Photosyn function, but it only affects 236 | calculations of Km and GammaStar (as used by fitaci), and transpiration rate. 237 | Setting only Patm \strong{does not correct for atmospheric pressure effects on 238 | photosynthesis rates}. 239 | 240 | The simulation of limitation of the photosynthetic rate to triose-phosphate 241 | utilization follows details in Ellsworth et al. (2015), their Eq. 7. Note that 242 | the parameter \code{alphag} is set to zero by default. 243 | } 244 | \examples{ 245 | # Run the coupled leaf gas exchange model, set only a couple of parameters 246 | Photosyn(VPD=2, g1=4, Ca=500) 247 | 248 | # It is easy to set multiple values for inputs (and these can be mixed with single inputs); 249 | r <- Photosyn(VPD=seq(0.5, 4, length=25), Vcmax=50, Jmax=100) 250 | with(r, plot(VPD, ALEAF, type='l')) 251 | 252 | # Set the mesophyll conductance 253 | run1 <- Photosyn(PPFD=seq(50,1000,length=25), gmeso=0.15, Vcmax=40, Jmax=85) 254 | with(run1, plot(PPFD, GS, type='l')) 255 | 256 | # Run A-Ci curve only (provide Ci instead of calculating it). 257 | arun1 <- Aci(Ci=seq(50, 1200, length=101), Vcmax=40, Jmax=85) 258 | arun2 <- Aci(Ci=seq(50, 1200, length=101), Vcmax=30, Jmax=70) 259 | with(arun1, plot(Ci, ALEAF, type='l')) 260 | with(arun2, points(Ci, ALEAF, type='l', lty=5)) 261 | 262 | # Find the intersection between supply of CO2 and demand for CO2 (cf. Farquhar and Sharkey 1982). 263 | 264 | # Set some parameters 265 | gs <- 0.2 # stomatal conductance to H2O 266 | Ca <- 400 # ambient CO2 267 | gctogw <- 1.57 # conversion 268 | gc <- gs / gctogw # stomatal conductance to CO2 269 | 270 | # Demand curve (Farquhar model) 271 | p <- Aci(seq(60,500,length=101), Ca=400) 272 | 273 | # Provide stomatal conductance as input, gives intersection point. 274 | g <- Photosyn(GS=gs, Ca=Ca) 275 | 276 | # Intersection point visualized 277 | par(yaxs="i") 278 | with(p, plot(Ci, ALEAF, type='l', ylim=c(0,max(ALEAF)))) 279 | with(g, points(Ci, ALEAF, pch=19, col="red")) 280 | abline(gc * Ca, -gc, lty=5) 281 | 282 | legend("topleft", c(expression("Demand:"~~A==f(C[i])), 283 | expression("Supply:"~~A==g[c]*(C[a]-C[i])), 284 | "Operating point"), 285 | lty=c(1,5,-1),pch=c(-1,-1,19), 286 | col=c("black","black","red"), 287 | bty='n', cex=0.9) 288 | } 289 | \references{ 290 | Duursma, R.A., Payton, P., Bange, M.P., Broughton, K.J., Smith, R.A., Medlyn, B.E., Tissue, 291 | D. T., 2013, Near-optimal response of instantaneous transpiration efficiency to vapour 292 | pressure deficit, temperature and [CO2] in cotton (Gossypium hirsutum L.). Agricultural 293 | and Forest Meteorology 168 : 168 - 176. 294 | 295 | Duursma, R.A., Barton, C.V.M., Lin, Y.-S., Medlyn, B.E., Eamus, D., Tissue, D.T., Ellsworth, 296 | D.S., McMurtrie, R.E., 2014. The peaked response of transpiration rate to vapour pressure 297 | deficit in field conditions can be explained by the temperature optimum of photosynthesis. 298 | Agricultural and Forest Meteorology 189 - 190, 2-10. doi:10.1016/j.agrformet.2013.12.007 299 | 300 | Duursma, R.A., 2015. Plantecophys - An R Package for Analysing and Modelling Leaf Gas 301 | Exchange Data. PLoS ONE 10, e0143346. doi:10.1371/journal.pone.0143346 302 | 303 | Ellsworth, D.S., Crous, K.Y., Lambers, H., Cooke, J., 2015. Phosphorus recycling in 304 | photorespiration maintains high photosynthetic capacity in woody species. Plant Cell 305 | Environ 38, 1142-1156. doi:10.1111/pce.12468 306 | 307 | Ethier, G. and N. Livingston. 2004. On the need to incorporate sensitivity to CO2 308 | transfer conductance into the Farquhar von Caemmerer Berry leaf photosynthesis model. 309 | Plant, Cell & Environment. 27:137-153. 310 | 311 | Farquhar, G.D., S. Caemmerer and J.A. Berry. 1980. A biochemical model of photosynthetic 312 | CO2 assimilation in leaves of C3 species. Planta. 149:78-90. 313 | 314 | Farquhar, G. D., & Sharkey, T. D. (1982). Stomatal conductance and photosynthesis. 315 | Annual review of plant physiology, 33(1), 317-345. 316 | 317 | Gale, J., 1972. Availability of Carbon Dioxide for Photosynthesis at High Altitudes: 318 | Theoretical Considerations. Ecology 53, 494-497. doi:10.2307/1934239 319 | 320 | Leuning, R. 1995. A critical-appraisal of a combined stomatal-photosynthesis model for 321 | C-3 plants. Plant Cell and Environment. 18:339-355. 322 | 323 | Medlyn, B.E., E. Dreyer, D. Ellsworth, M. Forstreuter, P.C. Harley, M.U.F. Kirschbaum, 324 | X. Le Roux, P. Montpied, J. Strassemeyer, A. Walcroft, K. Wang and D. Loustau. 2002. 325 | Temperature response of parameters of a biochemically based model of photosynthesis. 326 | II. A review of experimental data. Plant Cell and Environment. 25:1167-1179. 327 | 328 | Medlyn, B.E., R.A. Duursma, D. Eamus, D.S. Ellsworth, I.C. Prentice, C.V.M. Barton, 329 | K.Y. Crous, P. De Angelis, M. Freeman and L. Wingate. 2011. Reconciling the optimal 330 | and empirical approaches to modelling stomatal conductance. Global Change Biology. 331 | 17:2134-2144. 332 | 333 | Terashima, I., Masuzawa, T., Ohba, H., Yokoi, Y., 1995. Is photosynthesis suppressed at 334 | higher elevations due to low CO2 pressure? Ecology 76, 2663-2668. doi:10.2307/2265838 335 | } 336 | \seealso{ 337 | \code{\link{FARAO}}, \code{\link{fitaci}}, \code{\link{AciC4}} 338 | } 339 | -------------------------------------------------------------------------------- /man/fitaci.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitaci.R, R/fitaci_methods.R 3 | \name{fitaci} 4 | \alias{fitaci} 5 | \alias{plot.acifit} 6 | \title{Fit the Farquhar-Berry-von Caemmerer model of leaf photosynthesis} 7 | \usage{ 8 | fitaci( 9 | data, 10 | varnames = list(ALEAF = "A", Tleaf = "TleafCnd", Ci = "Ci", PPFD = "Qin", Rd = "Rd"), 11 | Tcorrect = TRUE, 12 | Patm = 100, 13 | citransition = NULL, 14 | quiet = FALSE, 15 | startValgrid = TRUE, 16 | fitmethod = c("default", "bilinear", "onepoint"), 17 | algorithm = "default", 18 | fitTPU = FALSE, 19 | alphag = 0, 20 | useRd = FALSE, 21 | PPFD = NULL, 22 | Tleaf = NULL, 23 | alpha = 0.24, 24 | theta = 0.85, 25 | gmeso = NULL, 26 | EaV = 82620.87, 27 | EdVC = 0, 28 | delsC = 645.1013, 29 | EaJ = 39676.89, 30 | EdVJ = 2e+05, 31 | delsJ = 641.3615, 32 | GammaStar = NULL, 33 | Km = NULL, 34 | id = NULL, 35 | ... 36 | ) 37 | 38 | \method{plot}{acifit}( 39 | x, 40 | what = c("data", "model", "none"), 41 | xlim = NULL, 42 | ylim = NULL, 43 | whichA = c("Ac", "Aj", "Amin", "Ap"), 44 | add = FALSE, 45 | pch = 19, 46 | addzeroline = TRUE, 47 | addlegend = !add, 48 | legendbty = "n", 49 | transitionpoint = TRUE, 50 | linecols = c("#E41A1C", "#377EB8", "#4DAF4A"), 51 | lwd = c(2.5, 2.5), 52 | lty = 1, 53 | ... 54 | ) 55 | } 56 | \arguments{ 57 | \item{data}{Dataframe with Ci, Photo, Tleaf, PPFD (the last two are optional). For \code{fitacis}, 58 | also requires a grouping variable.} 59 | 60 | \item{varnames}{List of names of variables in the dataset (see Details).} 61 | 62 | \item{Tcorrect}{If TRUE, Vcmax and Jmax are corrected to 25C. Otherwise, Vcmax and Jmax 63 | are estimated at measurement temperature. \strong{Warning} : since package version 1.4, the default parameters have been adjusted (see Details).} 64 | 65 | \item{Patm}{Atmospheric pressure (kPa)} 66 | 67 | \item{citransition}{If provided, fits the Vcmax and Jmax limited regions separately (see Details).} 68 | 69 | \item{quiet}{If TRUE, no messages are written to the screen.} 70 | 71 | \item{startValgrid}{If TRUE (the default), uses a fine grid of starting values to increase 72 | the chance of finding a solution.} 73 | 74 | \item{fitmethod}{Method to fit the A-Ci curve. Either 'default' (Duursma 2015), 'bilinear' (See Details), or 'onepoint' (De Kauwe et al. 2016).} 75 | 76 | \item{algorithm}{Passed to \code{\link{nls}}, sets the algorithm for finding parameter values.} 77 | 78 | \item{fitTPU}{Logical (default FALSE). Attempt to fit TPU limitation (fitmethod set to 'bilinear' 79 | automatically if used). See Details.} 80 | 81 | \item{alphag}{When estimating TPU limitation (with \code{fitTPU}), an 82 | additional parameter (see Details).} 83 | 84 | \item{useRd}{If Rd provided in data, and useRd=TRUE (default is FALSE), uses measured Rd in 85 | fit. Otherwise it is estimated from the fit to the A-Ci curve.} 86 | 87 | \item{PPFD}{Photosynthetic photon flux density ('PAR') (mu mol m-2 s-1)} 88 | 89 | \item{Tleaf}{Leaf temperature (degrees C)} 90 | 91 | \item{alpha}{Quantum yield of electron transport (mol mol-1)} 92 | 93 | \item{theta}{Shape of light response curve.} 94 | 95 | \item{gmeso}{Mesophyll conductance (mol m-2 s-1 bar-1). If not NULL (the default), Vcmax 96 | and Jmax are chloroplastic rates.} 97 | 98 | \item{EaV, EdVC, delsC}{Vcmax temperature response parameters} 99 | 100 | \item{EaJ, EdVJ, delsJ}{Jmax temperature response parameters} 101 | 102 | \item{Km, GammaStar}{Optionally, provide Michaelis-Menten coefficient for Farquhar model, and 103 | Gammastar. If not provided, they are calculated with a built-in function of leaf temperature.} 104 | 105 | \item{id}{Names of variables (quoted, can be a vector) in the original dataset to be stored 106 | in the result. Most useful when using \code{\link{fitacis}}, see there for examples of its use.} 107 | 108 | \item{\dots}{Further arguments (ignored at the moment).} 109 | 110 | \item{x}{For plot.acifit, an object returned by \code{fitaci}} 111 | 112 | \item{what}{The default is to plot both the data and the model fit, or specify 'data' or 113 | 'model' to plot one of them, or 'none' for neither (only the plot region is set up)} 114 | 115 | \item{xlim}{Limits for the X axis, if left blank estimated from data} 116 | 117 | \item{ylim}{Limits for the Y axis, if left blank estimated from data} 118 | 119 | \item{whichA}{By default all photosynthetic rates are plotted (Aj=Jmax-limited 120 | (blue), Ac=Vcmax-limited (red), Hyperbolic minimum (black)), TPU-limited rate 121 | (Ap, if estimated in the fit). Or, specify one or two of them.} 122 | 123 | \item{add}{If TRUE, adds to the current plot} 124 | 125 | \item{pch}{The plotting symbol for the data} 126 | 127 | \item{addzeroline}{If TRUE, the default, adds a dashed line at y=0} 128 | 129 | \item{addlegend}{If TRUE, adds a legend (by default does not add a legend if add=TRUE)} 130 | 131 | \item{legendbty}{Box type for the legend, passed to argument bty in \code{\link{legend}}.} 132 | 133 | \item{transitionpoint}{For plot.acifit, whether to plot a symbol at the transition point.} 134 | 135 | \item{linecols}{Vector of three colours for the lines (limiting rate, Ac, Aj), if one value 136 | provided it is used for all three.} 137 | 138 | \item{lwd}{Line widths, can be a vector of length 2 (first element for Ac and Aj, second one 139 | for the limiting rate).} 140 | 141 | \item{lty}{Line type (only for Amin - the limiting rate).} 142 | } 143 | \value{ 144 | A list of class 'acifit', with the following components: 145 | \describe{ 146 | \item{df}{A dataframe with the original data, including the measured photosynthetic 147 | rate (Ameas), the fitted photosynthetic rate (Amodel), Jmax and Vcmax-limited gross 148 | rates (Aj, Ac), TPU-limited rate (Ap), dark respiration (Rd), leaf temperature (Tleaf), 149 | chloroplastic CO2 (Cc), PPFD, atmospheric pressure (Patm), and 'original Ci, i.e. the 150 | Ci used as input (which is different from the Ci used in fitting if Patm was not set to 100kPa)} 151 | \item{pars}{Contains the parameter estimates and their approximate standard errors} 152 | \item{nlsfit}{The object returned by \code{\link{nls}}, and contains more detail on 153 | the quality of the fit} 154 | \item{Tcorrect}{whether the temperature correction was applied (logical)} 155 | \item{Photosyn}{A copy of the \code{\link{Photosyn}} function with the arguments adjusted for 156 | the current fit. That is, Vcmax, Jmax and Rd are set to those estimated in the fit, and Tleaf and 157 | PPFD are set to the mean value in the dataset. All other parameters that were set in fitaci are 158 | also used (e.g. temperature dependency parameters, TPU, etc.).} 159 | \item{Ci}{As Photosyn, except the opposite: calculate the Ci where some rate of net photosynthesis 160 | is achieved.} 161 | \item{Ci_transition}{The Ci at which photosynthesis transitions from Vcmax 162 | to Jmax limited photosynthesis.} 163 | \item{Ci_transition2}{The Ci at which photosynthesis transitions from Jmax to 164 | TPU limitation. Set to NA is either TPU was not estimated, or it could not be 165 | estimated from the data.} 166 | \item{Rd_measured}{Logical - was Rd provided as measured input?} 167 | \item{GammaStar}{The value for GammaStar, either calculated or provided to the fit.} 168 | \item{Km}{he value for Km, either calculated or provided to the fit.} 169 | \item{kminput}{Was Km provided as input? (If FALSE, it was calculated from Tleaf)} 170 | \item{gstarinput}{Was GammaStar provided as input? (If FALSE, it was calculated from Tleaf)} 171 | \item{fitmethod}{The fitmethod uses, either default or bilinear} 172 | \item{citransition}{The input citransition (NA if it was not provided as input)} 173 | \item{gmeso}{The mesophyll conductance used in the fit (NA if it was not set)} 174 | \item{fitTPU}{Was TPU fit?} 175 | \item{alphag}{The value of alphag used in estimating TPU.} 176 | \item{RMSE}{The Root-mean squared error, calculated as \code{sqrt(sum((Ameas-Amodel)^2))}.} 177 | \item{runorder}{The data returned in the 'df' slot are ordered by Ci, but in rare cases the 178 | original order of the data contains information; 'runorder' is 179 | the order in which the data were provided.} 180 | 181 | } 182 | } 183 | \description{ 184 | Fits the Farquhar-Berry-von Caemmerer model of photosynthesis to measurements of 185 | photosynthesis and intercellular \eqn{CO_2}{CO2} concentration (Ci). Estimates Jmax, Vcmax, Rd 186 | and their standard errors. A simple plotting method is also included, as well as the function 187 | \code{\link{fitacis}} which quickly fits multiple A-Ci curves (see its help page). Temperature 188 | dependencies of the parameters are taken into account following Medlyn et al. (2002), see 189 | \code{\link{Photosyn}} for more details. 190 | } 191 | \details{ 192 | \subsection{Fitting method}{The default method to fit A-Ci curves (set by 193 | \code{fitmethod="default"}) uses non-linear regression to fit the A-Ci curve. No assumptions 194 | are made on which part of the curve is Vcmax or Jmax limited. Normally, all three parameters 195 | are estimated: Jmax, Vcmax and Rd, unless Rd is provided as measured (when \code{useRd=TRUE}, 196 | and Rd is contained in the data). This is the method as described by Duursma (2015, Plos One). 197 | 198 | The 'bilinear' method to fit A-Ci curves (set by \code{fitmethod="bilinear"}) linearizes 199 | the Vcmax and Jmax-limited regions, and applies linear regression twice to estimate first 200 | Vcmax and Rd, and then Jmax (using Rd estimated from the Vcmax-limited region). The transition 201 | point is found as the one which gives the best overall fit to the data (i.e. all possible 202 | transitions are tried out, similar to Gu et al. 2010, PCE). The advantage of this method is that 203 | it \emph{always} returns parameter estimates, so it should be used in cases where the default 204 | method fails. Be aware, though, that the default method fails mostly when the curve contains 205 | bad data (so check your data before believing the fitted parameters). 206 | 207 | When \code{citransition} is set, it splits the data into a Vcmax-limited (where Ci < citransition), 208 | and Jmax-limited region (Ci > citransition). Both parameters are then estimated separately for 209 | each region (Rd is estimated only for the Vcmax-limited region). \bold{Note} that the actual 210 | transition point as shown in the standard plot of the fitted A-Ci curve may be quite different 211 | from that provided, since the fitting method simply decides which part of the dataset to use 212 | for which limitation, it does not constrain the actual estimated transition point directly. 213 | See the example below. If \code{fitmethod="default"}, it applies non-linear regression to 214 | both parts of the data, and when fitmethod="bilinear", it uses linear regression on the 215 | linearized photosynthesis rate. Results will differ between the two methods (slightly).} 216 | 217 | The 'onepoint' fitting method is a very simple estimation of Vcmax and Jmax for every point 218 | in the dataset, simply by inverting the photosynthesis equation. See De Kauwe et al. (2016) 219 | for details. The output will give the original data with Vcmax and Jmax added (note you can 220 | set \code{Tcorrect} as usual!). For increased reliability, this method only works if 221 | dark respiration (Rd) is included in the data (\code{useRd} is set automatically when 222 | setting \code{fitmethod='one-point'}). This method is not recommended for full A-Ci curves, 223 | but rather for spot gas exchange measurements, when a simple estimate of Vcmax or Jmax 224 | is needed, for example when separating stomatal and non-stomatal drought effects on 225 | photosynthesis (Zhou et al. 2013, AgForMet). The user will have to decide whether the Vcmax 226 | or Jmax rates are used in further analyses. This fitting method can not be used in \code{fitacis}, 227 | because Vcmax and Jmax are already estimated for each point in the dataset. 228 | 229 | \subsection{TPU limitation}{Optionally, the \code{fitaci} function estimates the triose-phosphate 230 | utilization (TPU) rate. The TPU can act as another limitation on photosynthesis, and can be 231 | recognized by a 'flattening out' of the A-Ci curve at high Ci. When \code{fitTPU=TRUE}, the 232 | fitting method used will always be 'bilinear'. The TPU is estimated by trying out whether the 233 | fit improves when the last n points of the curve are TPU-limited (where n=1,2,...). When TPU is 234 | estimated, it is possible (though rare) that no points are Jmax-limited (in which case estimated 235 | Jmax will be NA). A minimum of two points is always reserved for the estimate of Vcmax and Rd. 236 | An additional parameter (\code{alphag}) can be set that affects the behaviour at high Ci (see 237 | Ellsworth et al. 2015 for details, and also \code{\link{Photosyn}}). See examples.} 238 | 239 | \subsection{Temperature correction}{When \code{Tcorrect=TRUE} (the default), Jmax and Vcmax 240 | are re-scaled to 25C, using the temperature response parameters provided (but Rd is always 241 | at measurement temperature). When \code{Tcorrect=FALSE}, estimates of all parameters are at 242 | measurement temperature. If TPU is fit, it is never corrected for temperature. Important 243 | parameters to the fit are GammaStar and Km, both of which are calculated from leaf temperature 244 | using standard formulations. Alternatively, they can be provided as known inputs. \strong{Warning} : 245 | since package version 1.4, the default parameters have been adjusted. The new parameter values 246 | (EaV, EdVJ, delSJ, etc.) were based on a comprehensive literature review. See 247 | \code{vignette("new_T_responses")} or the article on remkoduursma.github.io/plantecophys.} 248 | 249 | \subsection{Mesophyll conductance}{It is possible to provide an estimate of the mesophyll 250 | conductance as input (\code{gmeso}), in which case the fitted Vcmax and Jmax are to be interpreted 251 | as chloroplastic rates. When using gmeso, it is recommended to use the 'default' fitting 252 | method (which will use the Ethier&Livingston equations inside \code{Photosyn}). It is also 253 | implemented with the 'bilinear' method but it requires more testing (and seems to give 254 | some strange results). When gmeso is set to a relatively low value, the resulting fit 255 | may be quite strange.} 256 | 257 | \subsection{Other parameters}{The A-Ci curve parameters depend on the values of a number 258 | of other parameters. For Jmax, PPFD is needed in order to express it as the asymptote. If 259 | PPFD is not provided in the dataset, it is assumed to equal 1800 mu mol m-2 s-1 (in which 260 | case a warning is printed). It is possible to either provide PPFD as a variable in the 261 | dataset (with the default name 'PARi', which can be changed), or as an argument to 262 | the \code{fitaci} directly.} 263 | 264 | \subsection{Plotting and summarizing}{The default \strong{plot} of the fit is constructed 265 | with \code{\link{plot.acifit}}, see Examples below. When plotting the fit, the A-Ci curve 266 | is simulated using the \code{\link{Aci}} function, with leaf temperature (Tleaf) and PPFD 267 | set to the mean value for the dataset. The \strong{coefficients} estimated in the fit 268 | (Vcmax, Jmax, and usually Rd) are extracted with \code{coef}. The summary of the fit is 269 | the same as the 'print' method, that is \code{myfit} will give the same output as 270 | \code{summary(myfit)} (where \code{myfit} is an object returned by \code{fitaci}). 271 | 272 | Because fitaci returns the fitted \code{\link{nls}} object, more details on statistics of 273 | the fit can be extracted with standard tools. The Examples below shows the use of the 274 | \pkg{nlstools} to extract many details of the fit at once. The fit also includes the 275 | \strong{root mean squared error} (RMSE), which can be extracted as \code{myfit$RMSE}. This 276 | is a useful metric to compare the different fitting methods.} 277 | 278 | \subsection{Predicting and the CO2 compensation point}{The fitted object contains two functions 279 | that reproduce the fitted curve exactly. Suppose your object is called 'myfit', then 280 | \code{myfit$Photosyn(200)} will give the fitted rate of photosynthesis at a Ci of 200. 281 | The inverse, calculating the Ci where some rate of photosynthesis is achieved, can be done with 282 | \code{myfit$Ci(10)} (find the Ci where net photosynthesis is ten). The (fitted!) CO2 283 | compensation point can then be calculated with : \code{myfit$Ci(0)}}. 284 | 285 | \subsection{Atmospheric pressure correction}{Note that atmospheric pressure (Patm) is taken 286 | into account, assuming the original data are in molar units (Ci in mu mol mol-1, or ppm). 287 | During the fit, Ci is converted to mu bar, and Km and Gammastar are recalculated accounting 288 | for the effects of Patm on the partial pressure of oxygen. When plotting the fit, though, 289 | molar units are shown on the X-axis. Thus, you should get (nearly) the same fitted curve when 290 | Patm was set to a value lower than 100kPa, but the fitted Vcmax and Jmax will be higher. 291 | This is because at low Patm, photosynthetic capacity has to be higher to achieve the same 292 | measured photosynthesis rate.} 293 | 294 | \subsection {From time to time, the \code{fitaci} function returns an error, indicating 295 | that the A-Ci curve could not be fit. In the majority of cases, this indicates a bad curve 296 | that probably could not (and should not) be fit in any case. Inspect the raw data to check 297 | if the curve does not include severe outliers, or large deviations from the expected, typical 298 | A-Ci curve. If the curve looks fine, refit using the option \code{fitmethod="bilinear"}, 299 | which will always return estimated parameters. Whatever you do, do not trust fitted curves 300 | without inspecting the raw data, and the fit of the model to the data.} 301 | } 302 | \examples{ 303 | \dontrun{ 304 | # Fit an A-Ci curve on a dataframe that contains Ci, Photo and optionally Tleaf and PPFD. 305 | # Here, we use the built-in example dataset 'acidata1'. 306 | f <- fitaci(acidata1) 307 | 308 | # Note that the default behaviour is to correct Vcmax and Jmax for temperature, 309 | # so the estimated values are at 25C. To turn this off: 310 | f2 <- fitaci(acidata1, Tcorrect=FALSE) 311 | 312 | # To use different T response parameters (see ?Photosyn), 313 | f3 <- fitaci(acidata1, Tcorrect=TRUE, EaV=25000) 314 | 315 | # Make a standard plot 316 | plot(f) 317 | 318 | # Look at a summary of the fit 319 | summary(f) 320 | 321 | # Extract coefficients only 322 | coef(f) 323 | 324 | # The object 'f' also contains the original data with predictions. 325 | # Here, Amodel are the modelled (fitted) values, Ameas are the measured values. 326 | with(f$df, plot(Amodel, Ameas)) 327 | abline(0,1) 328 | 329 | # The fitted values can also be extracted with the fitted() function: 330 | fitted(f) 331 | 332 | # The non-linear regression (nls) fit is stored as well, 333 | summary(f$nlsfit) 334 | 335 | # Many more details can be extracted with the nlstools package 336 | library(nlstools) 337 | overview(f$nlsfit) 338 | 339 | # The curve generator is stored as f$Photosyn: 340 | # Calculate photosynthesis at some value for Ci, using estimated 341 | # parameters and mean Tleaf, PPFD for the dataset. 342 | f$Photosyn(Ci=820) 343 | 344 | # Photosynthetic rate at the transition point: 345 | f$Photosyn(Ci=f$Ci_transition)$ALEAF 346 | 347 | # Set the transition point; this will fit Vcmax and Jmax separately. Note that the *actual* 348 | # transition is quite different from that provided, this is perfectly fine : 349 | # in this case Jmax is estimated from the latter 3 points only (Ci>800), but the actual 350 | # transition point is at ca. 400ppm. 351 | g <- fitaci(acidata1, citransition=800) 352 | plot(g) 353 | g$Ci_transition 354 | 355 | # Use measured Rd instead of estimating it from the A-Ci curve. 356 | # The Rd measurement must be added to the dataset used in fitting, 357 | # and you must set useRd=TRUE. 358 | acidata1$Rd <- 2 359 | f2 <- fitaci(acidata1, useRd=TRUE) 360 | f2 361 | 362 | # Fit TPU limitation 363 | ftpu <- fitaci(acidata1, fitTPU=TRUE, PPFD=1800, Tcorrect=TRUE) 364 | plot(ftpu) 365 | } 366 | } 367 | \references{ 368 | Duursma, R.A., 2015. Plantecophys - An R Package for Analysing and Modelling Leaf Gas 369 | Exchange Data. PLoS ONE 10, e0143346. doi:10.1371/journal.pone.0143346 370 | 371 | De Kauwe, M. G. et al. 2016. A test of the 'one-point method' for estimating maximum 372 | carboxylation capacity from field-measured, light-saturated photosynthesis. 373 | New Phytol 210, 1130-1144. 374 | } 375 | -------------------------------------------------------------------------------- /R/photosyn.R: -------------------------------------------------------------------------------- 1 | #' @title Coupled leaf gas exchange model 2 | #' @description A coupled photosynthesis - stomatal conductance model, based on the 3 | #' Farquhar model of photosynthesis, and a Ball-Berry type model of stomatal conductance. 4 | #' Includes options for temperature sensitivity of photosynthetic parameters, day respiration 5 | #' (optionally calculated from leaf temperature), and mesophyll conductance. 6 | #' @param VPD Vapour pressure deficit (kPa) (not needed when RH provided) 7 | #' @param Ca Atmospheric CO2 concentration (ppm) 8 | #' @param PPFD Photosynthetic photon flux density ('PAR') (mu mol m-2 s-1) 9 | #' @param Tleaf Leaf temperature (degrees C) 10 | #' @param Patm Atmospheric pressure (kPa) (but see warning below!) 11 | #' @param RH Relative humidity (in \%) (not needed when VPD provided) 12 | #' @param gsmodel One of BBOpti (Medlyn et al. 2011), BBLeuning (Leuning 1995), 13 | #' BallBerry (Ball et al. 1987), or BBdefine (for full control; see Details). 14 | #' @param g0,g1 Parameters of Ball-Berry type stomatal conductance models. 15 | #' @param gk Optional, exponent of VPD in gs model (Duursma et al. 2013) 16 | #' @param vpdmin Below vpdmin, VPD=vpdmin, to avoid very high gs. 17 | #' @param D0 Parameter for the BBLeuning stomatal conductance model. 18 | #' @param GS Optionally, stomatal conductance (to H2O). If provided, \code{Photosyn} 19 | #' calculates Ci and photosynthesis. See Details. 20 | #' @param BBmult Optional, only used when \code{gsmodel = "BBdefine"}, see Details. 21 | #' @param alpha Quantum yield of electron transport (mol mol-1) 22 | #' @param theta Shape of light response curve. 23 | #' @param Jmax Maximum rate of electron transport at 25 degrees C (mu mol m-2 s-1) 24 | #' @param Vcmax Maximum carboxylation rate at 25 degrees C (mu mol m-2 s-1) 25 | #' @param gmeso Mesophyll conductance (mol m-2 s-1). If not NULL (the default), Vcmax and 26 | #' Jmax are chloroplastic rates. 27 | #' @param TPU Triose-phosphate utilization rate (mu mol m-2 s-1); optional. 28 | #' @param alphag Fraction of glycolate not returned to the chloroplast; parameter in 29 | #' TPU-limited photosynthesis (optional, only to be used when TPU is provided) (0 - 1) 30 | #' @param Rd Day respiration rate (mu mol m-2 s-1), optional (if not provided, calculated 31 | #' from Tleaf, Rd0, Q10 and TrefR). Must be a positive value (an error occurs when a 32 | #' negative value is supplied). 33 | #' @param Rd0 Day respiration rate at reference temperature (\code{TrefR}). Must be a positive value. 34 | #' @param Q10 Temperature sensitivity of Rd. 35 | #' @param TrefR Reference temperature for Rd (Celcius). 36 | #' @param Rdayfrac Ratio of Rd in the light vs. in the dark. 37 | #' @param EaV,EdVC,delsC Vcmax temperature response parameters 38 | #' @param EaJ,EdVJ,delsJ Jmax temperature response parameters 39 | #' @param Km,GammaStar Optionally, provide Michaelis-Menten coefficient for Farquhar 40 | #' model, and Gammastar. If not provided, they are calculated with a built-in function 41 | #' of leaf temperature. 42 | #' @param Ci Optional, intercellular CO2 concentration (ppm). If not provided, 43 | #' calculated via gs model. 44 | #' @param Tcorrect If TRUE, corrects input Vcmax and Jmax for actual Tleaf (if FALSE, 45 | #' assumes the provided Vcmax and Jmax are at the Tleaf provided). \strong{Warning} : since package 46 | #' version 1.4, the default parameters have been adjusted (see Details). 47 | #' @param returnParsOnly If TRUE, returns calculated Vcmax,Jmax,Km and GammaStar based on 48 | #' leaf temperature. 49 | #' @param whichA Which assimilation rate does gs respond to? 50 | #' @param \dots Further arguments passed to \code{Photosyn} 51 | #' @seealso \code{\link{FARAO}}, \code{\link{fitaci}}, \code{\link{AciC4}} 52 | #' @details The coupled photosynthesis - stomatal conductance model finds the intersection between 53 | #' the supply of CO2 by diffusion, and the demand for CO2 by photosynthesis. See Farquhar and 54 | #' Sharkey (1982) for basic description of this type of model, Duursma (2015) for more details 55 | #' on the implementation in the \code{plantecophys} package, and Duursma et al. (2014) for an 56 | #' example application (that uses this implementation). 57 | #' 58 | #' \strong{Photosynthesis model and temperature response} - 59 | #' The model of Farquhar et al. (1980) is used to estimate the dependence of leaf net 60 | #' photosynthesis rate (ALEAF) on intercellular CO2 concentration (Ci), accounting for all 61 | #' three limitations (electron transport, carboxylation, and TPU limitation). The equations 62 | #' for the temperature response of photosynthetic parameters, including Vcmax, Jmax, 63 | #' Gammastar, and Km follow Medlyn et al. (2002). However, \strong{note that the default 64 | #' temperature response parameter values are not taken from Medlyn, and likely will have to 65 | #' be adjusted for your situation}. \strong{Warning} : since package version 1.4, the default 66 | #' parameters have been adjusted. The new parameter values (EaV, EdVJ, delSJ, etc.) were based 67 | #' on a comprehensive literature review. See vignette("new_T_responses") or the article on 68 | #' remkoduursma.github.io/plantecophys. 69 | #' 70 | #' #' By default, the \code{Photosyn} function returns the hyperbolic minimum of Vcmax and 71 | #' Jmax-limited photosynthetic rates, as well as the hyperbolic minimum of Jmax-limited and 72 | #' TPU-limited rates. This approach avoids the discontinuity at the transition between the 73 | #' two rates (thus allowing use of \code{Photosyn} and \code{fitaci} in optimization or 74 | #' fitting routines). The individual rates (Ac, Aj and Ap) are also returned as output 75 | #' should they be needed. Note that those rates are output as gross photosynthetic rates 76 | #' (leaf respiration has to be subtracted to give net leaf photosynthesis). 77 | #' 78 | #' \strong{Coupled leaf gas exchange} 79 | #' When Ci is not provided, Ci is calculated from the intersection between the 'supply' and 80 | #' 'demand', where 'demand' is given by the Farquhar model of photosynthesis (A=f(Ci)), and 81 | #' supply by the stomatal conductance. The latter is, by default, estimated using the 82 | #' stomatal conductance model of Medlyn et al. (2011), but two other models are provided 83 | #' as well (Ball-Berry and Leuning, see \code{gsmodel} argument). Otherwise, stomatal 84 | #' conductance may be directly provided via the \code{GS} argument. 85 | #' 86 | #' \strong{Stomatal conductance models} - 87 | #' At the moment, three stomatal conductance models are implemented. The 'BBOpti' model 88 | #' is a slightly more general form of the model of Medlyn et al. 2011 (see Duursma et al. 89 | #' 2013). It is given by (in notation of the parameters and output 90 | #' variables of \code{Photosyn}), 91 | #' 92 | #' \deqn{GS = g0 + 1.6*(1 + g1/D^(1-gk))*ALEAF/CA} 93 | #' 94 | #' where \code{gk = 0.5} if stomata behave optimally (cf. Medlyn et al. 2011). 95 | #' 96 | #' The 'BBLeuning' model is that of Leuning (1995). It is given by, 97 | #' 98 | #' \deqn{GS = g0 + g1*ALEAF/(Ca * (1 + VPD/D0))} 99 | #' 100 | #' Note that this model also uses the g1 parameter, but it needs to be set to 101 | #' a much higher value to be comparable in magnitude to the BBOpti model. 102 | #' 103 | #' The 'BallBerry' model is that of Ball et al. (1987). It is given by, 104 | #' 105 | #' \deqn{GS = g0 + g1*RH*ALEAF/Ca} 106 | #' 107 | #' Where RH is relative humidity. Again, the g1 value is not comparable to that 108 | #' used in the previous two models. 109 | #' 110 | #' Finally, \code{Photosyn} provides a very flexible Ball-Berry model, where 111 | #' the multiplier has to be specified by the user, the model is: 112 | #' 113 | #' \deqn{GS = g0 + BBmult*ALEAF} 114 | #' 115 | #' This interface can be used to quickly simulate what happens if stomata do 116 | #' not respond to humidity at all (in which case BBmult=g1/Ca, or ca. 5/400), or 117 | #' to use the Tuzet model of stomatal conductance inside another model that provides 118 | #' the leaf water potential function. 119 | #' 120 | #' For the full numerical solution to the Cowan-Farquhar optimization, use 121 | #' the \code{\link{FARAO}} function (which was used in Medlyn et al. 2011 for 122 | #' comparison to the approximation there presented). See Duursma (2015) for more details. 123 | #' 124 | #' \strong{Mesophyll conductance} - 125 | #' 126 | #' If the mesophyll conductance \code{gmeso} is provided as an input, it is assumed 127 | #' that Vcmax and Jmax are the chloroplastic rates, and leaf photosynthesis is 128 | #' calculated following the equations from Ethier and Livingston (2004). When very 129 | #' low mesophyll conductance rates are input, the model may return poor solutions 130 | #' (and sometimes they may not exist). 131 | #' 132 | #' \strong{Simulating A-Ci curves} 133 | #' 134 | #' If Ci is provided as an input, this function calculates an A-Ci curve. For 135 | #' example, you may do \code{Photosyn(Ci=300)}, for which the function \code{Aci} 136 | #' is included as a shortcut (\code{Aci(300)}). 137 | #' 138 | #' \strong{Atmospheric pressure} - 139 | #' 140 | #' A correction for atmospheric pressure (Patm) is implemented in \code{\link{fitaci}}, 141 | #' but \strong{not in Photosyn}. In \code{fitaci}, the necessary corrections are applied 142 | #' so that estimated Vcmax and Jmax are expressed at standard pressure (Patm=100kPa). 143 | #' In Photosyn, however, the corrections are much more complicated and tend to be very 144 | #' small, because effects of Patm on partial pressures are largely offset by increases 145 | #' in diffusivity (Terashima et al. 1995, Gale 1973). 146 | #' 147 | #' Note that Patm is an argument to the Photosyn function, but it only affects 148 | #' calculations of Km and GammaStar (as used by fitaci), and transpiration rate. 149 | #' Setting only Patm \strong{does not correct for atmospheric pressure effects on 150 | #' photosynthesis rates}. 151 | #' 152 | #' The simulation of limitation of the photosynthetic rate to triose-phosphate 153 | #' utilization follows details in Ellsworth et al. (2015), their Eq. 7. Note that 154 | #' the parameter \code{alphag} is set to zero by default. 155 | #' 156 | #' 157 | #' 158 | #' @references 159 | #' Duursma, R.A., Payton, P., Bange, M.P., Broughton, K.J., Smith, R.A., Medlyn, B.E., Tissue, 160 | #' D. T., 2013, Near-optimal response of instantaneous transpiration efficiency to vapour 161 | #' pressure deficit, temperature and [CO2] in cotton (Gossypium hirsutum L.). Agricultural 162 | #' and Forest Meteorology 168 : 168 - 176. 163 | #' 164 | #' Duursma, R.A., Barton, C.V.M., Lin, Y.-S., Medlyn, B.E., Eamus, D., Tissue, D.T., Ellsworth, 165 | #' D.S., McMurtrie, R.E., 2014. The peaked response of transpiration rate to vapour pressure 166 | #' deficit in field conditions can be explained by the temperature optimum of photosynthesis. 167 | #' Agricultural and Forest Meteorology 189 - 190, 2-10. doi:10.1016/j.agrformet.2013.12.007 168 | #' 169 | #' Duursma, R.A., 2015. Plantecophys - An R Package for Analysing and Modelling Leaf Gas 170 | #' Exchange Data. PLoS ONE 10, e0143346. doi:10.1371/journal.pone.0143346 171 | #' 172 | #' Ellsworth, D.S., Crous, K.Y., Lambers, H., Cooke, J., 2015. Phosphorus recycling in 173 | #' photorespiration maintains high photosynthetic capacity in woody species. Plant Cell 174 | #' Environ 38, 1142-1156. doi:10.1111/pce.12468 175 | #' 176 | #'Ethier, G. and N. Livingston. 2004. On the need to incorporate sensitivity to CO2 177 | #'transfer conductance into the Farquhar von Caemmerer Berry leaf photosynthesis model. 178 | #'Plant, Cell & Environment. 27:137-153. 179 | #' 180 | #' Farquhar, G.D., S. Caemmerer and J.A. Berry. 1980. A biochemical model of photosynthetic 181 | #' CO2 assimilation in leaves of C3 species. Planta. 149:78-90. 182 | #' 183 | #' Farquhar, G. D., & Sharkey, T. D. (1982). Stomatal conductance and photosynthesis. 184 | #' Annual review of plant physiology, 33(1), 317-345. 185 | #' 186 | #' Gale, J., 1972. Availability of Carbon Dioxide for Photosynthesis at High Altitudes: 187 | #' Theoretical Considerations. Ecology 53, 494-497. doi:10.2307/1934239 188 | #' 189 | #' Leuning, R. 1995. A critical-appraisal of a combined stomatal-photosynthesis model for 190 | #' C-3 plants. Plant Cell and Environment. 18:339-355. 191 | #' 192 | #' Medlyn, B.E., E. Dreyer, D. Ellsworth, M. Forstreuter, P.C. Harley, M.U.F. Kirschbaum, 193 | #' X. Le Roux, P. Montpied, J. Strassemeyer, A. Walcroft, K. Wang and D. Loustau. 2002. 194 | #' Temperature response of parameters of a biochemically based model of photosynthesis. 195 | #' II. A review of experimental data. Plant Cell and Environment. 25:1167-1179. 196 | #' 197 | #' Medlyn, B.E., R.A. Duursma, D. Eamus, D.S. Ellsworth, I.C. Prentice, C.V.M. Barton, 198 | #' K.Y. Crous, P. De Angelis, M. Freeman and L. Wingate. 2011. Reconciling the optimal 199 | #' and empirical approaches to modelling stomatal conductance. Global Change Biology. 200 | #' 17:2134-2144. 201 | #' 202 | #' Terashima, I., Masuzawa, T., Ohba, H., Yokoi, Y., 1995. Is photosynthesis suppressed at 203 | #' higher elevations due to low CO2 pressure? Ecology 76, 2663-2668. doi:10.2307/2265838 204 | #' 205 | #' @aliases Photosyn Aci 206 | #' @return Returns a dataframe. 207 | #' @examples 208 | #' # Run the coupled leaf gas exchange model, set only a couple of parameters 209 | #' Photosyn(VPD=2, g1=4, Ca=500) 210 | #' 211 | #' # It is easy to set multiple values for inputs (and these can be mixed with single inputs); 212 | #' r <- Photosyn(VPD=seq(0.5, 4, length=25), Vcmax=50, Jmax=100) 213 | #' with(r, plot(VPD, ALEAF, type='l')) 214 | #' 215 | #' # Set the mesophyll conductance 216 | #' run1 <- Photosyn(PPFD=seq(50,1000,length=25), gmeso=0.15, Vcmax=40, Jmax=85) 217 | #' with(run1, plot(PPFD, GS, type='l')) 218 | #' 219 | #' # Run A-Ci curve only (provide Ci instead of calculating it). 220 | #' arun1 <- Aci(Ci=seq(50, 1200, length=101), Vcmax=40, Jmax=85) 221 | #' arun2 <- Aci(Ci=seq(50, 1200, length=101), Vcmax=30, Jmax=70) 222 | #' with(arun1, plot(Ci, ALEAF, type='l')) 223 | #' with(arun2, points(Ci, ALEAF, type='l', lty=5)) 224 | #' 225 | #' # Find the intersection between supply of CO2 and demand for CO2 (cf. Farquhar and Sharkey 1982). 226 | #' 227 | #' # Set some parameters 228 | #' gs <- 0.2 # stomatal conductance to H2O 229 | #' Ca <- 400 # ambient CO2 230 | #' gctogw <- 1.57 # conversion 231 | #' gc <- gs / gctogw # stomatal conductance to CO2 232 | #' 233 | #' # Demand curve (Farquhar model) 234 | #' p <- Aci(seq(60,500,length=101), Ca=400) 235 | #' 236 | #' # Provide stomatal conductance as input, gives intersection point. 237 | #' g <- Photosyn(GS=gs, Ca=Ca) 238 | #' 239 | #' # Intersection point visualized 240 | #' par(yaxs="i") 241 | #' with(p, plot(Ci, ALEAF, type='l', ylim=c(0,max(ALEAF)))) 242 | #' with(g, points(Ci, ALEAF, pch=19, col="red")) 243 | #' abline(gc * Ca, -gc, lty=5) 244 | #' 245 | #' legend("topleft", c(expression("Demand:"~~A==f(C[i])), 246 | #' expression("Supply:"~~A==g[c]*(C[a]-C[i])), 247 | #' "Operating point"), 248 | #' lty=c(1,5,-1),pch=c(-1,-1,19), 249 | #' col=c("black","black","red"), 250 | #' bty='n', cex=0.9) 251 | #' @export 252 | #' @rdname Photosyn 253 | Photosyn <- function(VPD=1.5, 254 | Ca=400, 255 | PPFD=1500, 256 | Tleaf=25, 257 | Patm=100, 258 | RH=NULL, 259 | 260 | gsmodel=c("BBOpti","BBLeuning","BallBerry","BBdefine"), 261 | g1=4, 262 | g0=0, 263 | gk=0.5, 264 | vpdmin=0.5, 265 | D0=5, 266 | GS=NULL, 267 | BBmult=NULL, 268 | 269 | alpha=0.24, 270 | theta=0.85, 271 | Jmax=100, 272 | Vcmax=50, 273 | gmeso=NULL, 274 | TPU=1000, 275 | alphag=0, 276 | 277 | Rd0 = 0.92, 278 | Q10 = 1.92, 279 | Rd=NULL, 280 | TrefR = 25, 281 | Rdayfrac = 1.0, 282 | 283 | EaV = 58550, 284 | EdVC = 200000, 285 | delsC = 629.26, 286 | 287 | EaJ = 29680, 288 | EdVJ = 200000, 289 | delsJ = 631.88, 290 | 291 | GammaStar = NULL, 292 | Km = NULL, 293 | 294 | Ci = NULL, 295 | Tcorrect=TRUE, 296 | returnParsOnly=FALSE, 297 | whichA=c("Ah","Amin","Ac","Aj")){ 298 | 299 | 300 | whichA <- match.arg(whichA) 301 | gsmodel <- match.arg(gsmodel) 302 | if(gsmodel == "BBdefine" && is.null(BBmult)){ 303 | Stop("When defining your own BB multiplier, set BBmult.") 304 | } 305 | inputCi <- !is.null(Ci) 306 | inputGS <- !is.null(GS) 307 | 308 | if(inputCi & inputGS)Stop("Cannot provide both Ci and GS.") 309 | 310 | if(is.null(TPU))TPU <- 1000 311 | 312 | if(is.null(VPD) && !is.null(RH)){ 313 | VPD <- RHtoVPD(RH, Tleaf) 314 | } 315 | if(is.null(VPD) && is.null(RH)){ 316 | Stop("Need one of VPD, RH.") 317 | } 318 | 319 | #---- Constants; hard-wired parameters. 320 | Rgas <- .Rgas() 321 | GCtoGW <- 1.57 # conversion from conductance to CO2 to H2O 322 | 323 | 324 | #---- Do all calculations that can be vectorized 325 | 326 | # g1 and g0 are input ALWAYS IN UNITS OF H20 327 | # G0 must be converted to CO2 (but not G1, see below) 328 | g0 <- g0/GCtoGW 329 | 330 | # Leaf respiration 331 | if(is.null(Rd)){ 332 | Rd <- Rdayfrac*Rd0*Q10^((Tleaf-TrefR)/10) 333 | } 334 | 335 | # CO2 compensation point in absence of photorespiration 336 | if(is.null(GammaStar))GammaStar <- TGammaStar(Tleaf,Patm) 337 | 338 | # Michaelis-Menten coefficient 339 | if(is.null(Km))Km <- TKm(Tleaf,Patm) 340 | 341 | #-- Vcmax, Jmax T responses 342 | if(Tcorrect){ 343 | Vcmax <- Vcmax * TVcmax(Tleaf,EaV, delsC, EdVC) 344 | Jmax <- Jmax * TJmax(Tleaf, EaJ, delsJ, EdVJ) 345 | } 346 | 347 | # Electron transport rate 348 | J <- Jfun(PPFD, alpha, Jmax, theta) 349 | VJ <- J/4 350 | 351 | #--- Stop here if only the parameters are required 352 | if(returnParsOnly){ 353 | return(list(Vcmax=Vcmax, Jmax=Jmax, Km=Km, GammaStar=GammaStar, VJ=VJ)) 354 | } 355 | 356 | # Medlyn et al. 2011 model gs/A. NOTE: 1.6 not here because we need GCO2! 357 | if(gsmodel == "BBOpti"){ 358 | vpduse <- VPD 359 | vpduse[vpduse < vpdmin] <- vpdmin 360 | GSDIVA <- (1 + g1/(vpduse^(1-gk)))/Ca 361 | } 362 | 363 | # Leuning 1995 model, without gamma (CO2 compensation point) 364 | if(gsmodel == "BBLeuning"){ 365 | GSDIVA <- g1 / Ca / (1 + VPD/D0) 366 | GSDIVA <- GSDIVA / GCtoGW # convert to conductance to CO2 367 | } 368 | 369 | # Original Ball&Berry 1987 model. 370 | if(gsmodel == "BallBerry"){ 371 | if(is.null(RH))RH <- VPDtoRH(VPD, Tleaf) 372 | RH <- RH / 100 373 | GSDIVA <- g1 * RH / Ca 374 | GSDIVA <- GSDIVA / GCtoGW # convert to conductance to CO2 375 | } 376 | 377 | # Multiplier is user-defined. 378 | if(gsmodel == "BBdefine"){ 379 | GSDIVA <- BBmult / GCtoGW 380 | } 381 | 382 | if(inputGS){ 383 | 384 | GC <- GS / GCtoGW 385 | 386 | if(GS > 0){ 387 | 388 | # Solution when Rubisco activity is limiting 389 | A <- 1./GC 390 | B <- (Rd - Vcmax)/GC - Ca - Km 391 | C <- Vcmax * (Ca - GammaStar) - Rd * (Ca + Km) 392 | Ac <- QUADM(A,B,C) 393 | 394 | # Photosynthesis when electron transport is limiting 395 | B <- (Rd - VJ)/GC - Ca - 2*GammaStar 396 | C <- VJ * (Ca - GammaStar) - Rd * (Ca + 2*GammaStar) 397 | Aj <- QUADM(A,B,C) 398 | 399 | # NOTE: the solution above gives net photosynthesis, add Rd 400 | # to get gross rates (to be consistent with other solutions). 401 | Ac <- Ac + Rd 402 | Aj <- Aj + Rd 403 | } else { 404 | Ac <- Aj <- 0 405 | } 406 | 407 | } else { 408 | 409 | # If CI not provided, calculate from intersection between supply and demand 410 | if(!inputCi){ 411 | 412 | #--- non-vectorized workhorse 413 | getCI <- function(VJ,GSDIVA,PPFD,VPD,Ca,Tleaf,vpdmin,g0,Rd, 414 | Vcmax,Jmax,Km,GammaStar){ 415 | 416 | if(identical(PPFD, 0) | identical(VJ, 0)){ 417 | vec <- c(Ca,Ca) 418 | return(vec) 419 | } 420 | 421 | # Taken from MAESTRA. 422 | # Following calculations are used for both BB & BBL models. 423 | # Solution when Rubisco activity is limiting 424 | A <- g0 + GSDIVA * (Vcmax - Rd) 425 | B <- (1. - Ca*GSDIVA) * (Vcmax - Rd) + g0 * 426 | (Km - Ca)- GSDIVA * (Vcmax*GammaStar + Km*Rd) 427 | C <- -(1. - Ca*GSDIVA) * (Vcmax*GammaStar + Km*Rd) - g0*Km*Ca 428 | 429 | CIC <- QUADP(A,B,C) 430 | 431 | # Solution when electron transport rate is limiting 432 | A <- g0 + GSDIVA * (VJ - Rd) 433 | B <- (1 - Ca*GSDIVA) * (VJ - Rd) + g0 * (2.*GammaStar - Ca)- 434 | GSDIVA * (VJ*GammaStar + 2.*GammaStar*Rd) 435 | C <- -(1 - Ca*GSDIVA) * GammaStar * (VJ + 2*Rd) - 436 | g0*2*GammaStar*Ca 437 | 438 | CIJ <- QUADP(A,B,C) 439 | return(c(CIJ,CIC)) 440 | } 441 | 442 | # get Ci 443 | x <- mapply(getCI, 444 | VJ=VJ, 445 | GSDIVA = GSDIVA, 446 | PPFD=PPFD, 447 | VPD=VPD, 448 | Ca=Ca, 449 | Tleaf=Tleaf, 450 | vpdmin=vpdmin, 451 | g0=g0, 452 | Rd=Rd, 453 | Vcmax=Vcmax, 454 | Jmax=Jmax, 455 | Km=Km, 456 | GammaStar=GammaStar) 457 | 458 | CIJ <- x[1,] 459 | CIC <- x[2,] 460 | } else { 461 | 462 | # Rare case where one Ci is provided, and multiple Tleaf (Jena bug). 463 | if(length(Ci) == 1){ 464 | Ci <- rep(Ci, length(Km)) 465 | } 466 | 467 | # Ci provided (A-Ci function mode) 468 | CIJ <- Ci 469 | 470 | if(length(GammaStar) > 1){ 471 | CIJ[CIJ <= GammaStar] <- GammaStar[CIJ < GammaStar] 472 | } else { 473 | CIJ[CIJ <= GammaStar] <- GammaStar 474 | } 475 | 476 | CIC <- Ci 477 | 478 | } 479 | 480 | # Photosynthetic rates, without or with mesophyll limitation 481 | if(is.null(gmeso) || gmeso < 0){ 482 | # Get photosynthetic rate 483 | Ac <- Vcmax*(CIC - GammaStar)/(CIC + Km) 484 | Aj <- VJ * (CIJ - GammaStar)/(CIJ + 2*GammaStar) 485 | 486 | } else { 487 | # Ethier and Livingston (2004) (Equation 10). 488 | A <- -1/gmeso 489 | BC <- (Vcmax - Rd)/gmeso + CIC + Km 490 | CC <- Rd*(CIC+Km)-Vcmax*(CIC-GammaStar) 491 | Ac <- mapply(QUADP, A=A,B=BC,C=CC) 492 | 493 | BJ <- (VJ - Rd)/gmeso + CIC + 2.0*GammaStar 494 | CJ <- Rd*(CIC+2.0*GammaStar) - VJ*(CIC - GammaStar) 495 | Aj <- mapply(QUADP, A=A,B=BJ,C=CJ) 496 | 497 | Ac <- Ac + Rd 498 | Aj <- Aj + Rd 499 | 500 | } 501 | 502 | 503 | # When below light-compensation points, assume Ci=Ca. 504 | if(!inputCi){ 505 | lesslcp <- vector("logical", length(Aj)) 506 | lesslcp <- Aj <= Rd + 1E-09 507 | 508 | if(length(Ca) == 1)Ca <- rep(Ca, length(CIJ)) 509 | if(length(GammaStar) == 1)GammaStar <- rep(GammaStar, length(CIJ)) 510 | if(length(VJ) == 1)VJ <- rep(VJ, length(CIJ)) 511 | 512 | CIJ[lesslcp] <- Ca[lesslcp] 513 | Aj[lesslcp] <- VJ[lesslcp] * (CIJ[lesslcp] - GammaStar[lesslcp]) / 514 | (CIJ[lesslcp] + 2*GammaStar[lesslcp]) 515 | 516 | Ci <- ifelse(Aj < Ac, CIJ, CIC) 517 | 518 | } 519 | } 520 | 521 | # Limitation by triose-phosphate utilization 522 | if(!is.null(Ci)){ 523 | Ap <- 3 * TPU * (Ci - GammaStar)/(Ci - (1 + 3*alphag)*GammaStar) 524 | Ap[Ci < 400] <- 1000 # avoid nonsense 525 | } else { 526 | Ap <- 1000 # This is when inputGS = TRUE; 527 | } 528 | 529 | 530 | # Hyperbolic minimum. 531 | Am <- -mapply(QUADP, A = 1 - 1E-04, B = Ac+Aj, C = Ac*Aj) 532 | 533 | # Another hyperbolic minimum with the transition to TPU 534 | tpulim <- any(Ap < Am) 535 | if(!is.na(tpulim) && tpulim){ 536 | Am <- -mapply(QUADP, A = 1 - 1E-07, B = Am+Ap, C = Am*Ap) 537 | } 538 | 539 | # Net photosynthesis 540 | Am <- Am - Rd 541 | 542 | # Calculate conductance to CO2 543 | if(!inputCi && !inputGS){ 544 | if(whichA == "Ah")GS <- g0 + GSDIVA*Am 545 | if(whichA == "Aj")GS <- g0 + GSDIVA*(Aj-Rd) 546 | if(whichA == "Ac")GS <- g0 + GSDIVA*(Ac-Rd) 547 | } 548 | if(inputCi) { 549 | if(whichA == "Ah")GS <- Am/(Ca - Ci) 550 | if(whichA == "Aj")GS <- (Aj-Rd)/(Ca - Ci) 551 | if(whichA == "Ac")GS <- (Ac-Rd)/(Ca - Ci) 552 | } 553 | 554 | # Extra step here; GS can be negative 555 | GS[GS < g0] <- g0 556 | 557 | # Output conductance to H2O 558 | if(!inputGS){ 559 | GS <- GS*GCtoGW 560 | } 561 | 562 | # Calculate Ci if GS was provided as input. 563 | if(inputGS){ 564 | Ci <- Ca - Am/GC 565 | 566 | # For zero GC: 567 | Ci[!is.finite(Ci)] <- Ca 568 | # Stomata fully shut; Ci is not really Ca but that's 569 | # how we like to think about it. 570 | } 571 | 572 | # Chloroplastic CO2 concentration 573 | if(!is.null(gmeso)){ 574 | Cc <- Ci - Am/gmeso 575 | } else { 576 | Cc <- Ci 577 | } 578 | 579 | # Transpiration rate assuming perfect coupling. 580 | # Output units are mmol m-2 s-1 581 | E <- 1000*GS*VPD/Patm 582 | 583 | df <- data.frame( Ci=Ci, 584 | ALEAF=Am, 585 | GS=GS, 586 | ELEAF=E, 587 | Ac=Ac, 588 | Aj=Aj, 589 | Ap=Ap, 590 | Rd=Rd, 591 | VPD=VPD, 592 | Tleaf=Tleaf, 593 | Ca=Ca, 594 | Cc=Cc, 595 | PPFD=PPFD, 596 | Patm=Patm) 597 | 598 | return(df) 599 | } 600 | 601 | 602 | #'@export 603 | #'@rdname Photosyn 604 | Aci <- function(Ci,...)Photosyn(Ci=Ci,...) 605 | 606 | 607 | 608 | # Non-rectangular hyperbola 609 | Jfun <- function(PPFD, alpha, Jmax, theta){ 610 | (alpha*PPFD + Jmax - 611 | sqrt((alpha*PPFD + Jmax)^2 - 4*alpha*theta*PPFD*Jmax))/(2*theta) 612 | } 613 | 614 | # Given PPFD and J, what is Jmax? (inverse non-rectangular hyperbola) 615 | inverseJfun <- function(PPFD, alpha, J, theta){ 616 | J*(J*theta - alpha*PPFD)/(J - alpha*PPFD) 617 | } 618 | 619 | 620 | 621 | -------------------------------------------------------------------------------- /R/fitaci.R: -------------------------------------------------------------------------------- 1 | #' Fit the Farquhar-Berry-von Caemmerer model of leaf photosynthesis 2 | #' @description Fits the Farquhar-Berry-von Caemmerer model of photosynthesis to measurements of 3 | #' photosynthesis and intercellular \eqn{CO_2}{CO2} concentration (Ci). Estimates Jmax, Vcmax, Rd 4 | #' and their standard errors. A simple plotting method is also included, as well as the function 5 | #' \code{\link{fitacis}} which quickly fits multiple A-Ci curves (see its help page). Temperature 6 | #' dependencies of the parameters are taken into account following Medlyn et al. (2002), see 7 | #' \code{\link{Photosyn}} for more details. 8 | #' @param data Dataframe with Ci, Photo, Tleaf, PPFD (the last two are optional). For \code{fitacis}, 9 | #' also requires a grouping variable. 10 | #' @param varnames List of names of variables in the dataset (see Details). 11 | #' @param Tcorrect If TRUE, Vcmax and Jmax are corrected to 25C. Otherwise, Vcmax and Jmax 12 | #' are estimated at measurement temperature. \strong{Warning} : since package version 1.4, the default parameters have been adjusted (see Details). 13 | #' @param Patm Atmospheric pressure (kPa) 14 | #' @param citransition If provided, fits the Vcmax and Jmax limited regions separately (see Details). 15 | #' @param quiet If TRUE, no messages are written to the screen. 16 | #' @param startValgrid If TRUE (the default), uses a fine grid of starting values to increase 17 | #' the chance of finding a solution. 18 | #' @param fitmethod Method to fit the A-Ci curve. Either 'default' (Duursma 2015), 'bilinear' (See Details), or 'onepoint' (De Kauwe et al. 2016). 19 | #' @param algorithm Passed to \code{\link{nls}}, sets the algorithm for finding parameter values. 20 | #' @param fitTPU Logical (default FALSE). Attempt to fit TPU limitation (fitmethod set to 'bilinear' 21 | #' automatically if used). See Details. 22 | #' @param alphag When estimating TPU limitation (with \code{fitTPU}), an 23 | #' additional parameter (see Details). 24 | #' @param useRd If Rd provided in data, and useRd=TRUE (default is FALSE), uses measured Rd in 25 | #' fit. Otherwise it is estimated from the fit to the A-Ci curve. 26 | #' @param PPFD Photosynthetic photon flux density ('PAR') (mu mol m-2 s-1) 27 | #' @param Tleaf Leaf temperature (degrees C) 28 | #' @param alpha Quantum yield of electron transport (mol mol-1) 29 | #' @param theta Shape of light response curve. 30 | #' @param gmeso Mesophyll conductance (mol m-2 s-1 bar-1). If not NULL (the default), Vcmax 31 | #' and Jmax are chloroplastic rates. 32 | #' @param EaV,EdVC,delsC Vcmax temperature response parameters 33 | #' @param EaJ,EdVJ,delsJ Jmax temperature response parameters 34 | #' @param Km,GammaStar Optionally, provide Michaelis-Menten coefficient for Farquhar model, and 35 | #' Gammastar. If not provided, they are calculated with a built-in function of leaf temperature. 36 | #' @param id Names of variables (quoted, can be a vector) in the original dataset to be stored 37 | #' in the result. Most useful when using \code{\link{fitacis}}, see there for examples of its use. 38 | #' @param \dots Further arguments (ignored at the moment). 39 | #' 40 | #' 41 | #' @details 42 | #' 43 | #' \subsection{Fitting method}{The default method to fit A-Ci curves (set by 44 | #' \code{fitmethod="default"}) uses non-linear regression to fit the A-Ci curve. No assumptions 45 | #' are made on which part of the curve is Vcmax or Jmax limited. Normally, all three parameters 46 | #' are estimated: Jmax, Vcmax and Rd, unless Rd is provided as measured (when \code{useRd=TRUE}, 47 | #' and Rd is contained in the data). This is the method as described by Duursma (2015, Plos One). 48 | #' 49 | #' The 'bilinear' method to fit A-Ci curves (set by \code{fitmethod="bilinear"}) linearizes 50 | #' the Vcmax and Jmax-limited regions, and applies linear regression twice to estimate first 51 | #' Vcmax and Rd, and then Jmax (using Rd estimated from the Vcmax-limited region). The transition 52 | #' point is found as the one which gives the best overall fit to the data (i.e. all possible 53 | #' transitions are tried out, similar to Gu et al. 2010, PCE). The advantage of this method is that 54 | #' it \emph{always} returns parameter estimates, so it should be used in cases where the default 55 | #' method fails. Be aware, though, that the default method fails mostly when the curve contains 56 | #' bad data (so check your data before believing the fitted parameters). 57 | #' 58 | #' When \code{citransition} is set, it splits the data into a Vcmax-limited (where Ci < citransition), 59 | #' and Jmax-limited region (Ci > citransition). Both parameters are then estimated separately for 60 | #' each region (Rd is estimated only for the Vcmax-limited region). \bold{Note} that the actual 61 | #' transition point as shown in the standard plot of the fitted A-Ci curve may be quite different 62 | #' from that provided, since the fitting method simply decides which part of the dataset to use 63 | #' for which limitation, it does not constrain the actual estimated transition point directly. 64 | #' See the example below. If \code{fitmethod="default"}, it applies non-linear regression to 65 | #' both parts of the data, and when fitmethod="bilinear", it uses linear regression on the 66 | #' linearized photosynthesis rate. Results will differ between the two methods (slightly).} 67 | #' 68 | #' The 'onepoint' fitting method is a very simple estimation of Vcmax and Jmax for every point 69 | #' in the dataset, simply by inverting the photosynthesis equation. See De Kauwe et al. (2016) 70 | #' for details. The output will give the original data with Vcmax and Jmax added (note you can 71 | #' set \code{Tcorrect} as usual!). For increased reliability, this method only works if 72 | #' dark respiration (Rd) is included in the data (\code{useRd} is set automatically when 73 | #' setting \code{fitmethod='one-point'}). This method is not recommended for full A-Ci curves, 74 | #' but rather for spot gas exchange measurements, when a simple estimate of Vcmax or Jmax 75 | #' is needed, for example when separating stomatal and non-stomatal drought effects on 76 | #' photosynthesis (Zhou et al. 2013, AgForMet). The user will have to decide whether the Vcmax 77 | #' or Jmax rates are used in further analyses. This fitting method can not be used in \code{fitacis}, 78 | #' because Vcmax and Jmax are already estimated for each point in the dataset. 79 | #' 80 | #' \subsection{TPU limitation}{Optionally, the \code{fitaci} function estimates the triose-phosphate 81 | #' utilization (TPU) rate. The TPU can act as another limitation on photosynthesis, and can be 82 | #' recognized by a 'flattening out' of the A-Ci curve at high Ci. When \code{fitTPU=TRUE}, the 83 | #' fitting method used will always be 'bilinear'. The TPU is estimated by trying out whether the 84 | #' fit improves when the last n points of the curve are TPU-limited (where n=1,2,...). When TPU is 85 | #' estimated, it is possible (though rare) that no points are Jmax-limited (in which case estimated 86 | #' Jmax will be NA). A minimum of two points is always reserved for the estimate of Vcmax and Rd. 87 | #' An additional parameter (\code{alphag}) can be set that affects the behaviour at high Ci (see 88 | #' Ellsworth et al. 2015 for details, and also \code{\link{Photosyn}}). See examples.} 89 | #' 90 | #' \subsection{Temperature correction}{When \code{Tcorrect=TRUE} (the default), Jmax and Vcmax 91 | #' are re-scaled to 25C, using the temperature response parameters provided (but Rd is always 92 | #' at measurement temperature). When \code{Tcorrect=FALSE}, estimates of all parameters are at 93 | #' measurement temperature. If TPU is fit, it is never corrected for temperature. Important 94 | #' parameters to the fit are GammaStar and Km, both of which are calculated from leaf temperature 95 | #' using standard formulations. Alternatively, they can be provided as known inputs. \strong{Warning} : 96 | #' since package version 1.4, the default parameters have been adjusted. The new parameter values 97 | #' (EaV, EdVJ, delSJ, etc.) were based on a comprehensive literature review. See 98 | #' \code{vignette("new_T_responses")} or the article on remkoduursma.github.io/plantecophys.} 99 | #' 100 | #' \subsection{Mesophyll conductance}{It is possible to provide an estimate of the mesophyll 101 | #' conductance as input (\code{gmeso}), in which case the fitted Vcmax and Jmax are to be interpreted 102 | #' as chloroplastic rates. When using gmeso, it is recommended to use the 'default' fitting 103 | #' method (which will use the Ethier&Livingston equations inside \code{Photosyn}). It is also 104 | #' implemented with the 'bilinear' method but it requires more testing (and seems to give 105 | #' some strange results). When gmeso is set to a relatively low value, the resulting fit 106 | #' may be quite strange.} 107 | #' 108 | #' \subsection{Other parameters}{The A-Ci curve parameters depend on the values of a number 109 | #' of other parameters. For Jmax, PPFD is needed in order to express it as the asymptote. If 110 | #' PPFD is not provided in the dataset, it is assumed to equal 1800 mu mol m-2 s-1 (in which 111 | #' case a warning is printed). It is possible to either provide PPFD as a variable in the 112 | #' dataset (with the default name 'PARi', which can be changed), or as an argument to 113 | #' the \code{fitaci} directly.} 114 | #' 115 | #' \subsection{Plotting and summarizing}{The default \strong{plot} of the fit is constructed 116 | #' with \code{\link{plot.acifit}}, see Examples below. When plotting the fit, the A-Ci curve 117 | #' is simulated using the \code{\link{Aci}} function, with leaf temperature (Tleaf) and PPFD 118 | #' set to the mean value for the dataset. The \strong{coefficients} estimated in the fit 119 | #' (Vcmax, Jmax, and usually Rd) are extracted with \code{coef}. The summary of the fit is 120 | #' the same as the 'print' method, that is \code{myfit} will give the same output as 121 | #' \code{summary(myfit)} (where \code{myfit} is an object returned by \code{fitaci}). 122 | #' 123 | #' Because fitaci returns the fitted \code{\link{nls}} object, more details on statistics of 124 | #' the fit can be extracted with standard tools. The Examples below shows the use of the 125 | #' \pkg{nlstools} to extract many details of the fit at once. The fit also includes the 126 | #' \strong{root mean squared error} (RMSE), which can be extracted as \code{myfit$RMSE}. This 127 | #' is a useful metric to compare the different fitting methods.} 128 | #' 129 | #' \subsection{Predicting and the CO2 compensation point}{The fitted object contains two functions 130 | #' that reproduce the fitted curve exactly. Suppose your object is called 'myfit', then 131 | #' \code{myfit$Photosyn(200)} will give the fitted rate of photosynthesis at a Ci of 200. 132 | #' The inverse, calculating the Ci where some rate of photosynthesis is achieved, can be done with 133 | #' \code{myfit$Ci(10)} (find the Ci where net photosynthesis is ten). The (fitted!) CO2 134 | #' compensation point can then be calculated with : \code{myfit$Ci(0)}}. 135 | #' 136 | #' \subsection{Atmospheric pressure correction}{Note that atmospheric pressure (Patm) is taken 137 | #' into account, assuming the original data are in molar units (Ci in mu mol mol-1, or ppm). 138 | #' During the fit, Ci is converted to mu bar, and Km and Gammastar are recalculated accounting 139 | #' for the effects of Patm on the partial pressure of oxygen. When plotting the fit, though, 140 | #' molar units are shown on the X-axis. Thus, you should get (nearly) the same fitted curve when 141 | #' Patm was set to a value lower than 100kPa, but the fitted Vcmax and Jmax will be higher. 142 | #' This is because at low Patm, photosynthetic capacity has to be higher to achieve the same 143 | #' measured photosynthesis rate.} 144 | #' 145 | #' \subsection{From time to time, the \code{fitaci} function returns an error, indicating 146 | #' that the A-Ci curve could not be fit. In the majority of cases, this indicates a bad curve 147 | #' that probably could not (and should not) be fit in any case. Inspect the raw data to check 148 | #' if the curve does not include severe outliers, or large deviations from the expected, typical 149 | #' A-Ci curve. If the curve looks fine, refit using the option \code{fitmethod="bilinear"}, 150 | #' which will always return estimated parameters. Whatever you do, do not trust fitted curves 151 | #' without inspecting the raw data, and the fit of the model to the data.} 152 | #' 153 | #' 154 | #' @references 155 | #' Duursma, R.A., 2015. Plantecophys - An R Package for Analysing and Modelling Leaf Gas 156 | #' Exchange Data. PLoS ONE 10, e0143346. doi:10.1371/journal.pone.0143346 157 | #' 158 | #' De Kauwe, M. G. et al. 2016. A test of the 'one-point method' for estimating maximum 159 | #' carboxylation capacity from field-measured, light-saturated photosynthesis. 160 | #' New Phytol 210, 1130-1144. 161 | #' 162 | #' @return A list of class 'acifit', with the following components: 163 | #' \describe{ 164 | #' \item{df}{A dataframe with the original data, including the measured photosynthetic 165 | #' rate (Ameas), the fitted photosynthetic rate (Amodel), Jmax and Vcmax-limited gross 166 | #' rates (Aj, Ac), TPU-limited rate (Ap), dark respiration (Rd), leaf temperature (Tleaf), 167 | #' chloroplastic CO2 (Cc), PPFD, atmospheric pressure (Patm), and 'original Ci, i.e. the 168 | #' Ci used as input (which is different from the Ci used in fitting if Patm was not set to 100kPa)} 169 | #' \item{pars}{Contains the parameter estimates and their approximate standard errors} 170 | #' \item{nlsfit}{The object returned by \code{\link{nls}}, and contains more detail on 171 | #' the quality of the fit} 172 | #' \item{Tcorrect}{whether the temperature correction was applied (logical)} 173 | #' \item{Photosyn}{A copy of the \code{\link{Photosyn}} function with the arguments adjusted for 174 | #' the current fit. That is, Vcmax, Jmax and Rd are set to those estimated in the fit, and Tleaf and 175 | #' PPFD are set to the mean value in the dataset. All other parameters that were set in fitaci are 176 | #' also used (e.g. temperature dependency parameters, TPU, etc.).} 177 | #' \item{Ci}{As Photosyn, except the opposite: calculate the Ci where some rate of net photosynthesis 178 | #' is achieved.} 179 | #' \item{Ci_transition}{The Ci at which photosynthesis transitions from Vcmax 180 | #' to Jmax limited photosynthesis.} 181 | #' \item{Ci_transition2}{The Ci at which photosynthesis transitions from Jmax to 182 | #' TPU limitation. Set to NA is either TPU was not estimated, or it could not be 183 | #' estimated from the data.} 184 | #' \item{Rd_measured}{Logical - was Rd provided as measured input?} 185 | #' \item{GammaStar}{The value for GammaStar, either calculated or provided to the fit.} 186 | #' \item{Km}{he value for Km, either calculated or provided to the fit.} 187 | #' \item{kminput}{Was Km provided as input? (If FALSE, it was calculated from Tleaf)} 188 | #' \item{gstarinput}{Was GammaStar provided as input? (If FALSE, it was calculated from Tleaf)} 189 | #' \item{fitmethod}{The fitmethod uses, either default or bilinear} 190 | #' \item{citransition}{The input citransition (NA if it was not provided as input)} 191 | #' \item{gmeso}{The mesophyll conductance used in the fit (NA if it was not set)} 192 | #' \item{fitTPU}{Was TPU fit?} 193 | #' \item{alphag}{The value of alphag used in estimating TPU.} 194 | #' \item{RMSE}{The Root-mean squared error, calculated as \code{sqrt(sum((Ameas-Amodel)^2))}.} 195 | #' \item{runorder}{The data returned in the 'df' slot are ordered by Ci, but in rare cases the 196 | #' original order of the data contains information; 'runorder' is 197 | #' the order in which the data were provided.} 198 | #' 199 | #' } 200 | #' @examples 201 | #' \dontrun{ 202 | #' # Fit an A-Ci curve on a dataframe that contains Ci, Photo and optionally Tleaf and PPFD. 203 | #' # Here, we use the built-in example dataset 'acidata1'. 204 | #' f <- fitaci(acidata1) 205 | #' 206 | #' # Note that the default behaviour is to correct Vcmax and Jmax for temperature, 207 | #' # so the estimated values are at 25C. To turn this off: 208 | #' f2 <- fitaci(acidata1, Tcorrect=FALSE) 209 | #' 210 | #' # To use different T response parameters (see ?Photosyn), 211 | #' f3 <- fitaci(acidata1, Tcorrect=TRUE, EaV=25000) 212 | #' 213 | #' # Make a standard plot 214 | #' plot(f) 215 | #' 216 | #' # Look at a summary of the fit 217 | #' summary(f) 218 | #' 219 | #' # Extract coefficients only 220 | #' coef(f) 221 | #' 222 | #' # The object 'f' also contains the original data with predictions. 223 | #' # Here, Amodel are the modelled (fitted) values, Ameas are the measured values. 224 | #' with(f$df, plot(Amodel, Ameas)) 225 | #' abline(0,1) 226 | #' 227 | #' # The fitted values can also be extracted with the fitted() function: 228 | #' fitted(f) 229 | #' 230 | #' # The non-linear regression (nls) fit is stored as well, 231 | #' summary(f$nlsfit) 232 | #' 233 | #' # Many more details can be extracted with the nlstools package 234 | #' library(nlstools) 235 | #' overview(f$nlsfit) 236 | #' 237 | #' # The curve generator is stored as f$Photosyn: 238 | #' # Calculate photosynthesis at some value for Ci, using estimated 239 | #' # parameters and mean Tleaf, PPFD for the dataset. 240 | #' f$Photosyn(Ci=820) 241 | #' 242 | #' # Photosynthetic rate at the transition point: 243 | #' f$Photosyn(Ci=f$Ci_transition)$ALEAF 244 | #' 245 | #' # Set the transition point; this will fit Vcmax and Jmax separately. Note that the *actual* 246 | #' # transition is quite different from that provided, this is perfectly fine : 247 | #' # in this case Jmax is estimated from the latter 3 points only (Ci>800), but the actual 248 | #' # transition point is at ca. 400ppm. 249 | #' g <- fitaci(acidata1, citransition=800) 250 | #' plot(g) 251 | #' g$Ci_transition 252 | #' 253 | #' # Use measured Rd instead of estimating it from the A-Ci curve. 254 | #' # The Rd measurement must be added to the dataset used in fitting, 255 | #' # and you must set useRd=TRUE. 256 | #' acidata1$Rd <- 2 257 | #' f2 <- fitaci(acidata1, useRd=TRUE) 258 | #' f2 259 | #' 260 | #' # Fit TPU limitation 261 | #' ftpu <- fitaci(acidata1, fitTPU=TRUE, PPFD=1800, Tcorrect=TRUE) 262 | #' plot(ftpu) 263 | #' } 264 | #' @export 265 | #' @rdname fitaci 266 | #' @importFrom stats lm 267 | 268 | fitaci <- function(data, 269 | varnames=list(ALEAF="A", Tleaf="TleafCnd", 270 | Ci="Ci", PPFD="Qin", Rd="Rd"), 271 | Tcorrect=TRUE, 272 | Patm=100, 273 | citransition=NULL, 274 | quiet=FALSE, startValgrid=TRUE, 275 | fitmethod=c("default","bilinear","onepoint"), 276 | algorithm= "default", 277 | fitTPU=FALSE, 278 | alphag=0, 279 | 280 | useRd=FALSE, 281 | PPFD=NULL, 282 | Tleaf=NULL, 283 | 284 | alpha=0.24, 285 | theta=0.85, 286 | gmeso=NULL, 287 | EaV = 82620.87, 288 | EdVC = 0, 289 | delsC = 645.1013, 290 | EaJ = 39676.89, 291 | EdVJ = 200000, 292 | delsJ = 641.3615, 293 | 294 | GammaStar = NULL, 295 | Km = NULL, 296 | id=NULL, 297 | ...){ 298 | 299 | fitmethod <- match.arg(fitmethod) 300 | if(fitTPU & fitmethod == "default")fitmethod <- "bilinear" 301 | 302 | if(fitTPU & fitmethod == "onepoint"){ 303 | Stop("TPU limitation not implemented with one-point method.") 304 | } 305 | 306 | if(fitmethod == "onepoint")useRd <- TRUE 307 | 308 | gstarinput <- !is.null(GammaStar) 309 | kminput <- !is.null(Km) 310 | if(is.null(gmeso))gmeso <- -999 # cannot pass NULL value to nls 311 | 312 | # Check data 313 | if(nrow(data) == 0){ 314 | Stop("No rows in data - check observations.") 315 | } 316 | 317 | # Make sure data is a proper dataframe, not some tibble or other nonsense. 318 | data <- as.data.frame(data) 319 | 320 | # Extra parameters not used at the moment; warn when provided. 321 | chkDots(...) 322 | 323 | # Add PPFD and Tleaf to data, if needed (uses default values, or input values) 324 | data <- set_PPFD(varnames, data, PPFD, quiet) 325 | data <- set_Tleaf(varnames, data, Tleaf, quiet) 326 | 327 | # Set measured Rd if provided (or warn when provided but not used) 328 | Rd_meas <- set_Rdmeas(varnames, data, useRd, citransition, quiet) 329 | haveRd <- !is.na(Rd_meas) 330 | 331 | # Must have Rd with one-point method. 332 | if(!haveRd && fitmethod == "onepoint"){ 333 | Stop("Must add Rd to the dataset (and check that varnames is set correctly).") 334 | } 335 | 336 | # Extract Ci and apply pressure correction 337 | data$Ci_original <- data[,varnames$Ci] 338 | data$Ci <- data[,varnames$Ci] * Patm/100 339 | 340 | # Extract measured net leaf photosynthesis 341 | data$ALEAF <- data[,varnames$ALEAF] 342 | 343 | # Calculate Km and GammaStar, if not input 344 | # (Photosyn does this as well, but have to repeat it here 345 | # since we cannot have NULL in call to nls) 346 | Km_v <- if(!kminput) TKm(data$Tleaf, Patm) else rep(Km, nrow(data)) 347 | GammaStar_v <- if(!gstarinput){ 348 | TGammaStar(data$Tleaf, Patm) 349 | } else { 350 | rep(GammaStar, nrow(data)) 351 | } 352 | 353 | # Citransition not defined: 354 | # default method = full non-linear model 355 | # bilinear = optimize transition point in bilinear fit 356 | if(is.null(citransition)){ 357 | 358 | if(fitmethod == "default"){ 359 | 360 | f <- do_fit_method1(data, haveRd, Rd_meas, Patm, startValgrid, 361 | Tcorrect, algorithm,alpha,theta,gmeso,EaV, 362 | EdVC,delsC,EaJ,EdVJ,delsJ,GammaStar_v,Km_v) 363 | } 364 | if(fitmethod == "bilinear"){ 365 | 366 | f <- do_fit_method_bilinear_bestcitrans(data, haveRd, fitTPU, alphag, Rd_meas, 367 | Patm, Tcorrect, algorithm, 368 | alpha,theta,gmeso,EaV,EdVC,delsC, 369 | EaJ,EdVJ,delsJ, 370 | GammaStar_v, Km_v) 371 | } 372 | if(fitmethod == "onepoint"){ 373 | f <- do_fit_method_bilinear(data, haveRd, alphag, Rd_meas, Patm, 374 | NA,NA, # don't matter 375 | Tcorrect=Tcorrect, algorithm, 376 | alpha,theta,gmeso,EaV,EdVC,delsC, 377 | EaJ,EdVJ,delsJ, 378 | GammaStar, Km, onepoint=TRUE) 379 | return(f) 380 | } 381 | } 382 | 383 | # Ci transition defined. 384 | # default - two nonlinear fits (first Vcmax, then Jmax region) 385 | # bilinear - two linear fits 386 | if(!is.null(citransition)){ 387 | 388 | # NOTE-- bug in default method when citransition specified. Switch off for now. 389 | fitmethod <- "bilinear" 390 | 391 | # if(fitmethod == "default"){ 392 | # f <- do_fit_method2(data, haveRd, Rd_meas, Patm, citransition, Tcorrect, 393 | # algorithm,alpha,theta,gmeso,EaV,EdVC, 394 | # delsC,EaJ,EdVJ,delsJ,GammaStar_v,Km_v) 395 | # } 396 | if(fitmethod == "bilinear"){ 397 | f <- do_fit_method_bilinear(data, haveRd, alphag, Rd_meas, Patm, citransition, 398 | NULL, Tcorrect, algorithm, 399 | alpha,theta,gmeso,EaV,EdVC,delsC,EaJ,EdVJ,delsJ, 400 | GammaStar_v, Km_v) 401 | } 402 | 403 | } 404 | 405 | 406 | # TPU. If it is not estimated, put something in because we need it later. 407 | if(!("TPU" %in% names(f)))f$TPU <- 1000 408 | 409 | # Only used to add 'Amodel' to the output 410 | acirun <- do_acirun(data,f,Patm,Tcorrect, # Note several parameters are stored in 'f' 411 | alpha=alpha,theta=theta, 412 | gmeso=gmeso,EaV=EaV, 413 | EdVC=EdVC,delsC=delsC, 414 | EaJ=EaJ,EdVJ=EdVJ, 415 | delsJ=delsJ,GammaStar=GammaStar_v, Km=Km_v) 416 | 417 | # If Ap is never actually limiting, set estimated TPU to 1000 418 | # This means there is no evidence for TPU-limitation. 419 | if(!any(acirun$Ap < acirun$Aj))f$TPU <- 1000 420 | 421 | # Organize output 422 | l <- list() 423 | runorder <- order(acirun$Ci) 424 | l$df <- acirun[runorder,] 425 | if(!is.null(id)){ 426 | if(any(!id %in% names(data))){ 427 | Warning("id ignored - not all variables are in dataset provided") 428 | } else { 429 | l$df <- cbind(l$df, data[id]) 430 | } 431 | } 432 | l$id <- id 433 | l$pars <- f$pars 434 | if(fitTPU){ 435 | val <- ifelse(f$TPU < 1000, f$TPU, NA) 436 | tpu <- matrix(c(val,NA), ncol=2) 437 | colnames(tpu) <- colnames(l$pars) 438 | rownames(tpu) <- "TPU" 439 | l$pars <- rbind(l$pars, tpu) 440 | } 441 | 442 | l$nlsfit <- f$fit 443 | l$Tcorrect <- Tcorrect 444 | 445 | # Save function itself, the formals contain the parameters 446 | # used to fit the A-Ci curve. 447 | # First save Tleaf, PPFD in the formals (as the mean of the dataset) 448 | formals(Photosyn)$Tleaf <- mean(data$Tleaf) 449 | formals(Photosyn)$Patm <- Patm 450 | formals(Photosyn)$PPFD <- mean(data$PPFD) 451 | formals(Photosyn)$Vcmax <- l$pars[1] 452 | formals(Photosyn)$Jmax <- l$pars[2] 453 | formals(Photosyn)$Rd <- l$pars[3] 454 | formals(Photosyn)$TPU <- if(fitTPU)l$pars["TPU","Estimate"] else 1000 455 | formals(Photosyn)$alphag <- alphag 456 | formals(Photosyn)$Tcorrect <- Tcorrect 457 | formals(Photosyn)$alpha <- alpha 458 | formals(Photosyn)$theta <- theta 459 | formals(Photosyn)$gmeso <- gmeso 460 | formals(Photosyn)$EaV <- EaV 461 | formals(Photosyn)$EdVC <- EdVC 462 | formals(Photosyn)$delsC <- delsC 463 | formals(Photosyn)$EaJ <- EaJ 464 | formals(Photosyn)$EdVJ <- EdVJ 465 | formals(Photosyn)$delsJ <- delsJ 466 | 467 | if(gstarinput)formals(Photosyn)$GammaStar <- GammaStar 468 | if(kminput)formals(Photosyn)$Km <- Km 469 | 470 | l$Photosyn <- Photosyn 471 | 472 | # The inverse - find Ci given a rate of photosyntheiss 473 | l$Ci <- function(ALEAF){ 474 | 475 | O <- function(ci, photo){ 476 | (l$Photosyn(Ci=ci)$ALEAF - photo)^2 477 | } 478 | o <- optimize(O, interval=c(1,10^5), photo =ALEAF) 479 | if(o$objective > 1e-02){ 480 | return(NA) 481 | } else { 482 | return(o$minimum) 483 | } 484 | } 485 | 486 | # Transition points. 487 | trans <- findCiTransition(l$Photosyn) 488 | l$Ci_transition <- trans[1] 489 | l$Ci_transition2 <- trans[2] 490 | l$Rd_measured <- haveRd 491 | 492 | # Save GammaStar and Km (either evaluated at mean temperature, 493 | # or input if provided) 494 | l$GammaStar <- mean(GammaStar_v) 495 | l$Km <- mean(Km_v) 496 | l$kminput <- kminput 497 | l$gstarinput <- gstarinput 498 | 499 | l$fitmethod <- fitmethod 500 | l$citransition <- ifelse(is.null(citransition), NA, citransition) 501 | l$gmeso <- ifelse(is.null(gmeso) || gmeso < 0, NA, gmeso) 502 | l$fitTPU <- fitTPU 503 | l$alphag <- alphag 504 | l$RMSE <- rmse_acifit(l) 505 | l$runorder <- runorder 506 | 507 | class(l) <- "acifit" 508 | 509 | return(l) 510 | } 511 | 512 | 513 | #-- Component functions of fitaci 514 | 515 | rmse_acifit <- function(x)sqrt(sum((x$df$Ameas - x$df$Amodel)^2)) 516 | 517 | guess_Jmax <- function(data, Rd_guess, Patm, Tcorrect){ 518 | 519 | # Guess Jmax from max A, T-corrected gammastar (starting value) 520 | maxCi <- max(data$Ci) 521 | mi <- which.max(data$Ci) 522 | 523 | maxPhoto <- data$ALEAF[mi] 524 | 525 | Tl <- data$Tleaf[mi] 526 | gammastar <- TGammaStar(Tl,Patm) 527 | VJ <- (maxPhoto+Rd_guess) / ((maxCi - gammastar) / (maxCi + 2*gammastar)) 528 | Jmax_guess <- VJ*4 529 | if(Tcorrect){ 530 | Teffect <- TJmax(Tl, EaJ=39676.89, delsJ=641.3615, EdVJ=200000) 531 | Jmax_guess <- Jmax_guess / Teffect 532 | } 533 | 534 | return(Jmax_guess) 535 | } 536 | 537 | 538 | 539 | guess_Vcmax <- function(data, Jmax_guess, Rd_guess, Patm, Tcorrect){ 540 | # Guess Vcmax, from section of curve that is definitely Vcmax-limited 541 | dato <- data[data$Ci < 150 & data$Ci > 60 & data$ALEAF > 0,] 542 | if(nrow(dato) > 0){ 543 | Km <- TKm(dato$Tleaf,Patm) 544 | gammastar <- TGammaStar(dato$Tleaf,Patm) 545 | vcmax <- with(dato, (ALEAF + Rd_guess) / ((Ci - gammastar)/(Ci + Km))) 546 | Vcmax_guess <- median(vcmax) 547 | } else { 548 | Vcmax_guess <- Jmax_guess/1.8 549 | } 550 | if(Tcorrect){ 551 | Teffect <- TVcmax(mean(data$Tleaf), EaV=82620.87, delsC=645.1013, EdVC=0) 552 | Vcmax_guess <- Vcmax_guess / Teffect 553 | } 554 | return(Vcmax_guess) 555 | } 556 | 557 | 558 | guess_Rd <- function(haveRd, Rd_meas, default=1.5){ 559 | if(haveRd){ 560 | Rd_guess <- Rd_meas 561 | } else { 562 | Rd_guess <- default 563 | } 564 | return(Rd_guess) 565 | } 566 | 567 | set_PPFD <- function(varnames, data, PPFD, quiet){ 568 | 569 | if(!varnames$PPFD %in% names(data) & is.null(PPFD)){ 570 | data$PPFD <- 1800 571 | if(!quiet)Warning("PARi not in dataset; assumed PARi = 1800.") 572 | } else { 573 | if(!is.null(PPFD)) 574 | data$PPFD <- PPFD 575 | else 576 | data$PPFD <- data[,varnames$PPFD] 577 | } 578 | return(data) 579 | } 580 | 581 | 582 | 583 | set_Tleaf <- function(varnames, data, Tleaf, quiet){ 584 | # Check if Tleaf is provided 585 | if(!varnames$Tleaf %in% names(data) & is.null(Tleaf)){ 586 | data$Tleaf <- 25 587 | if(!quiet)Warning("Tleaf not in dataset; assumed Tleaf = 25.") 588 | } else { 589 | if(!is.null(Tleaf)) 590 | data$Tleaf <- Tleaf 591 | else 592 | data$Tleaf <- data[,varnames$Tleaf] 593 | } 594 | return(data) 595 | } 596 | 597 | 598 | 599 | # Check if Rd is provided and whether we want to set it as known. 600 | set_Rdmeas <- function(varnames, data, useRd, citransition, quiet){ 601 | 602 | Rd_meas <- NA 603 | if(!is.null(varnames$Rd)){ 604 | if(varnames$Rd %in% names(data) && useRd){ 605 | 606 | # Has to be a single unique value for this dataset 607 | Rd_meas <- data[,varnames$Rd] 608 | Rd_meas <- unique(Rd_meas) 609 | if(length(Rd_meas) > 1){ 610 | Stop("If Rd provided as measured, it must be a single", 611 | "\nunique value for an A-Ci curve.") 612 | } 613 | 614 | # Use positive value throughout. 615 | Rd_meas <- abs(Rd_meas) 616 | haveRd <- TRUE 617 | 618 | if(!is.null(citransition)) 619 | Stop("At the moment cannot provide citransition as well as measured Rd.") 620 | } 621 | if(varnames$Rd %in% names(data) && !useRd){ 622 | if(!quiet) 623 | message(paste("Rd found in dataset but useRd set to FALSE.", 624 | "Set to TRUE to use measured Rd.")) 625 | } 626 | } 627 | return(Rd_meas) 628 | } 629 | 630 | 631 | 632 | 633 | 634 | do_fit_method1 <- function(data, haveRd, Rd_meas, Patm, startValgrid, 635 | Tcorrect, algorithm, 636 | alpha,theta,gmeso,EaV,EdVC,delsC,EaJ,EdVJ,delsJ, 637 | GammaStar,Km){ 638 | 639 | # Guess Rd (starting value) 640 | Rd_guess <- guess_Rd(haveRd, Rd_meas) 641 | Jmax_guess <- guess_Jmax(data, Rd_guess, Patm, Tcorrect) 642 | Vcmax_guess <- guess_Vcmax(data, Jmax_guess, Rd_guess, Patm, Tcorrect) 643 | 644 | # Fine-tune starting values; try grid of values around initial estimates. 645 | if(startValgrid){ 646 | if(!haveRd){ 647 | aciSS <- function(Vcmax, Jmax, Rd){ 648 | Photo_mod <- acifun_wrap(data$Ci, PPFD=data$PPFD, 649 | Vcmax=Vcmax, Jmax=Jmax, 650 | Rd=Rd, Tleaf=data$Tleaf, Patm=Patm, 651 | TcorrectVJ=Tcorrect, 652 | alpha=alpha,theta=theta, 653 | gmeso=gmeso,EaV=EaV, 654 | EdVC=EdVC,delsC=delsC, 655 | EaJ=EaJ,EdVJ=EdVJ, 656 | delsJ=delsJ,Km=Km,GammaStar=GammaStar) 657 | 658 | SS <- sum((data$ALEAF - Photo_mod)^2) 659 | return(SS) 660 | } 661 | d <- 0.4 662 | n <- 25 663 | gg <- expand.grid(Vcmax=seq(Vcmax_guess*(1-d),Vcmax_guess*(1+d),length=n), 664 | Rd=seq(Rd_guess*(1-d),Rd_guess*(1+d),length=n)) 665 | 666 | m <- with(gg, mapply(aciSS, Vcmax=Vcmax, Jmax=Jmax_guess, Rd=Rd)) 667 | ii <- which.min(m) 668 | Vcmax_guess <- gg$Vcmax[ii] 669 | Rd_guess <- gg$Rd[ii] 670 | 671 | } else { 672 | 673 | aciSS <- function(Vcmax, Jmax, Rd){ 674 | Photo_mod <- acifun_wrap(data$Ci, PPFD=data$PPFD, 675 | Vcmax=Vcmax, Jmax=Jmax, 676 | Rd=Rd, Tleaf=data$Tleaf, Patm=Patm, 677 | TcorrectVJ=Tcorrect, 678 | alpha=alpha,theta=theta, 679 | gmeso=gmeso,EaV=EaV, 680 | EdVC=EdVC,delsC=delsC, 681 | EaJ=EaJ,EdVJ=EdVJ, 682 | delsJ=delsJ,Km=Km,GammaStar=GammaStar) 683 | SS <- sum((data$ALEAF - Photo_mod)^2) 684 | return(SS) 685 | } 686 | d <- 0.3 687 | n <- 20 688 | gg <- data.frame(Vcmax=seq(Vcmax_guess*(1-d),Vcmax_guess*(1+d),length=n)) 689 | 690 | m <- with(gg, mapply(aciSS, Vcmax=Vcmax, Jmax=Jmax_guess, Rd=Rd_meas)) 691 | ii <- which.min(m) 692 | Vcmax_guess <- gg$Vcmax[ii] 693 | 694 | } 695 | 696 | } 697 | 698 | if(!haveRd){ 699 | # Fit Vcmax, Jmax and Rd 700 | nlsfit <- try(nls(ALEAF ~ acifun_wrap(Ci, PPFD=PPFD, Vcmax=Vcmax, 701 | Jmax=Jmax, Rd=Rd, Tleaf=Tleaf, Patm=Patm, 702 | TcorrectVJ=Tcorrect, 703 | alpha=alpha,theta=theta, 704 | gmeso=gmeso,EaV=EaV, 705 | EdVC=EdVC,delsC=delsC, 706 | EaJ=EaJ,EdVJ=EdVJ, 707 | delsJ=delsJ,Km=Km,GammaStar=GammaStar), 708 | algorithm=algorithm, 709 | data=data, control=nls.control(maxiter=800, minFactor=1/10000), 710 | start=list(Vcmax=Vcmax_guess, Jmax=Jmax_guess, Rd=Rd_guess)), silent=TRUE) 711 | 712 | if(inherits(nlsfit, "try-error")){ 713 | Stop("Could not fit curve - check quality of data or fit using fitmethod='bilinear'.") 714 | } 715 | 716 | p <- coef(nlsfit) 717 | pars <- summary(nlsfit)$coefficients[,1:2] 718 | 719 | } else { 720 | 721 | # Fit Vcmax and Jmax 722 | nlsfit <- try(nls(ALEAF ~ acifun_wrap(Ci, PPFD=PPFD, Vcmax=Vcmax, 723 | Jmax=Jmax, Rd=Rd_meas, Tleaf=Tleaf, Patm=Patm, 724 | TcorrectVJ=Tcorrect, 725 | alpha=alpha,theta=theta, 726 | gmeso=gmeso,EaV=EaV, 727 | EdVC=EdVC,delsC=delsC, 728 | EaJ=EaJ,EdVJ=EdVJ, 729 | delsJ=delsJ,Km=Km,GammaStar=GammaStar), 730 | algorithm=algorithm, 731 | data=data, control=nls.control(maxiter=800, minFactor=1/10000), 732 | start=list(Vcmax=Vcmax_guess, Jmax=Jmax_guess, Rd=Rd_guess)), silent=TRUE) 733 | 734 | if(inherits(nlsfit, "try-error")){ 735 | Stop("Could not fit curve -", 736 | "\ncheck quality of data or fit using fitmethod='bilinear'.") 737 | } 738 | 739 | p <- coef(nlsfit) 740 | p[[3]] <- Rd_meas 741 | names(p)[3] <- "Rd" 742 | 743 | pars <- summary(nlsfit)$coefficients[,1:2] 744 | pars <- rbind(pars, c(Rd_meas, NA)) 745 | rownames(pars)[3] <- "Rd" 746 | } 747 | 748 | print(summary(nlsfit)) 749 | return(list(pars=pars, fit=nlsfit)) 750 | } 751 | 752 | 753 | 754 | 755 | do_fit_method_bilinear <- function(data, haveRd, alphag, Rd_meas, 756 | Patm, citransition, citransition2=NULL, 757 | Tcorrect, algorithm, 758 | alpha,theta,gmeso,EaV,EdVC,delsC, 759 | EaJ,EdVJ,delsJ, 760 | GammaStar, Km, onepoint=FALSE){ 761 | 762 | # Calculate T- <- ependent parameters 763 | ppar <- Photosyn(Tleaf=data$Tleaf, Patm=Patm, Tcorrect=Tcorrect, 764 | alpha=alpha,theta=theta, 765 | gmeso=gmeso,EaV=EaV, 766 | EdVC=EdVC,delsC=delsC, 767 | EaJ=EaJ,EdVJ=EdVJ, 768 | delsJ=delsJ, 769 | returnParsOnly=TRUE) 770 | if(!is.null(GammaStar))ppar$GammaStar <- GammaStar 771 | if(!is.null(Km))ppar$Km <- Km 772 | if(is.null(citransition2))citransition2 <- 10^6 773 | 774 | # Calculate Cc if gmeso included 775 | if(!is.null(gmeso) && gmeso > 0){ 776 | Conc <- data$Ci - data$ALEAF/gmeso 777 | } else { 778 | Conc <- data$Ci 779 | } 780 | 781 | # Linearize 782 | data$vcmax_pred <- (Conc - ppar$GammaStar)/(Conc + ppar$Km) 783 | data$Jmax_pred <- (Conc - ppar$GammaStar)/(Conc + 2*ppar$GammaStar) 784 | data$TPU_part <- (Conc - ppar$GammaStar)/(Conc - (1+3*alphag)*ppar$GammaStar) 785 | 786 | if(onepoint){ 787 | 788 | orig_dat <- data[, 1:(match("Ci_original", names(data))-1)] 789 | 790 | orig_dat$Vcmax <- (data$ALEAF + Rd_meas) / data$vcmax_pred 791 | J <- (data$ALEAF + Rd_meas) / data$Jmax_pred 792 | orig_dat$Jmax <- inverseJfun(mean(data$PPFD), alpha, 4 * J, theta) 793 | 794 | if(Tcorrect){ 795 | orig_dat$Jmax <- orig_dat$Jmax / TJmax(data$Tleaf, EaJ, delsJ, EdVJ) 796 | orig_dat$Vcmax <- orig_dat$Vcmax / TVcmax(data$Tleaf,EaV, delsC, EdVC) 797 | } 798 | 799 | return(orig_dat) 800 | } 801 | 802 | # Fit Vcmax and Rd from linearized portion 803 | datv <- data[data$Ci < citransition & data$Ci < citransition2,] 804 | if(nrow(datv) == 0){ 805 | return(list(pars=NA, fit=NA, TPU=NA, success=FALSE)) 806 | } 807 | 808 | if(!haveRd){ 809 | fitv <- lm(ALEAF ~ vcmax_pred, data=datv) 810 | Rd_fit <- coef(fitv)[[1]] 811 | Vcmax_fit <- coef(fitv)[[2]] 812 | } else { 813 | # If using measured Rd, add to Anet, and remove intercept from fit. 814 | datv$ALEAFg <- datv$ALEAF + Rd_meas 815 | fitv <- lm(ALEAFg ~ vcmax_pred-1, data=datv) 816 | Rd_fit <- -Rd_meas 817 | Vcmax_fit <- coef(fitv)[[1]] 818 | } 819 | 820 | # Fit Jmax from linearized portion 821 | datj <- data[data$Ci >= citransition & data$Ci < citransition2,] 822 | datp <- data[data$Ci >= citransition2,] 823 | 824 | # Manual fix: if only one point for TPU, and none for Jmax, abandon fit. 825 | # In this case it would be more defensible to use the single point for Jmax. 826 | if(nrow(datp) == 1 && nrow(datj) == 0){ 827 | return(list(pars=NA, fit=NA, TPU=NA, success=FALSE)) 828 | } 829 | 830 | 831 | if(nrow(datj) > 0){ 832 | 833 | # Fit gross photo using fitted Rd 834 | # (Rd_fit is negative) 835 | datj$Agross <- datj$ALEAF - Rd_fit 836 | 837 | # One point, calculate directly 838 | if(nrow(datj) == 1){ 839 | J_fit <- with(datj, 4 * Agross / Jmax_pred) 840 | } else { 841 | fitj <- lm(Agross ~ Jmax_pred-1, data=datj) 842 | J_fit <- 4 * coef(fitj)[[1]] 843 | } 844 | 845 | # And solve for Jmax from inverse non-rect. hyperbola 846 | Jmax_fit <- inverseJfun(mean(data$PPFD), alpha, J_fit, theta) 847 | 848 | } else { 849 | Jmax_fit <- 10^6 # not elegant but will do for now 850 | # (avoids trouble elsewhere) 851 | } 852 | 853 | # TPU 854 | if(nrow(datp) > 0){ 855 | datp$Agross <- datp$ALEAF - Rd_fit 856 | 857 | tpu_vals <- (1/3) * datp$Agross / datp$TPU_part 858 | 859 | TPU <- mean(tpu_vals) 860 | } else { 861 | TPU <- 1000 # same as default in Photosyn 862 | } 863 | 864 | # The above estimates are at the measured Tleaf. 865 | # Express at 25C? 866 | if(Tcorrect){ 867 | Jmax_fit <- Jmax_fit / TJmax(mean(data$Tleaf), EaJ, delsJ, EdVJ) 868 | Vcmax_fit <- Vcmax_fit / TVcmax(mean(data$Tleaf),EaV, delsC, EdVC) 869 | } 870 | 871 | ses <- summary(fitv)$coefficients[,2] 872 | if(!haveRd){ 873 | pars <- matrix(c(Vcmax_fit, Jmax_fit, -Rd_fit, 874 | ses[2],NA,ses[1]), ncol=2) 875 | } else { 876 | pars <- matrix(c(Vcmax_fit, Jmax_fit, -Rd_fit, 877 | ses[1],NA,NA), ncol=2) 878 | } 879 | 880 | 881 | rownames(pars) <- c("Vcmax","Jmax","Rd") 882 | colnames(pars) <- c("Estimate","Std. Error") 883 | 884 | 885 | return(list(pars=pars, fit=fitv, TPU=TPU, success=TRUE)) 886 | } 887 | 888 | do_fit_method_bilinear_bestcitrans <- function(data, haveRd, fitTPU, alphag, 889 | Rd_meas, Patm, Tcorrect, 890 | algorithm,alpha,theta,gmeso,EaV, 891 | EdVC,delsC,EaJ,EdVJ, 892 | delsJ,GammaStar, Km){ 893 | 894 | # Possible Ci transitions 895 | ci <- data$Ci 896 | nci <- length(ci) 897 | citransitions <- diff(ci)/2 + ci[-nci] 898 | 899 | # at least two Ci values to estimate Vcmax and Rd, so delete first 900 | citransitions1 <- citransitions[-1] 901 | 902 | # Possible transitions to TPU 903 | if(!fitTPU){ 904 | citransitions2 <- max(ci) + 1 # outside range, on purpose 905 | } else { 906 | # start at top, all the way down, leave lowest 2 points alone 907 | citransitions2 <- c(max(ci) + 1, rev(citransitions1)) 908 | } 909 | citransdf <- expand.grid(ci1=citransitions1, ci2=citransitions2) 910 | citransdf <- citransdf[citransdf$ci1 <= citransdf$ci2,] 911 | SS <- c() 912 | 913 | # Note that Tcorrect is set to FALSE inside the loop. If Tcorrect is needed, it is done 914 | # after the loop finishes (avoids a bug). 915 | for(i in seq_len(nrow(citransdf))){ 916 | 917 | fit <- do_fit_method_bilinear(data, haveRd, alphag, Rd_meas, Patm, 918 | citransdf$ci1[i], citransdf$ci2[i], 919 | Tcorrect=FALSE, algorithm, 920 | alpha,theta,gmeso,EaV,EdVC,delsC, 921 | EaJ,EdVJ,delsJ, 922 | GammaStar, Km) 923 | 924 | if(fit$success && !any(is.na(fit$pars[,"Estimate"]))){ 925 | run <- do_acirun(data,fit,Patm,Tcorrect=FALSE, 926 | alpha=alpha,theta=theta, 927 | gmeso=gmeso,EaV=EaV, 928 | EdVC=EdVC,delsC=delsC, 929 | EaJ=EaJ,EdVJ=EdVJ, 930 | delsJ=delsJ,GammaStar=GammaStar,Km=Km) 931 | 932 | SS[i] <- sum((run$Ameas - run$Amodel)^2) 933 | } else { 934 | SS[i] <- 10^6 935 | } 936 | } 937 | 938 | # Best Ci transitions 939 | bestcis <- citransdf[which.min(SS),] 940 | 941 | f <- do_fit_method_bilinear(data, haveRd, alphag, Rd_meas, Patm, 942 | bestcis$ci1, bestcis$ci2, Tcorrect, algorithm, 943 | alpha,theta,gmeso,EaV,EdVC,delsC,EaJ,EdVJ,delsJ, 944 | GammaStar, Km) 945 | 946 | if(f$pars["Jmax","Estimate"] < 0){ 947 | Stop("Cannot invert light response curve to estimate Jmax - increase alpha or theta.") 948 | } 949 | 950 | 951 | return(f) 952 | } 953 | 954 | # Wrapper around Photosyn; this wrapper will be sent to nls. 955 | acifun_wrap <- function(Ci,..., TcorrectVJ, returnwhat="ALEAF"){ 956 | r <- Photosyn(Ci=Ci,Tcorrect=TcorrectVJ,...) 957 | if(returnwhat == "ALEAF")return(r$ALEAF) 958 | if(returnwhat == "Ac")return(r$Ac - r$Rd) 959 | if(returnwhat == "Aj")return(r$Aj - r$Rd) 960 | } 961 | 962 | 963 | # Using fitted coefficients, get predictions from model. 964 | do_acirun <- function(data,f,Patm,Tcorrect,...){ 965 | 966 | acirun <- Photosyn(Ci=data$Ci, 967 | Vcmax=f$pars[1,1], Jmax=f$pars[2,1], 968 | Rd=f$pars[3,1], 969 | TPU=f$TPU, 970 | PPFD=data$PPFD, 971 | Tleaf=data$Tleaf, 972 | Patm=Patm, 973 | Tcorrect=Tcorrect,...) 974 | 975 | acirun$Ameas <- data$ALEAF 976 | acirun$ELEAF <- NULL 977 | acirun$GS <- NULL 978 | acirun$Ca <- NULL 979 | acirun$Ci_original <- data$Ci_original 980 | names(acirun)[names(acirun) == "ALEAF"] <- "Amodel" 981 | 982 | # shuffle 983 | avars <- match(c("Ci","Ameas","Amodel"),names(acirun)) 984 | acirun <- acirun[,c(avars, setdiff(1:ncol(acirun), avars))] 985 | 986 | return(acirun) 987 | } 988 | 989 | 990 | --------------------------------------------------------------------------------