Downloading libraries for project:

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.5.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
library(patchwork)
## Warning: package 'patchwork' was built under R version 4.5.2
library(ggplot2)
library(readr)
library(cowplot)
## Warning: package 'cowplot' was built under R version 4.5.2
## 
## Attaching package: 'cowplot'
## 
## The following object is masked from 'package:patchwork':
## 
##     align_plots
## 
## The following object is masked from 'package:lubridate':
## 
##     stamp
library(rlang)
## 
## Attaching package: 'rlang'
## 
## The following objects are masked from 'package:purrr':
## 
##     %@%, flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl,
##     flatten_raw, invoke, splice
library(knitr)
library(kableExtra)
## Warning: package 'kableExtra' was built under R version 4.5.2
## 
## Attaching package: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(nlme)
## 
## Attaching package: 'nlme'
## 
## The following object is masked from 'package:dplyr':
## 
##     collapse
library(purrr)
library(emmeans)
## Warning: package 'emmeans' was built under R version 4.5.2
## Welcome to emmeans.
## Caution: You lose important information if you filter this package's results.
## See '? untidy'

Introduction - Need to do more Literature Review for the Narrative: Revisit when other sections are done: Methods - Re- do diagrams, explain flux theory, and theory needed for stats, locations, data cleaning, and more revisit when Results is done.

Results Section

Functions for creating figures:

# importing data sets
Flux_Data_With_Covars <- read_csv("C:/Users/KAUFMADA/Documents/R_Files/windrow-fluxes-co2-model-improvements/windrow-fluxes-co2-model-improvements/data_clean/Flux_Data_With_Covars.csv")
## Rows: 21090 Columns: 82
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr  (5): Pile, Chamber_Corrected, Turning_Status, Date, DATE_TIME
## dbl (77): Chamber_Elevation, DOY, air_temperature, RH, VPD, Tdew, wind_speed...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Functions to clean data

# function used to remove NA values which LICOR uses -9999 for:

clean_numeric <- function(x,
                          min_val = -Inf,
                          max_val = Inf,
                          bad_values = c(-9999, -9999, 9999, 99999)) {
  x <- as.numeric(x)
  x[x %in% bad_values] <- NA_real_
  x[x < min_val | x > max_val] <- NA_real_
  x
}

# function that calculates the SE of a function: 

se <- function(x) sd(x, na.rm = TRUE) / sqrt(sum(!is.na(x)))

Cleaned_Data <- Flux_Data_With_Covars %>%
  mutate(
    # DOY has decimals, so collapse to whole day
    DOY = floor(DOY),

    pile_temp   = clean_numeric(TS_1.initial_value,  min_val = -10, max_val = 90),
    pile_moist  = clean_numeric(SWC_1.initial_value, min_val = 0.01, max_val = 0.98),
    BulkDensity = clean_numeric(BulkDensity,         min_val = 0.01, max_val = 1)
  )

daily_pile_doy <- Cleaned_Data %>%
  group_by(Pile, DOY) %>%
  summarise(
    n_obs           = sum(!is.na(pile_temp) | !is.na(pile_moist)),
    pile_temp_mean  = mean(pile_temp,  na.rm = TRUE),
    pile_temp_se    = se(pile_temp),
    pile_moist_mean = mean(pile_moist, na.rm = TRUE),
    pile_moist_se   = se(pile_moist),
    .groups = "drop"
  )


# making weekly figures to reduce noise for final paper figures: 

summarise_weekly_mean_se <- function(df,
                                     doy,
                                     value,
                                     group,
                                     min_n = 2) {

  doy   <- enquo(doy)
  value <- enquo(value)
  group <- enquo(group)

  df %>%
    filter(!is.na(!!value), !is.na(!!doy), !is.na(!!group)) %>%
    mutate(
      week = floor((!!doy - 1) / 7) + 1
    ) %>%
    group_by(!!group, week) %>%
    summarise(
      DOY  = mean(!!doy, na.rm = TRUE),   # x-position for plotting
      mean = mean(!!value, na.rm = TRUE),
      sd   = sd(!!value, na.rm = TRUE),
      n    = sum(!is.na(!!value)),
      se   = sd / sqrt(n),
      .groups = "drop"
    ) %>%
    filter(n >= min_n)
}

weekly_moist <- summarise_weekly_mean_se(
  df    = Cleaned_Data,
  doy   = DOY,
  value = pile_moist,
  group = Pile,
  min_n = 2
)
weekly_temp <- summarise_weekly_mean_se(
  df    = Cleaned_Data,
  doy   = DOY,
  value = pile_temp,
  group = Pile,
  min_n = 2
)

Plotting Moisture and Temperature for the experimental cycle.

plot_points_err <- function(df,
                            x, y_mean, y_se,
                            group,
                            cols = NULL,
                            shapes = NULL,
                            point_size = 2.0,
                            point_alpha = 0.85,
                            err_alpha = 0.6,
                            err_lwd = 0.4,
                            err_width = 0,
                            base_size = 12,
                            legend_position = "top",
                            drop_na = TRUE,
                            x_lab = NULL,
                            y_lab = NULL,
                            legend_title = NULL,
                            vline = NULL,
                            vlines_red = NULL,
                            vlines_black = NULL,
                            vline_lwd = 0.4,
                            vline_alpha = 0.8,
                            vline_linetype = "dashed",
                            vline_red_linetype   = "solid",
                            vline_black_linetype = "dashed",
                            vline_red_colour     = "#9b1c31",
                            vline_black_colour   = "#000000"){

  # capture column names
  x      <- enquo(x)
  y_mean <- enquo(y_mean)
  y_se   <- enquo(y_se)
  group  <- enquo(group)

  # optionally drop NA rows for required columns
  if (drop_na) {
    df <- df[complete.cases(
      df[, c(as_name(x), as_name(y_mean), as_name(y_se), as_name(group))]
    ), ]
  }

  p <- ggplot(df, aes(x = !!x, y = !!y_mean)) +

    # error bars first (so points draw on top)
    geom_errorbar(
      aes(ymin = (!!y_mean) - (!!y_se),
          ymax = (!!y_mean) + (!!y_se),
          colour = !!group),
      width = err_width,
      alpha = err_alpha,
      linewidth = err_lwd
    ) 
  
  if (!is.null(vlines_red)) {
  p <- p + geom_vline(
    xintercept = vlines_red,
    colour     = vline_red_colour,
    linetype   = vline_red_linetype,
    linewidth  = vline_lwd,
    alpha      = vline_alpha
  )
}

if (!is.null(vlines_black)) {
  p <- p + geom_vline(
    xintercept = vlines_black,
    colour     = vline_black_colour,
    linetype   = vline_black_linetype,
    linewidth  = vline_lwd,
    alpha      = vline_alpha
  )
  }
  p <- p +

    # points (no lines)
    geom_point(
      aes(colour = !!group, shape = !!group),
      size = point_size,
      alpha = point_alpha
    ) +

    theme_bw(base_size = base_size) +
    theme(
      legend.position = legend_position,
      panel.grid.minor = element_blank()
    )

  # manual scales if provided (keeps consistent styling across plots)
  if (!is.null(cols)) {
    p <- p + scale_colour_manual(values = cols)
  }
  if (!is.null(shapes)) {
    p <- p + scale_shape_manual(values = shapes)
  }
# ── LABELS SECTION ─────────────────────────────
  if (!is.null(x_lab) || !is.null(y_lab) || !is.null(legend_title)) {
    p <- p + labs(
      x = x_lab,
      y = y_lab,
      colour = legend_title,
      shape  = legend_title
    )
  }
  return(p)
}

Pile Temperature Plot using Daily Average Values Pile Moisture Using Daily Average Values

#turning data to add to plots if needed
turns_both_red <- c(159, 186, 208, 234, 262, 290)
turns_control_black <- c(173, 191, 222, 249, 276)
turns_both_red_week <- c(159, 186, 208, 234, 262, 290)/7
turns_control_black_week <- c(173, 191, 222, 249, 276)/7


pile_cols   <- c(C = "#000000", E = "#9b1c31")
pile_shapes <- c(C = 16, E = 17)

fig_moist_doy <- plot_points_err(
  daily_pile_doy,
  x = DOY,
  y_mean = pile_moist_mean,
  y_se = pile_moist_se,
  group = Pile,
  cols = pile_cols,
  shapes = pile_shapes,
  point_size = 1.8,
  x_lab = "Day of year",
  y_lab = expression("Volumetric water content (m"^3*" m"^-3*")"),
  legend_title = "Pile",

  vlines_red   = turns_both_red,
  vlines_black = turns_control_black,

  vline_red_linetype   = "solid",
  vline_black_linetype = "dashed"
)

fig_moist_doy

fig_moist_weekly <- plot_points_err(
  weekly_moist,
  x = week,
  y_mean = mean,
  y_se =  se,
  group = Pile,
  cols = pile_cols,
  shapes = pile_shapes,
  point_size = 1.8,
  x_lab = "Week of year",
  y_lab = expression("Volumetric water content (m"^3*" m"^-3*")"),
  legend_title = "Pile",
  
  vlines_red   = turns_both_red_week,
  vlines_black = turns_control_black_week,

  vline_red_linetype   = "solid",
  vline_black_linetype = "dashed"
)

fig_moist_weekly

fig_temp_doy <- plot_points_err(
  daily_pile_doy,
  x = DOY,
  y_mean =pile_temp_mean,
  y_se =  pile_temp_se,
  group = Pile,
  cols = pile_cols,
  shapes = pile_shapes,
  point_size = 1.8,
  x_lab = "Day of year",
  y_lab = expression("Pile Temperature Degrees Celsius"),
  legend_title = "Pile",
  
  vlines_red   = turns_both_red,
  vlines_black = turns_control_black,

  vline_red_linetype   = "solid",
  vline_black_linetype = "dashed"
)

fig_temp_doy

fig_temp_weekly <- plot_points_err(
  weekly_temp,
  x = week,
  y_mean =mean,
  y_se =  se,
  group = Pile,
  cols = pile_cols,
  shapes = pile_shapes,
  point_size = 1.8,
  x_lab = "Week of year",
  y_lab = expression("Pile Temperature Degrees Celsius"),
  legend_title = "Pile",
  
  vlines_red   = turns_both_red_week,
  vlines_black = turns_control_black_week,

  vline_red_linetype   = "solid",
  vline_black_linetype = "dashed"
)

fig_temp_weekly

Creating Tables with Pile Variables:

pile_vars_temp <- Cleaned_Data %>%
  filter(!is.na(pile_temp)) %>%
  group_by(Pile) %>%
  summarise(
    n_obs = n(),
    mean_temp = mean(pile_temp),
    sd_temp   = sd(pile_temp),
    se_temp   = sd_temp/sqrt(n_obs)
  )

pile_vars_water <- Cleaned_Data %>%
  filter(!is.na(pile_moist)) %>%
  group_by(Pile) %>%
  summarise(
    n_obs = n(),
    mean_moist = mean(pile_moist),
    sd_moist   = sd(pile_moist),
    se_moist   = sd_moist/sqrt(n_obs)
  )

Displaying Tables of Summary Stats for Moisture and Temperature

Summary statistics for volumetric water content by pile.
Pile n_obs mean_moist sd_moist se_moist
C 9177 0.490 0.327 0.003
E 9403 0.787 0.244 0.003
Summary statistics for Temperature by pile.
Pile n_obs mean_temp sd_temp se_temp
C 9192 54.069 8.992 0.094
E 9420 51.778 12.812 0.132

Examining Measured Fluxes by Pile

library(readxl)
co2_raw <-  read.csv("C:/Users/KAUFMADA/Documents/R_Files/windrow-fluxes-co2-model-improvements/windrow-fluxes-co2-model-improvements/data_clean/CO2_R_2_filtered.csv")

ch4_raw <-  read.csv("C:/Users/KAUFMADA/Documents/R_Files/windrow-fluxes-co2-model-improvements/windrow-fluxes-co2-model-improvements/data_clean/CH4_R_2_filtered.csv")

