# Fit models to estimate light respiration

#### Joseph R. Stinziano and Christopher D. Muir

#### 2024-11-24

## Preferred version (photosynthesis >= 2.1.1)

The package currently supports fitting light respiration according to the slope-intercept regression extension of the Laisk method (Laisk 1977; Walker & Ort 2015), the Kok method (Kok 1956), and the Yin modification of the Kok method (Yin et al. 2009, 2011).

### Estimate light respiration using least-squares (ls)

#### Walker & Ort (2015) model

    library(broom)
    library(dplyr)
    library(photosynthesis)

    acq_data = system.file("extdata", "A_Ci_Q_data_1.csv", package = "photosynthesis") |>
      read.csv()

    fit = fit_photosynthesis(
      .data = acq_data,
      .photo_fun = "r_light",
      .model = "walker_ort_2015",
      .vars = list(.A = A, .Q = Qin, .C = Ci),
      C_upper = 300,
      # Irradiance levels used in experiment
      Q_levels =  c(1500, 750, 375, 125, 100, 75, 50, 25),
    )

    # The 'fit' object inherits class 'lm' and many methods can be used

    ## Model summary:
    summary(fit)

    ##
    ## Call:
    ## lm(formula = `(Intercept)` ~ gamma_star, data = .)
    ##
    ## Residuals:
    ##       Min        1Q    Median        3Q       Max
    ## -0.218920 -0.136553  0.007685  0.117016  0.249953
    ##
    ## Coefficients:
    ##             Estimate Std. Error t value Pr(>|t|)
    ## (Intercept)  -0.1949     0.1002  -1.945   0.0998 .
    ## gamma_star   44.3113     3.2518  13.627  9.7e-06 ***
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ##
    ## Residual standard error: 0.1738 on 6 degrees of freedom
    ## Multiple R-squared:  0.9687, Adjusted R-squared:  0.9635
    ## F-statistic: 185.7 on 1 and 6 DF,  p-value: 9.698e-06

    ## Estimated parameters:
    coef(fit)

    ## (Intercept)  gamma_star
    ##    -0.19485    44.31131

    ## 95% confidence intervals:
    ## n.b. these confidence intervals are not correct because the regression is fit
    ## sequentially. It ignores the underlying data and uncertainty in estimates of
    ## slopes and intercepts with each A-C curve. Use '.method = "brms"' to properly
    ## calculate uncertainty.
    confint(fit)

    ##                  2.5 %      97.5 %
    ## (Intercept) -0.4400221  0.05032205
    ## gamma_star  36.3544139 52.26820542

    ## Tidy summary table using 'broom::tidy()'
    tidy(fit, conf.int = TRUE, conf.level = 0.95)

    ## # A tibble: 2 × 7
    ##   term        estimate std.error statistic    p.value conf.low conf.high
    ##   <chr>          <dbl>     <dbl>     <dbl>      <dbl>    <dbl>     <dbl>
    ## 1 (Intercept)   -0.195     0.100     -1.94 0.0998       -0.440    0.0503
    ## 2 gamma_star    44.3       3.25      13.6  0.00000970   36.4     52.3

    ## Calculate residual sum-of-squares
    sum(resid(fit)^2)

    ## [1] 0.1812764

#### Yin et al. (2011) model

    library(broom)
    library(dplyr)
    library(photosynthesis)

    acq_data = system.file("extdata", "A_Ci_Q_data_1.csv", package = "photosynthesis") |>
      read.csv()

    fit = fit_photosynthesis(
      .data = acq_data,
      .photo_fun = "r_light",
      .model = "yin_etal_2011",
      .vars = list(.A = A, .phiPSII = PhiPS2, .Q = Qin),
      Q_lower = 20,
      Q_upper = 250
    )

    # The 'fit' object inherits class 'lm' and many methods can be used

    ## Model summary:
    summary(fit)

    ##
    ## Call:
    ## lm(formula = .A ~ x_var, data = .)
    ##
    ## Residuals:
    ##     Min      1Q  Median      3Q     Max
    ## -3.8587 -0.4493  0.2041  0.7420  2.2232
    ##
    ## Coefficients:
    ##             Estimate Std. Error t value Pr(>|t|)
    ## (Intercept) -0.77214    0.39419  -1.959   0.0549 .
    ## x_var        0.22661    0.02739   8.274 2.13e-11 ***
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ##
    ## Residual standard error: 1.253 on 58 degrees of freedom
    ## Multiple R-squared:  0.5414, Adjusted R-squared:  0.5335
    ## F-statistic: 68.46 on 1 and 58 DF,  p-value: 2.129e-11

    ## Estimated parameters:
    coef(fit)

    ## (Intercept)       x_var
    ##  -0.7721399   0.2266078

    ## 95% confidence intervals:
    confint(fit)

    ##                  2.5 %     97.5 %
    ## (Intercept) -1.5611923 0.01691245
    ## x_var        0.1717862 0.28142935

    ## Tidy summary table using 'broom::tidy()'
    tidy(fit, conf.int = TRUE, conf.level = 0.95)

    ## # A tibble: 2 × 7
    ##   term        estimate std.error statistic  p.value conf.low conf.high
    ##   <chr>          <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
    ## 1 (Intercept)   -0.772    0.394      -1.96 5.49e- 2   -1.56     0.0169
    ## 2 x_var          0.227    0.0274      8.27 2.13e-11    0.172    0.281

    ## Calculate residual sum-of-squares
    sum(resid(fit)^2)

    ## [1] 91.04856