n2o_raw <- read.csv("C:/Users/KAUFMADA/Documents/R_Files/windrow-fluxes-co2-model-improvements/windrow-fluxes-co2-model-improvements/data_clean/N2O_R_2_filtered.csv")

Making Chart with Average values by Pile and SD,SE

pile_vars_co2 <- co2_raw %>%
  filter(!is.na(FCO2_DRY.LIN)) %>%
  group_by(Pile) %>%
  summarise(
    n_obs = n(),
    mean_co2 = mean(FCO2_DRY.LIN),
    sd_co2 = sd(FCO2_DRY.LIN),
    se_co2   = sd_co2/sqrt(n_obs)
  )

pile_vars_ch4 <- ch4_raw %>%
  filter(!is.na(FCH4_DRY.LIN)) %>%
  group_by(Pile) %>%
  summarise(
    n_obs = n(),
    mean_ch4 = mean(FCH4_DRY.LIN),
    sd_ch4   = sd(FCH4_DRY.LIN),
    se_ch4   = sd_ch4/sqrt(n_obs)
  )

pile_vars_n2o <- n2o_raw %>%
  filter(!is.na(FN2O_DRY.LIN)) %>%
  group_by(Pile) %>%
  summarise(
    n_obs = n(),
    mean_n2o = mean(FN2O_DRY.LIN),
    sd_n2o   = sd(FN2O_DRY.LIN),
    se_n2o   = sd_n2o/sqrt(n_obs)
  )

Displaying Average Values for Each Trace Gas:

kable(
  pile_vars_co2,
  digits = 3,
  caption = "Summary statistics for co2 flux by pile."
) %>%
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c("bordered", "striped")
  )
Summary statistics for co2 flux by pile.
Pile n_obs mean_co2 sd_co2 se_co2
C 5064 190.076 202.288 2.843
E 4215 191.757 193.987 2.988
kable(
  pile_vars_ch4,
  digits = 3,
  caption = "Summary statistics for ch4 flux by pile."
) %>%
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c("bordered", "striped")
  )
Summary statistics for ch4 flux by pile.
Pile n_obs mean_ch4 sd_ch4 se_ch4
C 5355 7055.016 11741.80 160.456
E 4937 6594.771 10324.23 146.935
kable(
  pile_vars_n2o,
  digits = 3,
  caption = "Summary statistics for n2o flux by pile."
) %>%
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c("bordered", "striped")
  )
Summary statistics for n2o flux by pile.
Pile n_obs mean_n2o sd_n2o se_n2o
C 6906 157.089 295.036 3.550
E 6911 98.481 245.689 2.955

Creating Daily and Weekly Plots for all Fluxes

# N2O
daily_n2o <- n2o_raw %>%
  mutate(DOY = floor(DOY)) %>%        # collapse fractional DOY
  group_by(Pile, DOY) %>%
  summarise(
    n_obs       = sum(!is.na(FN2O_DRY.LIN)),
    n2o_mean    = mean(FN2O_DRY.LIN, na.rm = TRUE),
    n2o_se      = se(FN2O_DRY.LIN),
    .groups     = "drop"
  ) %>%
  filter(n_obs >= 3)                  # tweak threshold if needed

# CO2
daily_co2 <- co2_raw %>%
  mutate(DOY = floor(DOY)) %>%
  group_by(Pile, DOY) %>%
  summarise(
    n_obs       = sum(!is.na(FCO2_DRY.LIN)),
    co2_mean    = mean(FCO2_DRY.LIN, na.rm = TRUE),
    co2_se      = se(FCO2_DRY.LIN),
    .groups     = "drop"
  ) %>%
  filter(n_obs >= 3)

# CH4
daily_ch4 <- ch4_raw %>%
  mutate(DOY = floor(DOY)) %>%
  group_by(Pile, DOY) %>%
  summarise(
    n_obs       = sum(!is.na(FCH4_DRY.LIN)),
    ch4_mean    = mean(FCH4_DRY.LIN, na.rm = TRUE),
    ch4_se      = se(FCH4_DRY.LIN),
    .groups     = "drop"
  ) %>%
  filter(n_obs >= 3)

weekly_n2o <- n2o_raw %>%
  mutate(
    week = floor((DOY - 1) / 7) + 1
  ) %>%
  group_by(Pile, week) %>%
  summarise(
    n_obs    = sum(!is.na(FN2O_DRY.LIN)),
    n2o_mean = mean(FN2O_DRY.LIN, na.rm = TRUE),
    n2o_se   = sd(FN2O_DRY.LIN, na.rm = TRUE) / sqrt(n_obs),
    DOY      = mean(DOY, na.rm = TRUE),   # x-position for plotting (optional but useful)
    .groups  = "drop"
  ) %>%
  filter(n_obs >= 3)

# CO2
weekly_co2 <- co2_raw %>%
  mutate(
    week = floor((DOY - 1) / 7) + 1
  ) %>%
  group_by(Pile, week) %>%
  summarise(
    n_obs    = sum(!is.na(FCO2_DRY.LIN)),
    co2_mean = mean(FCO2_DRY.LIN, na.rm = TRUE),
    co2_se   = sd(FCO2_DRY.LIN, na.rm = TRUE) / sqrt(n_obs),
    DOY      = mean(DOY, na.rm = TRUE),
    .groups  = "drop"
  ) %>%
  filter(n_obs >= 3)

# CH4
weekly_ch4 <- ch4_raw %>%
  mutate(
    week = floor((DOY - 1) / 7) + 1
  ) %>%
  group_by(Pile, week) %>%
  summarise(
    n_obs    = sum(!is.na(FCH4_DRY.LIN)),
    ch4_mean = mean(FCH4_DRY.LIN, na.rm = TRUE),
    ch4_se   = sd(FCH4_DRY.LIN, na.rm = TRUE) / sqrt(n_obs),
    DOY      = mean(DOY, na.rm = TRUE),
    .groups  = "drop"
  ) %>%
  filter(n_obs >= 3)

Plotting Values

fig_co2_daily <- plot_points_err(
  daily_co2,
  x = DOY, y_mean = co2_mean, y_se = co2_se,
  group = Pile,
  cols = pile_cols, shapes = pile_shapes,
  point_size = 1.4,
  vlines_red = turns_both_red,
  vlines_black = turns_control_black,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed"
)

fig_co2_daily

fig_co2_weekly <- plot_points_err(
  weekly_co2,
  x = week, y_mean = co2_mean, y_se = co2_se,
  group = Pile,
  cols = pile_cols, shapes = pile_shapes,
  point_size = 1.4,
  vlines_red = turns_both_red_week,
  vlines_black = turns_control_black_week,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed"
)

fig_co2_weekly

fig_ch4_daily <- plot_points_err(
  daily_ch4,
  x = DOY, y_mean = ch4_mean, y_se = ch4_se,
  group = Pile,
  cols = pile_cols, shapes = pile_shapes,
  point_size = 1.4,
  vlines_red = turns_both_red,
  vlines_black = turns_control_black,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed"
)

fig_ch4_daily

fig_ch4_weekly <- plot_points_err(
  weekly_ch4,
  x = week, y_mean = ch4_mean, y_se = ch4_se,
  group = Pile,
  cols = pile_cols, shapes = pile_shapes,
  point_size = 1.4,
  vlines_red = turns_both_red_week,
  vlines_black = turns_control_black_week,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed"
)

fig_ch4_weekly

fig_n2o_daily <- plot_points_err(
  daily_n2o,
  x = DOY, y_mean = n2o_mean, y_se = n2o_se,
  group = Pile,
  cols = pile_cols, shapes = pile_shapes,
  point_size = 1.4,
  vlines_red = turns_both_red,
  vlines_black = turns_control_black,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed"
)

fig_n2o_daily

fig_n2o_weekly <- plot_points_err(
  weekly_n2o,
  x = week, y_mean = n2o_mean, y_se = n2o_se,
  group = Pile,
  cols = pile_cols, shapes = pile_shapes,
  point_size = 1.4,
  vlines_red = turns_both_red_week,
  vlines_black = turns_control_black_week,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed"
)

fig_n2o_weekly

Looking at A Combination of all three gases using established 100 year conversion, these can easily be changed:

Co2 1 Ch4 28 N2o 265

To combine Trace Gases as cleaning was done on a per measurement basis:

Method 1: Looking at trace gases where all there trace gases where taken at once Method 2: Averaging Per Day and Comparing Daily Average GHG Potential

Method to keep only rows with all three trace gases

Flux_All_Gases_QC <- Flux_Data_With_Covars %>%
  filter(
    # CO2
    FCO2_DRY.LIN > 0,
    FCO2_DRY.LIN_R2 > 0.9,

    # CH4
    FCH4_DRY.LIN > 0,
    FCH4_DRY.LIN_R2 > 0.9,

    # N2O
    FN2O_DRY.LIN > 0,
    FN2O_DRY.LIN_R2 > 0.9
  )


Flux_Data_With_Covars %>%
  summarise(
    total_obs = n(),
    pass_all  = sum(
      FCO2_DRY.LIN > 0 & FCO2_DRY.LIN_R2 > 0.9 &
      FCH4_DRY.LIN > 0 & FCH4_DRY.LIN_R2 > 0.9 &
      FN2O_DRY.LIN > 0 & FN2O_DRY.LIN_R2 > 0.9,
      na.rm = TRUE
    ),
    percent_retained = 100 * pass_all / total_obs
  )
## # A tibble: 1 × 3
##   total_obs pass_all percent_retained
##       <int>    <int>            <dbl>
## 1     21090     5558             26.4
Flux_All_Gases_QC <- Flux_All_Gases_QC %>%
  mutate(
    # keep units explicit
    CO2_umol = FCO2_DRY.LIN,                  # µmol m-2 s-1
    N2O_umol = FN2O_DRY.LIN,                  # µmol m-2 s-1
    CH4_umol = FCH4_DRY.LIN * 0.001,          # nmol -> µmol

    # GWP100 CO2-equivalent (µmol CO2-eq m-2 s-1)
    GHG_CO2e_umol = CO2_umol + 28 * CH4_umol + 265 * N2O_umol
  )

Summary Stats of Co2e by pile when looking at measurements where all three gases passed qa/qc

pile_vars_co2e <- Flux_All_Gases_QC %>%
  filter(!is.na(GHG_CO2e_umol)) %>%
  group_by(Pile) %>%
  summarise(
    n_obs = n(),
    mean_CO2e = mean(GHG_CO2e_umol),
    sd_CO2e   = sd(GHG_CO2e_umol),
    se_CO2e = sd_CO2e/sqrt(n_obs)
  )


kable(
  pile_vars_co2e,
  digits = 3,
  caption = "Summary statistics for co2e flux by pile."
) %>%
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c("bordered", "striped")
  )
Summary statistics for co2e flux by pile.
Pile n_obs mean_CO2e sd_CO2e se_CO2e
C 3269 59570.66 101672.24 1778.259
E 2289 40046.13 96185.23 2010.414

We are going to write the code to plot this but we will plot all three together - as this is only the first method of plotting the values.

# daily co2e
daily_co2e <- Flux_All_Gases_QC %>%
  mutate(DOY = floor(DOY)) %>%
  group_by(Pile, DOY) %>%
  summarise(
    n_obs       = sum(!is.na(GHG_CO2e_umol)),
    co2e_mean    = mean(GHG_CO2e_umol, na.rm = TRUE),
    co2e_se      = sd(GHG_CO2e_umol, na.rm = TRUE)/sqrt(n_obs),
    .groups     = "drop"
  ) %>%
  filter(n_obs >= 3)


# weekly co2e
weekly_co2e <- Flux_All_Gases_QC %>%
  mutate(
    week = floor((DOY - 1) / 7) + 1
  ) %>%
  group_by(Pile, week) %>%
  summarise(
    n_obs    = sum(!is.na(GHG_CO2e_umol)),
    co2e_mean = mean(GHG_CO2e_umol, na.rm = TRUE),
    co2e_se   = sd(GHG_CO2e_umol, na.rm = TRUE) / sqrt(n_obs),
    DOY      = mean(DOY, na.rm = TRUE),
    .groups  = "drop"
  ) %>%
  filter(n_obs >= 3)