#### Kok (1956) model

    library(broom)
    library(dplyr)
    library(photosynthesis)

    acq_data = system.file("extdata", "A_Ci_Q_data_1.csv", package = "photosynthesis") |>
      read.csv()

    fit = fit_photosynthesis(
      .data = acq_data,
      .photo_fun = "r_light",
      .model = "kok_1956",
      .vars = list(.A = A, .Q = Qin),
      Q_lower = 20,
      Q_upper = 150
    )

    # The 'fit' object inherits class 'lm' and many methods can be used

    ## Model summary:
    summary(fit)

    ##
    ## Call:
    ## lm(formula = .A ~ .Q, data = .)
    ##
    ## Residuals:
    ##     Min      1Q  Median      3Q     Max
    ## -4.1759 -0.4492  0.2077  0.6613  2.3544
    ##
    ## Coefficients:
    ##              Estimate Std. Error t value Pr(>|t|)
    ## (Intercept) -0.523701   0.396419  -1.321    0.192
    ## .Q           0.036347   0.004781   7.602 2.85e-10 ***
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ##
    ## Residual standard error: 1.309 on 58 degrees of freedom
    ## Multiple R-squared:  0.4991, Adjusted R-squared:  0.4905
    ## F-statistic: 57.79 on 1 and 58 DF,  p-value: 2.851e-10

    ## Estimated parameters:
    coef(fit)

    ## (Intercept)          .Q
    ## -0.52370105  0.03634651

    ## 95% confidence intervals:
    confint(fit)

    ##                  2.5 %     97.5 %
    ## (Intercept) -1.3172199 0.26981784
    ## .Q           0.0267761 0.04591691

    ## Tidy summary table using 'broom::tidy()'
    tidy(fit, conf.int = TRUE, conf.level = 0.95)

    ## # A tibble: 2 × 7
    ##   term        estimate std.error statistic  p.value conf.low conf.high
    ##   <chr>          <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
    ## 1 (Intercept)  -0.524    0.396       -1.32 1.92e- 1  -1.32      0.270
    ## 2 .Q            0.0363   0.00478      7.60 2.85e-10   0.0268    0.0459

    ## Calculate residual sum-of-squares
    sum(resid(fit)^2)

    ## [1] 99.4385

### Bayesian estimate of light respiration with brms and Stan

We added an option to estimate light respiration (\(R_\mathrm{d}\)) and the CO\(_2\) compensation point (\(C_\mathrm{i}^*\) or \(\Gamma^*\)) using the amazing brms package which fits models in Stan. This method has the advantage in that it properly estimates confidence intervals, but requires some post-processing. The example below shows how to do that.

#### Walker & Ort (2015) model

The code below is not run, but copy-and-paste into your console to see output.

    library(dplyr)
    library(photosynthesis)
    library(tidyr)

    acq_data = system.file("extdata", "A_Ci_Q_data_1.csv", package = "photosynthesis") |>
      read.csv()

    fit = fit_photosynthesis(
      .data = acq_data,
      .photo_fun = "r_light",
      .model = "walker_ort_2015",
      .method = "brms",
      .vars = list(.A = A, .Q = Qin, .C = Ci),
      C_upper = 300,
      # Irradiance levels used in experiment
      Q_levels =  c(1500, 750, 375, 125, 100, 75, 50, 25),
      brm_options = list(chains = 1, seed = 20221118)
    )

    # The 'fit' object inherits class 'brmsfit' and many methods can be used

    # The following code will extract parameter estimates and 95% CIs from the
    # posterior distribution
    ests = fit |>
      as.data.frame() |>
      dplyr::select(
        r_xy = `cor_.Q_level__Intercept__.C`,
        sd_x = `sd_.Q_level__Intercept`,
        sd_y = `sd_.Q_level__.C`,
        mu_x = b_Intercept,
        mu_y = b_.C
      ) |>
      dplyr::mutate(
        gamma_star = -r_xy * sd_x / sd_y,
        Rd = mu_x + gamma_star * mu_y
      ) |>
      dplyr::summarise(dplyr::across(gamma_star:Rd, list(
        estimate = median,
        lower = ~quantile(., probs = 0.25),
        upper = ~quantile(., probs = 0.975)
      ))) |>
      pivot_longer(
        everything(),
        names_to = c("parameter", ".value"),
        names_pattern = "(gamma_star|Rd)_(estimate|lower|upper)"
      )

#### Yin et al. (2011) model