# CO2


fig__co2e_daily <- plot_points_err(
  daily_co2e,
  x = DOY, y_mean = co2e_mean, y_se = co2e_se,
  group = Pile,
  cols = pile_cols, shapes = pile_shapes,
  point_size = 1.4,
  vlines_red = turns_both_red,
  vlines_black = turns_control_black,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed"
)

fig__co2e_daily

fig_co2e_weekly <- plot_points_err(
  weekly_co2e,
  x = week, y_mean =co2e_mean, y_se = co2e_se,
  group = Pile,
  cols = pile_cols, shapes = pile_shapes,
  point_size = 1.4,
  vlines_red = turns_both_red_week,
  vlines_black = turns_control_black_week,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed"
)

fig_co2e_weekly

Method 2: Averaging Per Day and Comparing Daily Average GHG Potential

daily_all3 <- daily_co2 %>%
  select(Pile, DOY, n_obs_co2 = n_obs, co2_mean, co2_se) %>%
  inner_join(
    daily_ch4 %>% select(Pile, DOY, n_obs_ch4 = n_obs, ch4_mean, ch4_se),
    by = c("Pile", "DOY")
  ) %>%
  inner_join(
    daily_n2o %>% select(Pile, DOY, n_obs_n2o = n_obs, n2o_mean, n2o_se),
    by = c("Pile", "DOY")
  )

daily_all3 <- daily_all3 %>%
  mutate(
    ch4_mean_umol = ch4_mean * 0.001,
    ch4_se_umol   = ch4_se   * 0.001,

    # CO2-equivalent in µmol CO2-eq m-2 s-1 (using your factors)
    co2e_mean = co2_mean + 28 * ch4_mean_umol + 273 * n2o_mean
  )

# not going to error bars for this as it is messy but for now we assume they are independent so each one is one measurement and not correlated which is not true: 

daily_all3 <- daily_all3 %>%
  mutate(
    co2e_se = sqrt(
      co2_se^2 +
      (28 * ch4_se_umol)^2 +
      (273 * n2o_se)^2
    )
  )

fig_co2e_daily_all3 <- plot_points_err(
  daily_all3,
  x = DOY,
  y_mean = co2e_mean,
  y_se = co2e_se,
  group = Pile,
  cols = pile_cols,
  shapes = pile_shapes,
  point_size = 1.6,
  vlines_red = turns_both_red,
  vlines_black = turns_control_black,
  vline_red_linetype = "solid",
  vline_black_linetype = "dashed",
  x_lab = "Day of year",
  y_lab = expression("CO"[2]*"-eq flux ("*mu*"mol m"^-2*" s"^-1*")"),
  legend_title = "Pile"
)

fig_co2e_daily_all3

This is good, I don’t think I need to do a weekly version. Depending on what we want to do we can revisit.

Now we have looked at the differences in each trace gas and all of them together as well as the baseline differences between the piles (temp, moisture). We did not plot other variables like bulk density but the limited number of samples makes this a little difficult. - Not sure how we should include this, in other parts I have this as just a mean , sd , se.

Variable Unit Value SD SE n Bulk Density C g/cm^3 0.17 3.58 1.03 12 Bulk Density E g/cm^3 0.20 5.17 1.49 12


Basic Model Building:

Numerous models have been employed throughout the process of writting this paper: Here I will present all the models, explain them, and then we can choose the model which best explains the data.

Model 1: Linear Mixed Effects Model:

Here we are looking at the magnitude of fluxes between piles taking into account random effects from chambers and an auto correlation structure.

# log transforming flux data for each flux, grouping chambers together  and arranging by doy
dat_co2 <- co2_raw %>% 
  mutate(log_flux = log(FCO2_DRY.LIN))%>%  
  arrange(Chamber_Corrected, DOY) %>%
  group_by(Chamber_Corrected) %>%
  mutate(t_index = row_number()) %>%
  ungroup()

# applying model 

m_co2 <- lme(
  log_flux ~ Pile,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2,
  method = "REML"
)


# log transforming flux data for each flux, grouping chambers together  and arranging by doy
dat_ch4 <- ch4_raw %>% 
  mutate(log_flux = log(FCH4_DRY.LIN))%>%  
  arrange(Chamber_Corrected, DOY) %>%
  group_by(Chamber_Corrected) %>%
  mutate(t_index = row_number()) %>%
  ungroup()

# applying model 

m_ch4 <- lme(
  log_flux ~ Pile,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ch4,
  method = "REML"
)

# log transforming flux data for each flux, grouping chambers together  and arranging by doy
dat_n2o <- n2o_raw %>% 
  mutate(log_flux = log(FN2O_DRY.LIN))%>%  
  arrange(Chamber_Corrected, DOY) %>%
  group_by(Chamber_Corrected) %>%
  mutate(t_index = row_number()) %>%
  ungroup()

# applying model 

m_n2o <- lme(
  log_flux ~ Pile,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_n2o,
  method = "REML"
)


dat_co2e <- Flux_All_Gases_QC %>% 
  mutate(log_flux = log(GHG_CO2e_umol))%>%  
  arrange(Chamber_Corrected, DOY) %>%
  group_by(Chamber_Corrected) %>%
  mutate(t_index = row_number()) %>%
  ungroup()

# applying model 

m_co2e <- lme(
  log_flux ~ Pile,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2e,
  method = "REML"
)


summary(m_co2)$tTable
##                  Value Std.Error   DF     t-value      p-value
## (Intercept) 4.85541274 0.2502765 9273 19.40019048 3.231981e-82
## PileE       0.01324912 0.3567358    4  0.03713986 9.721531e-01
summary(m_ch4)$tTable
##                  Value Std.Error    DF    t-value      p-value
## (Intercept)  7.2578757 0.6867876 10286 10.5678613 5.704952e-26
## PileE       -0.4425029 1.0106864     4 -0.4378241 6.841178e-01
summary(m_n2o)$tTable
##                  Value Std.Error    DF   t-value      p-value
## (Intercept)  4.0477206 0.2135944 13811 18.950502 4.396067e-79
## PileE       -0.4668237 0.3152567     4 -1.480773 2.127801e-01
summary(m_co2e)$tTable
##                  Value Std.Error   DF    t-value       p-value
## (Intercept)  9.9295460 0.3194308 5552 31.0851212 1.003075e-195
## PileE       -0.3951411 0.4905084    4 -0.8055747  4.656491e-01
extract_pile_effect <- function(model, gas_name) {
  tt <- summary(model)$tTable
  
  beta <- tt["PileE", "Value"]
  se   <- tt["PileE", "Std.Error"]
  pval <- tt["PileE", "p-value"]
  
  data.frame(
    Gas = gas_name,
    Ratio_E_over_C = exp(beta),
    CI_low  = exp(beta - 1.96 * se),
    CI_high = exp(beta + 1.96 * se),
    p_value = pval
  )
}

results_lme <- bind_rows(
  extract_pile_effect(m_co2,  "CO2"),
  extract_pile_effect(m_ch4,  "CH4"),
  extract_pile_effect(m_n2o,  "N2O"),
  extract_pile_effect(m_co2e, "CO2e")
)

results_lme
##    Gas Ratio_E_over_C     CI_low  CI_high   p_value
## 1  CO2      1.0133373 0.50361004 2.038983 0.9721531
## 2  CH4      0.6424265 0.08861552 4.657331 0.6841178
## 3  N2O      0.6269906 0.33799410 1.163089 0.2127801
## 4 CO2e      0.6735850 0.25755097 1.761658 0.4656491
knitr::kable(
  results_lme,
  digits = 3,
  caption = "Pile effect estimates from log-scale linear mixed-effects models."
)
Pile effect estimates from log-scale linear mixed-effects models.
Gas Ratio_E_over_C CI_low CI_high p_value
CO2 1.013 0.504 2.039 0.972
CH4 0.642 0.089 4.657 0.684
N2O 0.627 0.338 1.163 0.213
CO2e 0.674 0.258 1.762 0.466

adding chamber elevation to the model as this impact flux magnitude and does not have to do with mechanism or treatment:

dat_co2 <- dat_co2 %>%
  group_by(Chamber_Corrected) %>%
  mutate(
    Chamber_Elevation_filled = ifelse(
      is.na(Chamber_Elevation),
      median(Chamber_Elevation, na.rm = TRUE),  # or mean(...)
      Chamber_Elevation
    )
  ) %>%
  ungroup()

dat_co2 <- dat_co2 %>%
  mutate(Chamber_Elevation_sc = as.numeric(scale(Chamber_Elevation_filled)))

m_co2_elev <- lme(
  log_flux ~ Pile + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2,
  method = "REML",
  na.action = na.omit
)

dat_ch4 <- dat_ch4 %>%
  group_by(Chamber_Corrected) %>%
  mutate(
    Chamber_Elevation_filled = ifelse(
      is.na(Chamber_Elevation),
      median(Chamber_Elevation, na.rm = TRUE),  # or mean(...)
      Chamber_Elevation
    )
  ) %>%
  ungroup()

dat_ch4 <- dat_ch4 %>%
  mutate(Chamber_Elevation_sc = as.numeric(scale(Chamber_Elevation_filled)))

m_ch4_elev <- lme(
  log_flux ~ Pile + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ch4,
  method = "REML",
  na.action = na.omit
)

dat_n2o <- dat_n2o %>%
  group_by(Chamber_Corrected) %>%
  mutate(
    Chamber_Elevation_filled = ifelse(
      is.na(Chamber_Elevation),
      median(Chamber_Elevation, na.rm = TRUE),  # or mean(...)
      Chamber_Elevation
    )
  ) %>%
  ungroup()

dat_n2o <- dat_n2o %>%
  mutate(Chamber_Elevation_sc = as.numeric(scale(Chamber_Elevation_filled)))

m_n2o_elev <- lme(
  log_flux ~ Pile + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_n2o,
  method = "REML",
  na.action = na.omit
)

dat_co2e <- dat_co2e %>%
  group_by(Chamber_Corrected) %>%
  mutate(
    Chamber_Elevation_filled = ifelse(
      is.na(Chamber_Elevation),
      median(Chamber_Elevation, na.rm = TRUE),  # or mean(...)
      Chamber_Elevation
    )
  ) %>%
  ungroup()

dat_co2e <- dat_co2e %>%
  mutate(Chamber_Elevation_sc = as.numeric(scale(Chamber_Elevation_filled)))

m_co2e_elev <- lme(
  log_flux ~ Pile + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2e,
  method = "REML",
  na.action = na.omit
)


results_lme_elv <- bind_rows(
  extract_pile_effect(m_co2e_elev,  "CO2"),
  extract_pile_effect(m_ch4_elev,  "CH4"),
  extract_pile_effect(m_n2o_elev,  "N2O"),
  extract_pile_effect(m_co2e_elev, "CO2e")
)

results_lme_elv
##    Gas Ratio_E_over_C     CI_low  CI_high   p_value
## 1  CO2      0.6089703 0.20520084 1.807229 0.4220016
## 2  CH4      0.6297099 0.06327159 6.267183 0.7133416
## 3  N2O      0.6643068 0.32098921 1.374824 0.3322370
## 4 CO2e      0.6089703 0.20520084 1.807229 0.4220016
dat_co2_int <- dat_co2 %>%
  mutate(Pile = factor(Pile, levels = c("C", "E"))) %>%
  # remove invalid temperature values
  mutate(
    TS_1.initial_value = ifelse(
      TS_1.initial_value <= 0,
      NA,
      TS_1.initial_value
    )
  ) %>%
  
  # fill missing values with the mean
  mutate(
    TS_1_filled = ifelse(
      is.na(TS_1.initial_value),
      mean(TS_1.initial_value, na.rm = TRUE),
      TS_1.initial_value
    )
  ) %>%
  
  # scale AFTER filling
  mutate(
    pile_temp_sc = as.numeric(scale(TS_1_filled))
  )

m_co2_temp_int <- lme(
  log_flux ~ Pile * pile_temp_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2_int,
  method = "REML",
  na.action = na.omit
)

summary(m_co2_temp_int)$tTable
##                           Value  Std.Error   DF     t-value      p-value
## (Intercept)          4.86755246 0.26679958 9270 18.24422825 4.331843e-73
## PileE                0.02531670 0.37894125    4  0.06680903 9.499398e-01
## pile_temp_sc         0.22594883 0.02694158 9270  8.38662226 5.734823e-17
## Chamber_Elevation_sc 0.09157048 0.02282059 9270  4.01262472 6.051956e-05
## PileE:pile_temp_sc   0.14611501 0.04231313 9270  3.45318321 5.564882e-04
m_base_ml <- update(m_co2_elev, method = "ML")
m_int_ml <- update(m_co2_temp_int, method = "ML")
anova(m_base_ml  , m_int_ml)
##           Model df      AIC      BIC    logLik   Test  L.Ratio p-value
## m_base_ml     1  7 5631.331 5681.279 -2808.665                        
## m_int_ml      2  9 5465.668 5529.888 -2723.834 1 vs 2 169.6624  <.0001
# temperature range (scaled)
temp_seq <- seq(
  min(dat_co2_int$pile_temp_sc),
  max(dat_co2_int$pile_temp_sc),
  length.out = 100
)

newdat <- expand.grid(
  pile_temp_sc = temp_seq,
  Pile = levels(dat_co2_int$Pile)
)

# hold elevation at its mean (0 after scaling)
newdat$Chamber_Elevation_sc <- 0

# predict population-level (fixed effects only)
newdat$pred_log <- predict(m_co2_temp_int, newdata = newdat, level = 0)
newdat$pred_flux <- exp(newdat$pred_log)

ggplot(dat_co2_int,
       aes(x = pile_temp_sc,
           y = exp(log_flux),
           colour = Pile)) +

  geom_point(alpha = 0.15, size = 1) +

  geom_line(data = newdat,
            aes(x = pile_temp_sc,
                y = pred_flux,
                colour = Pile),
            linewidth = 1.2) +

  theme_bw(base_size = 12) +
  theme(panel.grid.minor = element_blank()) +

  labs(
    x = "Pile temperature (scaled)",
    y = expression(CO[2]~flux~(back~transformed)),
    colour = "Pile"
  )

This was an interesting plot, lets do it for the other trace gases and then move forward. At lower temps the piles converge but there is more flux per increase in temp for the E pile

dat_ch4_int <- dat_ch4 %>%
  mutate(Pile = factor(Pile, levels = c("C", "E"))) %>%
  # remove invalid temperature values
  mutate(
    TS_1.initial_value = ifelse(
      TS_1.initial_value <= 0,
      NA,
      TS_1.initial_value
    )
  ) %>%
  
  # fill missing values with the mean
  mutate(
    TS_1_filled = ifelse(
      is.na(TS_1.initial_value),
      mean(TS_1.initial_value, na.rm = TRUE),
      TS_1.initial_value
    )
  ) %>%
  
  # scale AFTER filling
  mutate(
    pile_temp_sc = as.numeric(scale(TS_1_filled))
  )

m_ch4_temp_int <- lme(
  log_flux ~ Pile * pile_temp_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ch4_int,
  method = "REML",
  na.action = na.omit
)

summary(m_ch4_temp_int)$tTable
##                            Value  Std.Error    DF    t-value      p-value
## (Intercept)           7.23817774 0.78662856 10283  9.2015191 4.214351e-20
## PileE                -0.38927899 1.14064600     4 -0.3412794 7.500671e-01
## pile_temp_sc          0.03203206 0.03755505 10283  0.8529361 3.937146e-01
## Chamber_Elevation_sc  0.26676320 0.03865930 10283  6.9003628 5.492551e-12
## PileE:pile_temp_sc    0.54664546 0.06184593 10283  8.8388265 1.125410e-18
m_base_ml <- update(m_ch4_elev, method = "ML")
m_int_ml <- update(m_ch4_temp_int, method = "ML")
anova(m_base_ml  , m_int_ml)
##           Model df      AIC      BIC    logLik   Test  L.Ratio p-value
## m_base_ml     1  7 11026.02 11076.70 -5506.012                        
## m_int_ml      2  9 10894.13 10959.28 -5438.066 1 vs 2 135.8926  <.0001
# temperature range (scaled)
temp_seq <- seq(
  min(dat_ch4_int$pile_temp_sc),
  max(dat_ch4_int$pile_temp_sc),
  length.out = 100
)

newdat <- expand.grid(
  pile_temp_sc = temp_seq,
  Pile = levels(dat_ch4_int$Pile)
)

# hold elevation at its mean (0 after scaling)
newdat$Chamber_Elevation_sc <- 0

# predict population-level (fixed effects only)
newdat$pred_log <- predict(m_ch4_temp_int, newdata = newdat, level = 0)
newdat$pred_flux <- exp(newdat$pred_log)

ggplot(dat_ch4_int,
       aes(x = pile_temp_sc,
           y = exp(log_flux),
           colour = Pile)) +

  geom_point(alpha = 0.15, size = 1) +

  geom_line(data = newdat,
            aes(x = pile_temp_sc,
                y = pred_flux,
                colour = Pile),
            linewidth = 1.2) +

  theme_bw(base_size = 12) +
  theme(panel.grid.minor = element_blank()) +

  labs(
    x = "Pile temperature (scaled)",
    y = expression(Ch[4]~flux~(back~transformed)),
    colour = "Pile"
  )

For both ch4 and co2 the E pile has a stronger response to an increase in temp.

dat_n2o_int <- dat_n2o %>%
  mutate(Pile = factor(Pile, levels = c("C", "E"))) %>%
  # remove invalid temperature values
  mutate(
    TS_1.initial_value = ifelse(
      TS_1.initial_value <= 0,
      NA,
      TS_1.initial_value
    )
  ) %>%
  
  # fill missing values with the mean
  mutate(
    TS_1_filled = ifelse(
      is.na(TS_1.initial_value),
      mean(TS_1.initial_value, na.rm = TRUE),
      TS_1.initial_value
    )
  ) %>%
  
  # scale AFTER filling
  mutate(
    pile_temp_sc = as.numeric(scale(TS_1_filled))
  )

m_n2o_temp_int <- lme(
  log_flux ~ Pile * pile_temp_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_n2o_int,
  method = "REML",
  na.action = na.omit
)

summary(m_n2o_temp_int)$tTable
##                            Value  Std.Error    DF    t-value      p-value
## (Intercept)           4.03288450 0.25385310 13808 15.8866862 2.471057e-56
## PileE                -0.41692329 0.37023339     4 -1.1261094 3.231086e-01
## pile_temp_sc         -0.01431110 0.03251010 13808 -0.4402047 6.597958e-01
## Chamber_Elevation_sc  0.25640388 0.03787607 13808  6.7695482 1.344010e-11
## PileE:pile_temp_sc   -0.02935484 0.05247626 13808 -0.5593929 5.759027e-01
m_base_ml <- update(m_n2o_elev, method = "ML")
m_int_ml <- update(m_n2o_temp_int, method = "ML")
anova(m_base_ml  , m_int_ml)
##           Model df      AIC      BIC    logLik   Test  L.Ratio p-value
## m_base_ml     1  7 10630.19 10682.92 -5308.093                        
## m_int_ml      2  9 10632.88 10700.68 -5307.438 1 vs 2 1.310477  0.5193
# temperature range (scaled)
temp_seq <- seq(
  min(dat_n2o_int$pile_temp_sc),
  max(dat_n2o_int$pile_temp_sc),
  length.out = 100
)

newdat <- expand.grid(
  pile_temp_sc = temp_seq,
  Pile = levels(dat_n2o_int$Pile)
)

# hold elevation at its mean (0 after scaling)
newdat$Chamber_Elevation_sc <- 0

# predict population-level (fixed effects only)
newdat$pred_log <- predict(m_n2o_temp_int, newdata = newdat, level = 0)
newdat$pred_flux <- exp(newdat$pred_log)

ggplot(dat_n2o_int,
       aes(x = pile_temp_sc,
           y = exp(log_flux),
           colour = Pile)) +

  geom_point(alpha = 0.15, size = 1) +

  geom_line(data = newdat,
            aes(x = pile_temp_sc,
                y = pred_flux,
                colour = Pile),
            linewidth = 1.2) +

  theme_bw(base_size = 12) +
  theme(panel.grid.minor = element_blank()) +

  labs(
    x = "Pile temperature (scaled)",
    y = expression(n2o~flux~(back~transformed)),
    colour = "Pile"
  )

dat_co2e_int <- dat_co2e %>%
  mutate(Pile = factor(Pile, levels = c("C", "E"))) %>%
  # remove invalid temperature values
  mutate(
    TS_1.initial_value = ifelse(
      TS_1.initial_value <= 0,
      NA,
      TS_1.initial_value
    )
  ) %>%
  
  # fill missing values with the mean
  mutate(
    TS_1_filled = ifelse(
      is.na(TS_1.initial_value),
      mean(TS_1.initial_value, na.rm = TRUE),
      TS_1.initial_value
    )
  ) %>%
  
  # scale AFTER filling
  mutate(
    pile_temp_sc = as.numeric(scale(TS_1_filled))
  )

m_co2e_temp_int <- lme(
  log_flux ~ Pile * pile_temp_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2e_int,
  method = "REML",
  na.action = na.omit
)

summary(m_co2e_temp_int)$tTable
##                           Value  Std.Error   DF    t-value       p-value
## (Intercept)           9.9584957 0.41351778 5549 24.0823880 5.753037e-122
## PileE                -0.5311861 0.61399085    4 -0.8651369  4.357648e-01
## pile_temp_sc          0.1202260 0.04005672 5549  3.0013927  2.699437e-03
## Chamber_Elevation_sc  0.2199742 0.03335744 5549  6.5944558  4.663712e-11
## PileE:pile_temp_sc   -0.3739876 0.06384617 5549 -5.8576365  4.964783e-09
m_base_ml <- update(m_co2e_elev, method = "ML")
m_int_ml <- update(m_co2e_temp_int, method = "ML")
anova(m_base_ml  , m_int_ml)
##           Model df      AIC      BIC    logLik   Test  L.Ratio p-value
## m_base_ml     1  7 4827.596 4873.957 -2406.798                        
## m_int_ml      2  9 4797.290 4856.897 -2389.645 1 vs 2 34.30643  <.0001
# temperature range (scaled)
temp_seq <- seq(
  min(dat_co2e_int$pile_temp_sc),
  max(dat_co2e_int$pile_temp_sc),
  length.out = 100
)

newdat <- expand.grid(
  pile_temp_sc = temp_seq,
  Pile = levels(dat_co2e_int$Pile)
)

# hold elevation at its mean (0 after scaling)
newdat$Chamber_Elevation_sc <- 0

# predict population-level (fixed effects only)
newdat$pred_log <- predict(m_co2e_temp_int, newdata = newdat, level = 0)
newdat$pred_flux <- exp(newdat$pred_log)

ggplot(dat_co2e_int,
       aes(x = pile_temp_sc,
           y = exp(log_flux),
           colour = Pile)) +

  geom_point(alpha = 0.15, size = 1) +

  geom_line(data = newdat,
            aes(x = pile_temp_sc,
                y = pred_flux,
                colour = Pile),
            linewidth = 1.2) +

  theme_bw(base_size = 12) +
  theme(panel.grid.minor = element_blank()) +

  labs(
    x = "Pile temperature (scaled)",
    y = expression(Co2e~flux~(back~transformed)),
    colour = "Pile"
  )

# ---- 1) Build modeling dataframe (CO2 + moisture) ----
dat_co2_moist_int <- dat_co2 %>%
  mutate(
    Pile = factor(Pile, levels = c("C", "E")),
    
    # moisture raw (your chosen column)
    moist_raw = SWC_1.initial_value,
    
    # OPTIONAL: screen impossible moisture values (adjust if needed)
    moist_raw = ifelse(moist_raw < 0.01 | moist_raw > 0.99, NA, moist_raw),
    
    # mean-fill, then scale
    moist_filled = ifelse(
      is.na(moist_raw),
      mean(moist_raw, na.rm = TRUE),
      moist_raw
    ),
    pile_moist_sc = as.numeric(scale(moist_filled))
  )