The code below is not run, but copy-and-paste into your console to see output.

    library(photosynthesis)

    acq_data = system.file("extdata", "A_Ci_Q_data_1.csv", package = "photosynthesis") |>
      read.csv()

    fit = fit_photosynthesis(
      .data = acq_data,
      .photo_fun = "r_light",
      .model = "yin_etal_2011",
      .method = "brms",
      .vars = list(.A = A, .phiPSII = PhiPS2, .Q = Qin),
      Q_lower = 20,
      Q_upper = 250,
      brm_options = list(chains = 1)
    )

    # The 'fit' object inherits class 'brmsfit' and many methods can be used
    summary(fit)

#### Kok (1956) model

The code below is not run, but copy-and-paste into your console to see output.

    library(photosynthesis)

    acq_data = system.file("extdata", "A_Ci_Q_data_1.csv", package = "photosynthesis") |>
      read.csv()

    fit = fit_photosynthesis(
      .data = acq_data,
      .photo_fun = "r_light",
      .model = "kok_1956",
      .method = "brms",
      .vars = list(.A = A, .Q = Qin),
      Q_lower = 20,
      Q_upper = 150,
      brm_options = list(chains = 1)
    )

    # The 'fit' object inherits class 'brmsfit' and many methods can be used
    summary(fit)

## Deprecated version (photosynthesis <= 2.1.1)

The functions to estimate light respiration in the original version (`fit_r_light_kok()`, `fit_r_light_yin()`, `fit_r_light_WalkerOrt()`) will no longer be updated and may phase it out of future releases. Use one of:

- `fit_photosynthesisi(..., .photo_fun = 'r_light', .model = 'kok_1956')`
- `fit_photosynthesisi(..., .photo_fun = 'r_light', .model = 'walker_ort_1956')`
- `fit_photosynthesisi(..., .photo_fun = 'r_light', .model = 'yin_etal_2011')`

    library(dplyr)
    library(photosynthesis)

    round_to_nearest = function(x, values) {
      sapply(x, function(y, values) {
        values[which.min(abs(y - values))]
      }, values = values)
    }

    # Read in your data
    dat = system.file("extdata", "A_Ci_Q_data_1.csv", package = "photosynthesis") |>
      read.csv() |>
      mutate(group = photosynthesis:::round_to_nearest(CO2_s, values = c(50, 100, 200, 300, 410, 600, 820, 1200, 1600))) |>
      rename(A_net = A, PPFD = Qin, phi_PSII = PhiPS2, C_i = Ci)

    # Fit light respiration with Yin method
    r_light = fit_r_light_yin(data = dat, PPFD_lower = 20, PPFD_upper = 250)

    # Fit light respiration with Kok method
    r_light = fit_r_light_kok(data = dat, PPFD_lower = 20, PPFD_upper = 150)

    # Fit light respiration across groups with Yin method
    r_lights = fit_many(
      data = dat,
      funct = fit_r_light_yin,
      group = "group",
      PPFD_lower = 20, PPFD_upper = 250,
      progress = FALSE
    )

    #Fit the Walker-Ort method for GammaStar and light respiration
    walker_ort = fit_r_light_WalkerOrt(dat)

    # View model output
    summary(walker_ort[[1]])

    ##
    ## Call:
    ## lm(formula = Intercept ~ Slope, data = coefs)
    ##
    ## Residuals:
    ##      Min       1Q   Median       3Q      Max
    ## -0.36114 -0.10958 -0.05553  0.08166  0.62016
    ##
    ## Coefficients:
    ##             Estimate Std. Error t value Pr(>|t|)
    ## (Intercept)  -0.1526     0.1176  -1.297    0.224
    ## Slope        -4.6004     0.4069 -11.307  5.1e-07 ***
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ##
    ## Residual standard error: 0.2513 on 10 degrees of freedom
    ## Multiple R-squared:  0.9275, Adjusted R-squared:  0.9202
    ## F-statistic: 127.8 on 1 and 10 DF,  p-value: 5.103e-07

    # View graph
    # walker_ort[[2]]

    # View coefficients
    walker_ort[[3]]

    ##       GammaStar   r_light
    ## Slope  46.00427 -0.152643

# References

Kok B. 1956. On the inhibition of photosynthesis by intense light. Biochimica et Biophysica Acta 21: 234–244

Laisk A. 1977. Kinetics of photosynthesis and photorespiration in C3 plants. Nauka, Moscow.

Walker BJ, Ort DR. 2015. Improved method for measuring the apparent CO2 photocompensation point resolves the impact of multiple internal conductances to CO2 to net gas exchange. Plant, Cell & Environment 38:2462- 2474

Yin X, Struik PC, Romero P, Harbinson J, Evers JB, van der Putten PEL, Vos J. 2009. Using combined measurements of gas exchange and chlorophyll fluorescence to estimate parameters of a biochemical C3 photosynthesis model: a critical appraisal and a new integrated approach applied to leaves in a wheat (Triticum aestivum) canopy. Plant, Cell & Environment 32:448-464

Yin X, Sun Z, Struik PC, Gu J. 2011. Evaluating a new method to estimate the rate of leaf respiration in the light by analysis of combined gas exchange and chlorophyll fluorescence measurements. Journal of Experimental Botany 62: 3489–3499