# sanity checks
stopifnot(nrow(dat_co2_moist_int) > 0)
stopifnot(all(!is.na(dat_co2_moist_int$pile_moist_sc)))
stopifnot(!is.null(levels(dat_co2_moist_int$Pile)))

# ---- 2) Fit moisture interaction model (REML) ----
m_co2_moist_int <- lme(
  log_flux ~ Pile * pile_moist_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2_moist_int,
  method = "REML",
  na.action = na.omit
)

summary(m_co2_moist_int)$tTable
##                            Value  Std.Error   DF     t-value      p-value
## (Intercept)           4.84217146 0.29294469 9270 16.52930249 1.649691e-60
## PileE                -0.01294063 0.41710942    4 -0.03102454 9.767363e-01
## pile_moist_sc        -0.09582767 0.02889467 9270 -3.31644826 9.152002e-04
## Chamber_Elevation_sc  0.07924892 0.02398781 9270  3.30371615 9.577405e-04
## PileE:pile_moist_sc   0.13491124 0.04839345 9270  2.78779942 5.317547e-03
# ---- 3) ML refits for valid model comparison ----
m_co2_elev_ml <- lme(
  log_flux ~ Pile + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2_moist_int,   # IMPORTANT: same data object
  method = "ML",
  na.action = na.omit
)

m_co2_moist_int_ml <- lme(
  log_flux ~ Pile * pile_moist_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2_moist_int,   # same data object
  method = "ML",
  na.action = na.omit
)

anova(m_co2_elev_ml, m_co2_moist_int_ml)
##                    Model df      AIC      BIC    logLik   Test  L.Ratio p-value
## m_co2_elev_ml          1  7 5631.331 5681.279 -2808.665                        
## m_co2_moist_int_ml     2  9 5623.971 5688.190 -2802.985 1 vs 2 11.36004  0.0034
# ---- 4) Prediction grid + plot ----
moist_seq <- seq(
  min(dat_co2_moist_int$pile_moist_sc, na.rm = TRUE),
  max(dat_co2_moist_int$pile_moist_sc, na.rm = TRUE),
  length.out = 100
)

newdat <- expand.grid(
  pile_moist_sc = moist_seq,
  Pile = levels(dat_co2_moist_int$Pile)
)

# hold elevation at mean (0 after scaling)
newdat$Chamber_Elevation_sc <- 0

# predict fixed effects only
newdat$pred_log  <- predict(m_co2_moist_int, newdata = newdat, level = 0)
newdat$pred_flux <- exp(newdat$pred_log)

ggplot(dat_co2_moist_int,
       aes(x = pile_moist_sc, y = exp(log_flux), colour = Pile)) +
  geom_point(alpha = 0.15, size = 1) +
  geom_line(data = newdat, aes(y = pred_flux), linewidth = 1.2) +
  theme_bw(base_size = 12) +
  theme(panel.grid.minor = element_blank()) +
  labs(
    x = "Pile moisture (scaled)",
    y = expression(CO[2]~flux~(back~transformed)),
    colour = "Pile"
  )

dat_ch4_moist_int <- dat_ch4 %>%
  mutate(
    Pile = factor(Pile, levels = c("C", "E")),
    
    # moisture raw (your chosen column)
    moist_raw = SWC_1.initial_value,
    
    # OPTIONAL: screen impossible moisture values (adjust if needed)
    moist_raw = ifelse(moist_raw < 0.01 | moist_raw > 0.99, NA, moist_raw),
    
    # mean-fill, then scale
    moist_filled = ifelse(
      is.na(moist_raw),
      mean(moist_raw, na.rm = TRUE),
      moist_raw
    ),
    pile_moist_sc = as.numeric(scale(moist_filled))
  )

# sanity checks
stopifnot(nrow(dat_ch4_moist_int) > 0)
stopifnot(all(!is.na(dat_ch4_moist_int$pile_moist_sc)))
stopifnot(!is.null(levels(dat_ch4_moist_int$Pile)))

# ---- 2) Fit moisture interaction model (REML) ----
m_ch4_moist_int <- lme(
  log_flux ~ Pile * pile_moist_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ch4_moist_int,
  method = "REML",
  na.action = na.omit
)

summary(m_ch4_moist_int)$tTable
##                           Value  Std.Error    DF    t-value      p-value
## (Intercept)           7.4061972 0.72914951 10283 10.1573094 3.995447e-24
## PileE                -0.6500348 1.06818848     4 -0.6085394 5.756931e-01
## pile_moist_sc         0.3063050 0.03759737 10283  8.1469782 4.164514e-16
## Chamber_Elevation_sc  0.2132810 0.03879621 10283  5.4974713 3.944808e-08
## PileE:pile_moist_sc  -0.2441262 0.07226154 10283 -3.3783704 7.318748e-04
# ---- 3) ML refits for valid model comparison ----
m_ch4_elev_ml <- lme(
  log_flux ~ Pile + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ch4_moist_int,   # IMPORTANT: same data object
  method = "ML",
  na.action = na.omit
)

m_ch4_moist_int_ml <- lme(
  log_flux ~ Pile * pile_moist_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ch4_moist_int,   # same data object
  method = "ML",
  na.action = na.omit
)

anova(m_ch4_elev_ml, m_ch4_moist_int_ml)
##                    Model df      AIC      BIC    logLik   Test L.Ratio p-value
## m_ch4_elev_ml          1  7 11026.02 11076.70 -5506.012                       
## m_ch4_moist_int_ml     2  9 10962.77 11027.93 -5472.387 1 vs 2 67.2504  <.0001
# ---- 4) Prediction grid + plot ----
moist_seq <- seq(
  min(dat_ch4_moist_int$pile_moist_sc, na.rm = TRUE),
  max(dat_ch4_moist_int$pile_moist_sc, na.rm = TRUE),
  length.out = 100
)

newdat <- expand.grid(
  pile_moist_sc = moist_seq,
  Pile = levels(dat_ch4_moist_int$Pile)
)

# hold elevation at mean (0 after scaling)
newdat$Chamber_Elevation_sc <- 0

# predict fixed effects only
newdat$pred_log  <- predict(m_ch4_moist_int, newdata = newdat, level = 0)
newdat$pred_flux <- exp(newdat$pred_log)

ggplot(dat_ch4_moist_int,
       aes(x = pile_moist_sc, y = exp(log_flux), colour = Pile)) +
  geom_point(alpha = 0.15, size = 1) +
  geom_line(data = newdat, aes(y = pred_flux), linewidth = 1.2) +
  theme_bw(base_size = 12) +
  theme(panel.grid.minor = element_blank()) +
  labs(
    x = "Pile moisture (scaled)",
    y = expression(CH[4]~flux~(back~transformed)),
    colour = "Pile"
  )

dat_n2o_moist_int <- dat_n2o %>%
  mutate(
    Pile = factor(Pile, levels = c("C", "E")),
    
    # moisture raw (your chosen column)
    moist_raw = SWC_1.initial_value,
    
    # OPTIONAL: screen impossible moisture values (adjust if needed)
    moist_raw = ifelse(moist_raw < 0.01 | moist_raw > 0.99, NA, moist_raw),
    
    # mean-fill, then scale
    moist_filled = ifelse(
      is.na(moist_raw),
      mean(moist_raw, na.rm = TRUE),
      moist_raw
    ),
    pile_moist_sc = as.numeric(scale(moist_filled))
  )

# sanity checks
stopifnot(nrow(dat_n2o_moist_int) > 0)
stopifnot(all(!is.na(dat_n2o_moist_int$pile_moist_sc)))
stopifnot(!is.null(levels(dat_n2o_moist_int$Pile)))

# ---- 2) Fit moisture interaction model (REML) ----
m_n2o_moist_int <- lme(
  log_flux ~ Pile * pile_moist_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_n2o_moist_int,
  method = "REML",
  na.action = na.omit
)

summary(m_n2o_moist_int)$tTable
##                            Value  Std.Error    DF    t-value      p-value
## (Intercept)           4.02060940 0.26031732 13808 15.4450322 2.275968e-53
## PileE                -0.39629575 0.37916930     4 -1.0451683 3.549443e-01
## pile_moist_sc        -0.02186211 0.03052206 13808 -0.7162724 4.738353e-01
## Chamber_Elevation_sc  0.26227703 0.03812119 13808  6.8800849 6.239340e-12
## PileE:pile_moist_sc   0.01957934 0.04830110 13808  0.4053601 6.852191e-01
# ---- 3) ML refits for valid model comparison ----
m_n2o_elev_ml <- lme(
  log_flux ~ Pile + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_n2o_moist_int,   # IMPORTANT: same data object
  method = "ML",
  na.action = na.omit
)

m_n2o_moist_int_ml <- lme(
  log_flux ~ Pile * pile_moist_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_n2o_moist_int,   # same data object
  method = "ML",
  na.action = na.omit
)

anova(m_n2o_elev_ml, m_n2o_moist_int_ml)
##                    Model df      AIC      BIC    logLik   Test   L.Ratio
## m_n2o_elev_ml          1  7 10630.19 10682.92 -5308.093                 
## m_n2o_moist_int_ml     2  9 10633.75 10701.56 -5307.876 1 vs 2 0.4346655
##                    p-value
## m_n2o_elev_ml             
## m_n2o_moist_int_ml  0.8047
# ---- 4) Prediction grid + plot ----
moist_seq <- seq(
  min(dat_n2o_moist_int$pile_moist_sc, na.rm = TRUE),
  max(dat_n2o_moist_int$pile_moist_sc, na.rm = TRUE),
  length.out = 100
)

newdat <- expand.grid(
  pile_moist_sc = moist_seq,
  Pile = levels(dat_n2o_moist_int$Pile)
)

# hold elevation at mean (0 after scaling)
newdat$Chamber_Elevation_sc <- 0

# predict fixed effects only
newdat$pred_log  <- predict(m_n2o_moist_int, newdata = newdat, level = 0)
newdat$pred_flux <- exp(newdat$pred_log)

ggplot(dat_n2o_moist_int,
       aes(x = pile_moist_sc, y = exp(log_flux), colour = Pile)) +
  geom_point(alpha = 0.15, size = 1) +
  geom_line(data = newdat, aes(y = pred_flux), linewidth = 1.2) +
  theme_bw(base_size = 12) +
  theme(panel.grid.minor = element_blank()) +
  labs(
    x = "Pile moisture (scaled)",
    y = expression(n2o~flux~(back~transformed)),
    colour = "Pile"
  )

dat_co2e_moist_int <- dat_co2e %>%
  mutate(
    Pile = factor(Pile, levels = c("C", "E")),
    
    # moisture raw (your chosen column)
    moist_raw = SWC_1.initial_value,
    
    # OPTIONAL: screen impossible moisture values (adjust if needed)
    moist_raw = ifelse(moist_raw < 0.01 | moist_raw > 0.99, NA, moist_raw),
    
    # mean-fill, then scale
    moist_filled = ifelse(
      is.na(moist_raw),
      mean(moist_raw, na.rm = TRUE),
      moist_raw
    ),
    pile_moist_sc = as.numeric(scale(moist_filled))
  )

# sanity checks
stopifnot(nrow(dat_co2e_moist_int) > 0)
stopifnot(all(!is.na(dat_co2e_moist_int$pile_moist_sc)))
stopifnot(!is.null(levels(dat_co2e_moist_int$Pile)))

# ---- 2) Fit moisture interaction model (REML) ----
m_co2e_moist_int <- lme(
  log_flux ~ Pile * pile_moist_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2e_moist_int,
  method = "REML",
  na.action = na.omit
)

summary(m_co2e_moist_int)$tTable
##                           Value  Std.Error   DF    t-value       p-value
## (Intercept)          10.0183537 0.33351685 5549 30.0385233 8.157644e-184
## PileE                -0.4570616 0.50581505    4 -0.9036142  4.172990e-01
## pile_moist_sc         0.1221855 0.04464400 5549  2.7368848  6.222153e-03
## Chamber_Elevation_sc  0.1935003 0.03342559 5549  5.7889862  7.469444e-09
## PileE:pile_moist_sc  -0.2787880 0.07960032 5549 -3.5023475  4.648106e-04
# ---- 3) ML refits for valid model comparison ----
m_co2e_elev_ml <- lme(
  log_flux ~ Pile + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2e_moist_int,   # IMPORTANT: same data object
  method = "ML",
  na.action = na.omit
)

m_co2e_moist_int_ml <- lme(
  log_flux ~ Pile * pile_moist_sc + Chamber_Elevation_sc,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2e_moist_int,   # same data object
  method = "ML",
  na.action = na.omit
)

anova(m_co2e_elev_ml, m_co2e_moist_int_ml)
##                     Model df      AIC      BIC    logLik   Test  L.Ratio
## m_co2e_elev_ml          1  7 4827.596 4873.957 -2406.798                
## m_co2e_moist_int_ml     2  9 4818.227 4877.834 -2400.114 1 vs 2 13.36917
##                     p-value
## m_co2e_elev_ml             
## m_co2e_moist_int_ml  0.0013
# ---- 4) Prediction grid + plot ----
moist_seq <- seq(
  min(dat_co2e_moist_int$pile_moist_sc, na.rm = TRUE),
  max(dat_co2e_moist_int$pile_moist_sc, na.rm = TRUE),
  length.out = 100
)

newdat <- expand.grid(
  pile_moist_sc = moist_seq,
  Pile = levels(dat_co2e_moist_int$Pile)
)

# hold elevation at mean (0 after scaling)
newdat$Chamber_Elevation_sc <- 0

# predict fixed effects only
newdat$pred_log  <- predict(m_co2e_moist_int, newdata = newdat, level = 0)
newdat$pred_flux <- exp(newdat$pred_log)

ggplot(dat_co2e_moist_int,
       aes(x = pile_moist_sc, y = exp(log_flux), colour = Pile)) +
  geom_point(alpha = 0.15, size = 1) +
  geom_line(data = newdat, aes(y = pred_flux), linewidth = 1.2) +
  theme_bw(base_size = 12) +
  theme(panel.grid.minor = element_blank()) +
  labs(
    x = "Pile moisture (scaled)",
    y = expression(Co2e~flux~(back~transformed)),
    colour = "Pile"
  )

Looking at impacts post turning -

dat_co2_pt <- dat_co2_int %>%
  mutate(
    PostTurn_0_1d = if_else(!is.na(DaysSinceLastTurn) &
                             DaysSinceLastTurn > 0 &
                             DaysSinceLastTurn <= 1,
                           1L, 0L)
  )

count(dat_co2_pt, Pile, PostTurn_0_1d)
## # A tibble: 4 × 3
##   Pile  PostTurn_0_1d     n
##   <fct>         <int> <int>
## 1 C                 0  4723
## 2 C                 1   341
## 3 E                 0  4089
## 4 E                 1   126
dat_co2_pt_use <- dat_co2_pt %>%
  filter(!is.na(FCO2_DRY.LIN), is.finite(FCO2_DRY.LIN))

count(dat_co2_pt_use, Pile, PostTurn_0_1d)
## # A tibble: 4 × 3
##   Pile  PostTurn_0_1d     n
##   <fct>         <int> <int>
## 1 C                 0  4723
## 2 C                 1   341
## 3 E                 0  4089
## 4 E                 1   126
summary_co2 <- dat_co2_pt_use %>%
  group_by(Pile, PostTurn_0_1d) %>%
  summarise(
    n = n(),
    mean = mean(FCO2_DRY.LIN),
    sd   = sd(FCO2_DRY.LIN),
    se   = sd / sqrt(n),
    median = median(FCO2_DRY.LIN),
    .groups = "drop"
  )

summary_co2
## # A tibble: 4 × 7
##   Pile  PostTurn_0_1d     n  mean    sd    se median
##   <fct>         <int> <int> <dbl> <dbl> <dbl>  <dbl>
## 1 C                 0  4723  188.  179.  2.61  132. 
## 2 C                 1   341  217.  403. 21.8    94.0
## 3 E                 0  4089  193.  195.  3.04  146. 
## 4 E                 1   126  164.  174. 15.5   130.
co2_effect <- summary_co2 %>%
  select(Pile, PostTurn_0_1d, mean, median) %>%
  tidyr::pivot_wider(names_from = PostTurn_0_1d,
                     values_from = c(mean, median),
                     names_prefix = "PostTurn=") %>%
  mutate(
    mean_diff   = `mean_PostTurn=1` - `mean_PostTurn=0`,
    mean_ratio  = `mean_PostTurn=1` / `mean_PostTurn=0`,
    median_diff = `median_PostTurn=1` - `median_PostTurn=0`,
    median_ratio= `median_PostTurn=1` / `median_PostTurn=0`
  )

co2_effect
## # A tibble: 2 × 9
##   Pile  `mean_PostTurn=0` `mean_PostTurn=1` `median_PostTurn=0`
##   <fct>             <dbl>             <dbl>               <dbl>
## 1 C                  188.              217.                132.
## 2 E                  193.              164.                146.
## # ℹ 5 more variables: `median_PostTurn=1` <dbl>, mean_diff <dbl>,
## #   mean_ratio <dbl>, median_diff <dbl>, median_ratio <dbl>
ggplot(dat_co2_pt_use,
       aes(x = factor(PostTurn_0_1d),
           y = FCO2_DRY.LIN,
           fill = Pile)) +
  geom_boxplot(outlier.alpha = 0.3) +
  scale_x_discrete(labels = c("0" = "Not post-turn",
                              "1" = "0–1 d post-turn")) +
  labs(x = "", y = "CO₂ flux") +
  theme_bw()

m_co2_postturn <- lme(
  fixed = log(FCO2_DRY.LIN) ~ Pile * PostTurn_0_1d,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2_pt_use,
  method = "REML",
  na.action = na.omit
)


m_co2_no_int <- lme(
  fixed = log(FCO2_DRY.LIN) ~ Pile + PostTurn_0_1d,
   random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2_pt_use,
  method = "REML",
  na.action = na.omit
)

anova(m_co2_no_int, m_co2_postturn)
## Warning in anova.lme(m_co2_no_int, m_co2_postturn): fitted objects with
## different fixed effects. REML comparisons are not meaningful.
##                Model df      AIC      BIC    logLik   Test  L.Ratio p-value
## m_co2_no_int       1  7 5634.897 5684.843 -2810.448                        
## m_co2_postturn     2  8 5610.898 5667.978 -2797.449 1 vs 2 25.99915  <.0001
summary(m_co2_postturn)
## Linear mixed-effects model fit by REML
##   Data: dat_co2_pt_use 
##        AIC      BIC    logLik
##   5610.898 5667.978 -2797.449
## 
## Random effects:
##  Formula: ~1 | Chamber_Corrected
##         (Intercept)  Residual
## StdDev:   0.4255526 0.7555908
## 
## Correlation Structure: AR(1)
##  Formula: ~t_index | Chamber_Corrected 
##  Parameter estimate(s):
##      Phi 
## 0.917621 
## Variance function:
##  Structure: Different standard deviations per stratum
##  Formula: ~1 | Pile 
##  Parameter estimates:
##        C        E 
## 1.000000 1.202184 
## Fixed effects:  log(FCO2_DRY.LIN) ~ Pile * PostTurn_0_1d 
##                         Value Std.Error   DF   t-value p-value
## (Intercept)          4.853508 0.2509395 9271 19.341350  0.0000
## PileE               -0.005522 0.3576634    4 -0.015440  0.9884
## PostTurn_0_1d        0.024753 0.0475013 9271  0.521105  0.6023
## PileE:PostTurn_0_1d  0.518734 0.0964963 9271  5.375683  0.0000
##  Correlation: 
##                     (Intr) PileE  PT_0_1
## PileE               -0.702              
## PostTurn_0_1d       -0.014  0.010       
## PileE:PostTurn_0_1d  0.007 -0.013 -0.492
## 
## Standardized Within-Group Residuals:
##         Min          Q1         Med          Q3         Max 
## -5.19017184 -0.59941032  0.07751574  0.70676900  3.28926220 
## 
## Number of Observations: 9279
## Number of Groups: 6

So if we look at day after turning between the pile there is a huge difference that is statistically sig, if you change this to 3 days there is too much noise but the mean is still higher. This is something that can be used to say that there are differences in how the piles respond to turning based on the treatment. Not sure what to say about this, but we can decide what this is.

dat_ch4_pt <- dat_ch4_int %>%
  mutate(
    PostTurn_0_1d = if_else(!is.na(DaysSinceLastTurn) &
                             DaysSinceLastTurn > 0 &
                             DaysSinceLastTurn <= 1,
                           1L, 0L)
  )

count(dat_ch4_pt, Pile, PostTurn_0_1d)
## # A tibble: 4 × 3
##   Pile  PostTurn_0_1d     n
##   <fct>         <int> <int>
## 1 C                 0  4957
## 2 C                 1   398
## 3 E                 0  4806
## 4 E                 1   131
dat_ch4_pt_use <- dat_ch4_pt %>%
  filter(!is.na(FCH4_DRY.LIN), is.finite(FCH4_DRY.LIN))

count(dat_ch4_pt_use, Pile, PostTurn_0_1d)
## # A tibble: 4 × 3
##   Pile  PostTurn_0_1d     n
##   <fct>         <int> <int>
## 1 C                 0  4957
## 2 C                 1   398
## 3 E                 0  4806
## 4 E                 1   131
summary_ch4 <- dat_ch4_pt_use %>%
  group_by(Pile, PostTurn_0_1d) %>%
  summarise(
    n = n(),
    mean = mean(FCH4_DRY.LIN),
    sd   = sd(FCH4_DRY.LIN),
    se   = sd / sqrt(n),
    median = median(FCH4_DRY.LIN),
    .groups = "drop"
  )

summary_ch4
## # A tibble: 4 × 7
##   Pile  PostTurn_0_1d     n  mean     sd    se median
##   <fct>         <int> <int> <dbl>  <dbl> <dbl>  <dbl>
## 1 C                 0  4957 7101. 11525.  164.  1914.
## 2 C                 1   398 6488. 14168.  710.  1377.
## 3 E                 0  4806 6710. 10434.  151.  2527.
## 4 E                 1   131 2362.  2198.  192.  1665.
ch4_effect <- summary_ch4 %>%
  select(Pile, PostTurn_0_1d, mean, median) %>%
  tidyr::pivot_wider(names_from = PostTurn_0_1d,
                     values_from = c(mean, median),
                     names_prefix = "PostTurn=") %>%
  mutate(
    mean_diff   = `mean_PostTurn=1` - `mean_PostTurn=0`,
    mean_ratio  = `mean_PostTurn=1` / `mean_PostTurn=0`,
    median_diff = `median_PostTurn=1` - `median_PostTurn=0`,
    median_ratio= `median_PostTurn=1` / `median_PostTurn=0`
  )

ch4_effect
## # A tibble: 2 × 9
##   Pile  `mean_PostTurn=0` `mean_PostTurn=1` `median_PostTurn=0`
##   <fct>             <dbl>             <dbl>               <dbl>
## 1 C                 7101.             6488.               1914.
## 2 E                 6710.             2362.               2527.
## # ℹ 5 more variables: `median_PostTurn=1` <dbl>, mean_diff <dbl>,
## #   mean_ratio <dbl>, median_diff <dbl>, median_ratio <dbl>
ggplot(dat_ch4_pt_use,
       aes(x = factor(PostTurn_0_1d),
           y = FCH4_DRY.LIN,
           fill = Pile)) +
  geom_boxplot(outlier.alpha = 0.3) +
  scale_x_discrete(labels = c("0" = "Not post-turn",
                              "1" = "0–1 d post-turn")) +
  labs(x = "", y = "CO₂ flux") +
  theme_bw()

m_ch4_postturn <- lme(
  fixed = log(FCH4_DRY.LIN) ~ Pile * PostTurn_0_1d,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ch4_pt_use,
  method = "REML",
  na.action = na.omit
)


m_ch4_no_int <- lme(
  fixed = log(FCH4_DRY.LIN) ~ Pile + PostTurn_0_1d,
   random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ch4_pt_use,
  method = "REML",
  na.action = na.omit
)

anova(m_ch4_no_int, m_ch4_postturn)
## Warning in anova.lme(m_ch4_no_int, m_ch4_postturn): fitted objects with
## different fixed effects. REML comparisons are not meaningful.
##                Model df      AIC      BIC    logLik   Test  L.Ratio p-value
## m_ch4_no_int       1  7 11069.28 11119.95 -5527.641                        
## m_ch4_postturn     2  8 11061.47 11119.38 -5522.733 1 vs 2 9.816498  0.0017
summary(m_ch4_postturn)
## Linear mixed-effects model fit by REML
##   Data: dat_ch4_pt_use 
##        AIC      BIC    logLik
##   11061.47 11119.38 -5522.733
## 
## Random effects:
##  Formula: ~1 | Chamber_Corrected
##         (Intercept) Residual
## StdDev:    1.136322 1.575652
## 
## Correlation Structure: AR(1)
##  Formula: ~t_index | Chamber_Corrected 
##  Parameter estimate(s):
##       Phi 
## 0.9783855 
## Variance function:
##  Structure: Different standard deviations per stratum
##  Formula: ~1 | Pile 
##  Parameter estimates:
##        C        E 
## 1.000000 1.640851 
## Fixed effects:  log(FCH4_DRY.LIN) ~ Pile * PostTurn_0_1d 
##                         Value Std.Error    DF   t-value p-value
## (Intercept)          7.262763 0.6869372 10284 10.572675  0.0000
## PileE               -0.462141 1.0108066     4 -0.457200  0.6713
## PostTurn_0_1d       -0.057435 0.0507006 10284 -1.132831  0.2573
## PileE:PostTurn_0_1d  0.484199 0.1402659 10284  3.452011  0.0006
##  Correlation: 
##                     (Intr) PileE  PT_0_1
## PileE               -0.680              
## PostTurn_0_1d       -0.006  0.004       
## PileE:PostTurn_0_1d  0.002 -0.006 -0.361
## 
## Standardized Within-Group Residuals:
##        Min         Q1        Med         Q3        Max 
## -3.0399901 -0.5922974  0.2570463  0.7412248  2.5679548 
## 
## Number of Observations: 10292
## Number of Groups: 6

These values don’t make that much sense - we would expect post turn = 0 for the values to be similar with E having more ch4 then we have a much high mean for C after turning which I dont know what to make of that but we have a lower C pile median than E pile which makes me think there are huge spikes in the control and then it is lower where the E is just higher overall but not a huge spike ?

dat_n2o_pt <- dat_n2o_int %>%
  mutate(
    PostTurn_0_1d = if_else(!is.na(DaysSinceLastTurn) &
                             DaysSinceLastTurn > 0 &
                             DaysSinceLastTurn <= 1,
                           1L, 0L)
  )

count(dat_n2o_pt, Pile, PostTurn_0_1d)
## # A tibble: 4 × 3
##   Pile  PostTurn_0_1d     n
##   <fct>         <int> <int>
## 1 C                 0  6397
## 2 C                 1   509
## 3 E                 0  6642
## 4 E                 1   269
dat_n2o_pt_use <- dat_n2o_pt %>%
  filter(!is.na(FN2O_DRY.LIN), is.finite(FN2O_DRY.LIN))

count(dat_n2o_pt_use, Pile, PostTurn_0_1d)
## # A tibble: 4 × 3
##   Pile  PostTurn_0_1d     n
##   <fct>         <int> <int>
## 1 C                 0  6397
## 2 C                 1   509
## 3 E                 0  6642
## 4 E                 1   269
summary_n2o <- dat_n2o_pt_use %>%
  group_by(Pile, PostTurn_0_1d) %>%
  summarise(
    n = n(),
    mean = mean(FN2O_DRY.LIN),
    sd   = sd(FN2O_DRY.LIN),
    se   = sd / sqrt(n),
    median = median(FN2O_DRY.LIN),
    .groups = "drop"
  )

summary_n2o
## # A tibble: 4 × 7
##   Pile  PostTurn_0_1d     n  mean    sd    se median
##   <fct>         <int> <int> <dbl> <dbl> <dbl>  <dbl>
## 1 C                 0  6397 154.   298.  3.73   61.5
## 2 C                 1   509 200.   247. 10.9   111. 
## 3 E                 0  6642  96.2  247.  3.03   34.7
## 4 E                 1   269 154.   195. 11.9    89.9
n2o_effect <- summary_n2o %>%
  select(Pile, PostTurn_0_1d, mean, median) %>%
  tidyr::pivot_wider(names_from = PostTurn_0_1d,
                     values_from = c(mean, median),
                     names_prefix = "PostTurn=") %>%
  mutate(
    mean_diff   = `mean_PostTurn=1` - `mean_PostTurn=0`,
    mean_ratio  = `mean_PostTurn=1` / `mean_PostTurn=0`,
    median_diff = `median_PostTurn=1` - `median_PostTurn=0`,
    median_ratio= `median_PostTurn=1` / `median_PostTurn=0`
  )

n2o_effect
## # A tibble: 2 × 9
##   Pile  `mean_PostTurn=0` `mean_PostTurn=1` `median_PostTurn=0`
##   <fct>             <dbl>             <dbl>               <dbl>
## 1 C                 154.               200.                61.5
## 2 E                  96.2              154.                34.7
## # ℹ 5 more variables: `median_PostTurn=1` <dbl>, mean_diff <dbl>,
## #   mean_ratio <dbl>, median_diff <dbl>, median_ratio <dbl>
ggplot(dat_n2o_pt_use,
       aes(x = factor(PostTurn_0_1d),
           y = FN2O_DRY.LIN,
           fill = Pile)) +
  geom_boxplot(outlier.alpha = 0.3) +
  scale_x_discrete(labels = c("0" = "Not post-turn",
                              "1" = "0–1 d post-turn")) +
  labs(x = "", y = "CO₂ flux") +
  theme_bw()

m_n2o_postturn <- lme(
  fixed = log(FN2O_DRY.LIN) ~ Pile * PostTurn_0_1d,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_n2o_pt_use,
  method = "REML",
  na.action = na.omit
)


m_n2o_no_int <- lme(
  fixed = log(FN2O_DRY.LIN) ~ Pile + PostTurn_0_1d,
   random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_n2o_pt_use,
  method = "REML",
  na.action = na.omit
)

anova(m_n2o_no_int, m_n2o_postturn)
## Warning in anova.lme(m_n2o_no_int, m_n2o_postturn): fitted objects with
## different fixed effects. REML comparisons are not meaningful.
##                Model df      AIC      BIC    logLik   Test  L.Ratio p-value
## m_n2o_no_int       1  7 10525.12 10577.85 -5255.561                        
## m_n2o_postturn     2  8 10529.83 10590.10 -5256.914 1 vs 2 2.707689  0.0999
summary(m_n2o_postturn)
## Linear mixed-effects model fit by REML
##   Data: dat_n2o_pt_use 
##        AIC     BIC    logLik
##   10529.83 10590.1 -5256.914
## 
## Random effects:
##  Formula: ~1 | Chamber_Corrected
##         (Intercept) Residual
## StdDev:   0.3133339 1.222624
## 
## Correlation Structure: AR(1)
##  Formula: ~t_index | Chamber_Corrected 
##  Parameter estimate(s):
##       Phi 
## 0.9670161 
## Variance function:
##  Structure: Different standard deviations per stratum
##  Formula: ~1 | Pile 
##  Parameter estimates:
##        C        E 
## 1.000000 1.289316 
## Fixed effects:  log(FN2O_DRY.LIN) ~ Pile * PostTurn_0_1d 
##                         Value  Std.Error    DF   t-value p-value
## (Intercept)          4.002188 0.21311569 13809 18.779415  0.0000
## PileE               -0.447245 0.31504610     4 -1.419618  0.2287
## PostTurn_0_1d        0.537164 0.04795271 13809 11.201953  0.0000
## PileE:PostTurn_0_1d -0.022472 0.10048281 13809 -0.223644  0.8230
##  Correlation: 
##                     (Intr) PileE  PT_0_1
## PileE               -0.676              
## PostTurn_0_1d       -0.019  0.013       
## PileE:PostTurn_0_1d  0.009 -0.019 -0.477
## 
## Standardized Within-Group Residuals:
##         Min          Q1         Med          Q3         Max 
## -3.96298572 -0.67720660  0.05171918  0.69990626  3.22264388 
## 
## Number of Observations: 13817
## Number of Groups: 6

Hmmmmm this one is weird so I’m not sure how to interpret it, but there is a stat difference p value of .09 at 1 day and .08 at 3 days and then at 7 days we see 0.00 something so unlike the other gases there is less noise and more a pronounced difference a week after turning unlike the other gases. So it seems n2o does not change immediately after turning, there is a delay vs co2 and ch4 where there is an immediate change, this might be mechanistic as it might be more microbe mediated vs built up release and aeration related ?

So there is a period after turning where n2o is different that is from like 2-3 days to about a week and then they are more similar, this pulse is very interesting.

dat_co2e_pt <- dat_co2e_int %>%
  mutate(
    PostTurn_0_1d = if_else(!is.na(DaysSinceLastTurn) &
                             DaysSinceLastTurn > 0 &
                             DaysSinceLastTurn <= 1,
                           1L, 0L)
  )

count(dat_co2e_pt, Pile, PostTurn_0_1d)
## # A tibble: 4 × 3
##   Pile  PostTurn_0_1d     n
##   <fct>         <int> <int>
## 1 C                 0  3012
## 2 C                 1   257
## 3 E                 0  2201
## 4 E                 1    88
dat_co2e_pt_use <- dat_co2e_pt %>%
  filter(!is.na(GHG_CO2e_umol), is.finite(GHG_CO2e_umol))

count(dat_co2e_pt_use, Pile, PostTurn_0_1d)
## # A tibble: 4 × 3
##   Pile  PostTurn_0_1d     n
##   <fct>         <int> <int>
## 1 C                 0  3012
## 2 C                 1   257
## 3 E                 0  2201
## 4 E                 1    88
summary_co2e <- dat_co2e_pt_use %>%
  group_by(Pile, PostTurn_0_1d) %>%
  summarise(
    n = n(),
    mean = mean(GHG_CO2e_umol),
    sd   = sd(GHG_CO2e_umol),
    se   = sd / sqrt(n),
    median = median(GHG_CO2e_umol),
    .groups = "drop"
  )

summary_co2e
## # A tibble: 4 × 7
##   Pile  PostTurn_0_1d     n   mean      sd    se median
##   <fct>         <int> <int>  <dbl>   <dbl> <dbl>  <dbl>
## 1 C                 0  3012 59676. 104318. 1901. 22688.
## 2 C                 1   257 58331.  62977. 3928. 33204.
## 3 E                 0  2201 39549.  97303. 2074. 16789.
## 4 E                 1    88 52479.  61025. 6505. 34833.
co2e_effect <- summary_co2e %>%
  select(Pile, PostTurn_0_1d, mean, median) %>%
  tidyr::pivot_wider(names_from = PostTurn_0_1d,
                     values_from = c(mean, median),
                     names_prefix = "PostTurn=") %>%
  mutate(
    mean_diff   = `mean_PostTurn=1` - `mean_PostTurn=0`,
    mean_ratio  = `mean_PostTurn=1` / `mean_PostTurn=0`,
    median_diff = `median_PostTurn=1` - `median_PostTurn=0`,
    median_ratio= `median_PostTurn=1` / `median_PostTurn=0`
  )

co2e_effect
## # A tibble: 2 × 9
##   Pile  `mean_PostTurn=0` `mean_PostTurn=1` `median_PostTurn=0`
##   <fct>             <dbl>             <dbl>               <dbl>
## 1 C                59676.            58331.              22688.
## 2 E                39549.            52479.              16789.
## # ℹ 5 more variables: `median_PostTurn=1` <dbl>, mean_diff <dbl>,
## #   mean_ratio <dbl>, median_diff <dbl>, median_ratio <dbl>
ggplot(dat_co2e_pt_use,
       aes(x = factor(PostTurn_0_1d),
           y = GHG_CO2e_umol,
           fill = Pile)) +
  geom_boxplot(outlier.alpha = 0.3) +
  scale_x_discrete(labels = c("0" = "Not post-turn",
                              "1" = "0–1 d post-turn")) +
  labs(x = "", y = "CO₂e flux") +
  theme_bw()

m_co2e_postturn <- lme(
  fixed = log(GHG_CO2e_umol) ~ Pile * PostTurn_0_1d,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2e_pt_use,
  method = "REML",
  na.action = na.omit
)


m_co2e_no_int <- lme(
  fixed = log(GHG_CO2e_umol) ~ Pile + PostTurn_0_1d,
   random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_co2e_pt_use,
  method = "REML",
  na.action = na.omit
)

anova(m_co2e_no_int, m_co2e_postturn)
## Warning in anova.lme(m_co2e_no_int, m_co2e_postturn): fitted objects with
## different fixed effects. REML comparisons are not meaningful.
##                 Model df      AIC      BIC    logLik   Test L.Ratio p-value
## m_co2e_no_int       1  7 4835.454 4881.811 -2410.727                       
## m_co2e_postturn     2  8 4822.706 4875.684 -2403.353 1 vs 2 14.7479   1e-04
summary(m_co2e_postturn)
## Linear mixed-effects model fit by REML
##   Data: dat_co2e_pt_use 
##        AIC      BIC    logLik
##   4822.706 4875.684 -2403.353
## 
## Random effects:
##  Formula: ~1 | Chamber_Corrected
##         (Intercept) Residual
## StdDev:   0.4855702 1.182501
## 
## Correlation Structure: AR(1)
##  Formula: ~t_index | Chamber_Corrected 
##  Parameter estimate(s):
##       Phi 
## 0.9616903 
## Variance function:
##  Structure: Different standard deviations per stratum
##  Formula: ~1 | Pile 
##  Parameter estimates:
##        C        E 
## 1.000000 1.396841 
## Fixed effects:  log(GHG_CO2e_umol) ~ Pile * PostTurn_0_1d 
##                         Value Std.Error   DF   t-value p-value
## (Intercept)          9.913977 0.3164929 5550 31.324482  0.0000
## PileE               -0.414718 0.4862342    4 -0.852919  0.4418
## PostTurn_0_1d        0.210790 0.0533706 5550  3.949553  0.0001
## PileE:PostTurn_0_1d  0.536631 0.1300331 5550  4.126880  0.0000
##  Correlation: 
##                     (Intr) PileE  PT_0_1
## PileE               -0.651              
## PostTurn_0_1d       -0.013  0.008       
## PileE:PostTurn_0_1d  0.005 -0.014 -0.410
## 
## Standardized Within-Group Residuals:
##        Min         Q1        Med         Q3        Max 
## -3.2912859 -0.6082548  0.1009931  0.6584155  3.2024992 
## 
## Number of Observations: 5558
## Number of Groups: 6

It is significant for all three - which makes sense - sig goes down as I increase the time period we are looking at after turning.

Looking at ratios of gases:

While the means are not significantly different the ratio of trace gases might be, we would expect the exp pile to have more a higher ch4/co2 ratio compared to the more aerated c pile.

# -----------------------------
# 1) User-defined QC thresholds
# -----------------------------
r2_thresh <- 0.90   # example; set to whatever you use in your workflow

# ---------------------------------------------------
# 2) Build the ratio dataset with strict QC + units
# ---------------------------------------------------
# Key choices:
# - Only keep rows where BOTH CO2 and CH4 pass the linear-fit R² filter
# - Convert CH4 from nmol -> µmol so CO2 and CH4 are in comparable units
# - Keep only strictly positive fluxes for BOTH gases (required for log-ratios)
# - Create a per-chamber time index so AR(1) is defined correctly
dat_ratio <- Flux_All_Gases_QC %>%
  
  # ---- QC filter: both gases must pass the linear model R² threshold ----
  filter(
    FCO2_DRY.LIN_R2 >= r2_thresh,   # <-- replace with your actual CO2 R² column name
    FCH4_DRY.LIN_R2 >= r2_thresh    # <-- replace with your actual CH4 R² column name
  ) %>%
  
  # ---- Units: put both gases in µmol m-2 s-1 ----
  mutate(
    CO2_umol = FCO2_DRY.LIN,          # CO2 already in µmol m-2 s-1
    CH4_umol = FCH4_DRY.LIN * 0.001   # CH4 in nmol -> µmol
  ) %>%
  
  # ---- Ratio requires positive fluxes (log undefined at <= 0) ----
  filter(
    CO2_umol > 0,
    CH4_umol > 0
  ) %>%
  
  # ---- Define the ratio and its log-transform ----
  # log_ratio is what we model because ratios are skewed and multiplicative.
  mutate(
    ratio_CO2_CH4 = CO2_umol / CH4_umol,
    log_ratio     = log(ratio_CO2_CH4)
  ) %>%
  
  # ---- Time ordering + AR(1) index within chamber ----
  arrange(Chamber_Corrected, DOY) %>%
  group_by(Chamber_Corrected) %>%
  mutate(t_index = row_number()) %>%
  ungroup()

# Optional quick sanity checks
dat_ratio %>% summarise(
  n_obs = n(),
  n_chambers = n_distinct(Chamber_Corrected),
  min_ratio = min(ratio_CO2_CH4),
  max_ratio = max(ratio_CO2_CH4)
)
## # A tibble: 1 × 4
##   n_obs n_chambers min_ratio max_ratio
##   <int>      <int>     <dbl>     <dbl>
## 1  5558          6      2.23   115095.
# -----------------------------------------
# 3) Fit the mixed model on the log-ratio
# -----------------------------------------
# Model interpretation:
# - Fixed effect (Pile): does the mean log(CO2/CH4) differ by pile?
# - Random intercept (Chamber_Corrected): each chamber has its own baseline ratio
# - corAR1: accounts for strong autocorrelation within chamber time-series
# - varIdent: allows different residual variance in each pile
m_ratio <- lme(
  log_ratio ~ Pile,
  random = ~ 1 | Chamber_Corrected,
  correlation = corAR1(form = ~ t_index | Chamber_Corrected),
  weights = varIdent(form = ~ 1 | Pile),
  data = dat_ratio,
  method = "REML"
)

summary(m_ratio)
## Linear mixed-effects model fit by REML
##   Data: dat_ratio 
##        AIC      BIC    logLik
##   5382.935 5422.671 -2685.467
## 
## Random effects:
##  Formula: ~1 | Chamber_Corrected
##         (Intercept) Residual
## StdDev:    1.052017 1.252931
## 
## Correlation Structure: AR(1)
##  Formula: ~t_index | Chamber_Corrected 
##  Parameter estimate(s):
##       Phi 
## 0.9668886 
## Variance function:
##  Structure: Different standard deviations per stratum
##  Formula: ~1 | Pile 
##  Parameter estimates:
##        C        E 
## 1.000000 1.633941 
## Fixed effects:  log_ratio ~ Pile 
##                Value Std.Error   DF  t-value p-value
## (Intercept) 4.728213 0.6300319 5552 7.504720  0.0000
## PileE       0.899644 0.9315278    4 0.965772  0.3888
##  Correlation: 
##       (Intr)
## PileE -0.676
## 
## Standardized Within-Group Residuals:
##        Min         Q1        Med         Q3        Max 
## -2.3740851 -0.6680709 -0.1878222  0.5465996  3.0176256 
## 
## Number of Observations: 5558
## Number of Groups: 6
# ---------------------------------------------------------
# 4) Report pile-wise means on the RESPONSE SCALE (ratio)
# ---------------------------------------------------------
# emmeans avoids confusion about back-transformations and gives:
# - estimated mean ratio per pile
# - confidence intervals
# - correct degrees-of-freedom for the experimental design
# 4) Compare piles on the RESPONSE SCALE (ratio E/C)
# ============================================================
# Your model is fit on the log scale:
#   log_ratio = log(CO2/CH4)
#
# The pile contrast from emmeans (E - C) is therefore on the log scale.
# If we exponentiate that log difference, we get a multiplicative ratio:
#   exp( (log_ratio_E) - (log_ratio_C) ) = (CO2/CH4)_E / (CO2/CH4)_C
#
# This is the cleanest way to report: "Pile E has X times the CO2/CH4 ratio of pile C",
# along with a 95% confidence interval for that multiplicative factor.
# ============================================================



# 1) Estimated marginal means on the LINK (log) scale
emm_link <- emmeans(m_ratio, ~ Pile)     # returns E[log(CO2/CH4)] by pile

# 2) Pairwise contrast on the LINK scale: (E - C) in log units
ctr_link <- pairs(emm_link)

# 3) Get the contrast estimate + 95% CI on the LINK scale (this guarantees CI columns exist)
ctr_link_ci <- confint(ctr_link)  # returns estimate + lower.CL + upper.CL (and p.value)

ctr_link_ci
##  contrast estimate    SE df lower.CL upper.CL
##  C - E        -0.9 0.932  4    -3.49     1.69
## 
## Degrees-of-freedom method: containment 
## Confidence level used: 0.95
# This table is:
#   estimate  = (log_ratio_E - log_ratio_C)
#   lower.CL, upper.CL = confidence interval for that difference in logs


# 4) Convert to RESPONSE SCALE: multiplicative ratio (E/C) with CI
ratio_E_over_C <- as.data.frame(ctr_link_ci) %>%
  mutate(
    ratio      = exp(estimate),
    ratio_low  = exp(lower.CL),
    ratio_high = exp(upper.CL)
  )

ratio_E_over_C
##   contrast   estimate        SE df lower.CL upper.CL     ratio  ratio_low
## 1    C - E -0.8996436 0.9315278  4 -3.48598 1.686692 0.4067146 0.03062375
##   ratio_high
## 1   5.401585
# Interpretation:
#   ratio > 1  => E has higher CO2/CH4 than C (less CH4 per unit CO2)
#   ratio < 1  => E has lower CO2/CH4 than C (more CH4 per unit CO2)


# ------------------------------------------------------------
# Optional: also report pile means on the RESPONSE scale
# ------------------------------------------------------------
# These are the estimated mean CO2/CH4 ratios (not contrasts)
emm_resp <- emmeans(m_ratio, ~ Pile, type = "response")
emm_resp
##  Pile emmean    SE df lower.CL upper.CL
##  C      4.73 0.630  5     3.11     6.35
##  E      5.63 0.686  4     3.72     7.53
## 
## Degrees-of-freedom method: containment 
## Confidence level used: 0.95
confint(emm_resp)
##  Pile emmean    SE df lower.CL upper.CL
##  C      4.73 0.630  5     3.11     6.35
##  E      5.63 0.686  4     3.72     7.53
## 
## Degrees-of-freedom method: containment 
## Confidence level used: 0.95
# -----------------------------------------
# 5) Optional: quick diagnostic plots
# -----------------------------------------
plot(m_ratio)                 # residuals vs fitted

qqnorm(resid(m_ratio))
qqline(resid(m_ratio))

We do not have to use specific figures we can edit this, what Zaie has done on the ratios is similiar. There is not a statistical difference in the ratio of co2 to ch4 throughout the whole experimental cycle as there just are not enough df and there is too much variance, what we do see if a large effect size for the whole experiment.

Still A negative coefficient for PileE in a model of log(CO₂ / CH₄) means that the experimental pile has a lower CO₂/CH₄ ratio than the control pile,

Pile E has ~59% lower CO₂/CH₄ than pile C

Therefore:

More CH₄ per unit CO₂

Greater relative anaerobic (methanogenic) contribution

which in turn means more CH₄ emitted per unit CO₂ in the experimental pile. If you look at the end where the piles seem to stablize there are stat significant results but that is cherry picking times ?