Method: Equivalent Soil Mass (ESM) – Haden, von Haden, Yang & DeLucia (2020)
Compliance: VM0042 v2.2 / VCS v4.7
Both management zones processed: Project (WP) and Control (BSL)

Key design features (v1.8):

  • Auto-detected ESM depths: Reference depths are derived from the unique Lower_cm values present in each sheet — works for single-layer (0–30 cm) and multi-increment data (e.g. 0–15, 15–30 cm or 0–10, 10–20, 20–30 cm) with no configuration change required.
  • Group-level (project-wide) uncertainty deduction: VM0042 Eq. 74 uncertainty is pooled across all PAIs for each Zone x Year combination and that single group UNC_fraction is applied uniformly when deducting from every individual PAI’s area-weighted SOC stock. Per-PAI UNC_fraction is still reported for transparency but is not used in the deduction — the group value is.

Output workbook (8 sheets):

Sheet VM0042 Reference Contents
README Version, method, assumptions, unit notes
EQ_REF Eqs. 37-47, 74-79 Equation reference for IQC/VVB auditing
RUN_LOG Processing status per PAI x Year x Zone
ALL_FD Combined fixed-depth data, both zones
ALL_ESM Combined ESM-corrected data, both zones
SUMMARY_SOC Eqs. 46/47 inputs Mean / n / SD of ESM SOC (t CO2e/ha) per PAI x Year x Zone x Strata
AREA_WT_SOC Eqs. 74, 46/47 Area-weighted SOC per PAI x Year x Zone; PAI-level and group-level UNC both reported; group UNC used for deduction
DELTA_NET_SOC Eqs. 37, 40, 44-47, 74 Net SOC removal (CR) and reduction (ER) per PAI using group UNC deduction

1 Section 0: Configuration

# ==============================================================================
# USER INPUTS -- edit only this chunk
# ==============================================================================

input_file_name        <- "4842_SOC_data_for_ESM_V1_0.xlsx"   ## << INPUT >>

# ESM reference depths are now AUTO-DETECTED from each sheet's Lower_cm values.
# Set esm_depths_override to NULL for auto-detection (recommended).
# To force fixed depths for ALL sheets set a numeric vector, e.g. c(0, 15, 30).
esm_depths_override    <- NULL   ## << INPUT >> NULL = auto-detect per sheet

min_core_length_cm     <- 0      ## minimum core depth (cm)
extrapolation          <- TRUE   ## spline extrapolation?
year_col_name          <- "Year" ## column holding sampling year
project_zone_value     <- "project"   ## scenario filter -- WP zone
control_zone_value     <- "control"   ## scenario filter -- BSL zone

# VM0042 Eq.74 parameters
# ASSUMPTION A1: t0.667 = 0.4307 (one-sided t at 66.7% CI, large-n limit, VM0042).
t0667 <- 0.4307   ## << INPUT >>

# VM0042 Eq.46/47: verification period denominator
# ASSUMPTION A2: verif_period_yrs derived from min(Year) and each monitoring year.
# Set to NA to auto-derive; set an integer (e.g. 5) to override.
verif_period_override <- NA   ## << INPUT >> NA = auto, or integer override

# ==============================================================================
# FIXED CONSTANTS (VM0042 / IPCC / NIST)
# ==============================================================================
# ASSUMPTION A3: Strata_area values in the SOC file are in ACRES (US/CA practice).
ACRES_TO_HA   <- 1 / 2.47105381   # NIST exact
# ASSUMPTION A4: ESM Cum_SOC_g_cm2 at max depth * 100 = t C/ha.
G_CM2_TO_T_HA <- 100
# ASSUMPTION A5: C to CO2e conversion = 44/12 (molar mass ratio CO2/C).
C_TO_CO2e     <- 44 / 12

# ==============================================================================
# PATH RESOLUTION
# ==============================================================================
script_dir <- tryCatch(
  dirname(rstudioapi::getSourceEditorContext()$path),
  error = function(e) getwd()
)
input_file_path <- file.path(script_dir, input_file_name)
if (!file.exists(input_file_path))
  stop("Cannot find '", input_file_name, "' in:\n  ", script_dir)

run_stamp       <- format(Sys.time(), "%Y%m%d_%H%M%S")
output_filename <- file.path(script_dir,
  paste0("ESM_SOC_ALL_PAIs_", run_stamp, ".xlsx"))

cat("Input :", input_file_path,  "\n")
## Input : C:/Users/Quan/Carbon Friendly/Carbon Friendly - Verra/Project 4842 Sentry Ag Services Carbon Project/Validation/DATA/4842_SOC_data_for_ESM_V1_0.xlsx
cat("Output:", output_filename,  "\n")
## Output: C:/Users/Quan/Carbon Friendly/Carbon Friendly - Verra/Project 4842 Sentry Ag Services Carbon Project/Validation/DATA/ESM_SOC_ALL_PAIs_20260402_185811.xlsx
cat("Run   :", format(Sys.time(), "%Y-%m-%d %H:%M:%S %Z"), "\n")
## Run   : 2026-04-02 18:58:11 AEST
cat("ESM depths: ", if (is.null(esm_depths_override)) "AUTO-DETECT per sheet" else
      paste(esm_depths_override, collapse=", "), "\n")
## ESM depths:  AUTO-DETECT per sheet

2 Section 1: Sheet Discovery

all_sheets <- readxl::excel_sheets(input_file_path)
pai_sheets <- all_sheets[grepl("PAI", all_sheets, ignore.case = TRUE)]
if (length(pai_sheets) == 0)
  stop("No PAI sheets found in: ", input_file_name)
cat("PAI sheets:", paste(pai_sheets, collapse = ", "), "\n")
## PAI sheets: PAI3, PAI5, PAI9, PAI11, PAI15, PAI16

3 Section 2: Helpers

# ==============================================================================
# detect_esm_depths(): derive ESM reference depths from observed Lower_cm values
#
# The input file stores depths inverted: Lower_cm = surface (0), Upper_cm = bottom.
# After prepare_columns() swaps them the correct depths are in Lower_cm.
# This helper inspects the *post-swap* data frame and returns c(0, unique Lower_cm).
# Examples:
#   Single-layer 0-30 cm  -> c(0, 30)
#   Two increments         -> c(0, 15, 30)
#   Three increments       -> c(0, 10, 20, 30)
# ==============================================================================
detect_esm_depths <- function(df) {
  # df has already been through prepare_columns() so Lower_cm is the TRUE bottom depth
  bottoms <- sort(unique(na.omit(as.numeric(df[["Lower_cm"]]))))
  bottoms <- bottoms[bottoms > 0]
  if (length(bottoms) == 0) stop("No positive Lower_cm values found; cannot detect ESM depths.")
  c(0, bottoms)
}

# ==============================================================================
# prepare_columns(): rename and depth-swap for V1_0 input file
#
# Column mapping (V1_0 file convention):
#   Sample ID      -> ID       (sample identifier)
#   Rep_ID         -> Ref_ID   (ESM reference profile)
#   Rep_old/Rep.1  -> Rep      (analytical replicate 1-6, only if present)
#   Lower_cm (=0)  -> Upper_cm (surface; file stores surface depth as Lower_cm=0)
#   Upper_cm (=30) -> Lower_cm (core bottom; file stores bottom depth as Upper_cm=30)
#
# NOTE: 'Rep' is only removed when a renamed source (Rep_old / Rep.1) has already
# been promoted into that slot.  The V1_0 file has a plain 'Rep' column which
# must be preserved as-is.
# ==============================================================================
prepare_columns <- function(df) {
  nms <- names(df)

  # Step 1: promote Rep_old or Rep.1 -> Rep (only if those columns exist)
  rep_src <- if ("Rep_old" %in% nms) "Rep_old" else
             if ("Rep.1"   %in% nms) "Rep.1"   else NA_character_
  if (!is.na(rep_src)) {
    nms[nms == rep_src] <- "Rep_ESM"
    names(df) <- nms; nms <- names(df)
    # Remove the original 'Rep' (FD rep) only when we have a replacement
    if ("Rep" %in% nms) { df <- df[, nms != "Rep", drop = FALSE]; nms <- names(df) }
    nms[nms == "Rep_ESM"] <- "Rep"
    names(df) <- nms; nms <- names(df)
  }
  # If no Rep_old / Rep.1 exists, leave 'Rep' untouched

  # Step 2: depth swap via temp name
  #   V1_0 stores: Lower_cm = 0 (surface), Upper_cm = 30 (bottom) -- inverted
  #   ESM expects: Upper_cm = 0 (surface), Lower_cm = 30 (bottom)
  if (all(c("Upper_cm", "Lower_cm") %in% nms)) {
    nms[nms == "Lower_cm"] <- "_uc_tmp"
    nms[nms == "Upper_cm"] <- "Lower_cm"
    nms[nms == "_uc_tmp"]  <- "Upper_cm"
    names(df) <- nms; nms <- names(df)
  }

  # Step 3: rename ID columns
  nms[nms == "Sample ID"] <- "ID"
  nms[nms == "Rep_ID"]    <- "Ref_ID"
  names(df) <- nms
  df
}

# Safe NULL fallback: returns a if non-NULL and non-zero-length, else b.
# Deliberately avoids is.na() which returns logical(0) on lists/Style objects.
null_val <- function(a, b) if (!is.null(a) && length(a) > 0) a else b

# Spline: Hyman monotone spline with linear approx fallback
spline_safe <- function(x, y, xout) {
  tryCatch(
    spline(x = x, y = y, xout = xout, method = "hyman")$y,
    error = function(e) approx(x = x, y = y, xout = xout, rule = 2)$y
  )
}

# Pooled area-weighted variance of the mean:
#   Var_aw_mean = SUM_s [ (A_s / A_tot)^2 * SD_s^2 / n_s ]
pooled_var_aw <- function(A, s2, n) {
  A_tot <- sum(A, na.rm = TRUE)
  if (A_tot == 0) return(NA_real_)
  w <- A / A_tot
  sum(w^2 * s2 / pmax(n, 1L), na.rm = TRUE)
}

# VM0042 Eq.74: UNC = min(1, sqrt(pooled_var) / |mean| * t0.667)
eq74_unc <- function(pooled_var, mean_soc, t = t0667) {
  if (is.na(pooled_var) || is.na(mean_soc) || mean_soc == 0) return(NA_real_)
  min(1, sqrt(pooled_var) / abs(mean_soc) * t)
}

4 Section 3: ESM Calculation Loop (Both Zones)

req_cols <- c("ID","Ref_ID","Rep","Upper_cm","Lower_cm",
              "SOC_pct","SOM_pct","BD_g_cm3")

# Output containers
all_fd_rows  <- list()
all_esm_rows <- list()
esm_samps    <- list()   # per-sample ESM SOC at max depth: both zones
strata_reg   <- list()   # pai_id -> data.frame(Strata, Strata_area_ha)
run_log      <- list()
sheet_depth_log <- list()  # record detected depths per sheet

for (sheet_name in pai_sheets) {

  cat("\n\n---\n\n## PAI:", sheet_name, "\n\n")

  # -- 1. Read -------------------------------------------------------------
  raw_df <- tryCatch(
    suppressMessages(readxl::read_excel(input_file_path, sheet = sheet_name)),
    error = function(e) {
      cat("::: {.warn-box}\nSKIPPED:", sheet_name, "--", conditionMessage(e), "\n:::\n")
      NULL })
  if (is.null(raw_df) || nrow(raw_df) == 0) {
    cat("::: {.warn-box}\nSKIPPED: empty\n:::\n"); next }
  names(raw_df) <- trimws(names(raw_df))

  # -- 2. PAI ID -----------------------------------------------------------
  pai_id <- if ("PAI ID" %in% names(raw_df)) {
    v <- unique(na.omit(as.character(raw_df[["PAI ID"]])))
    if (length(v) == 1L) v else sheet_name
  } else sheet_name
  cat("**PAI ID:**", pai_id, "  \n")

  # -- 3. Identify metadata columns BEFORE rename --------------------------
  scen_col   <- names(raw_df)[grepl("scenario|management.*zone",
                                    tolower(names(raw_df)))][1]
  strata_col <- names(raw_df)[grepl("^strata$|^strata_id$",
                                    tolower(trimws(names(raw_df))))][1]
  area_col   <- names(raw_df)[grepl("strata_area", tolower(names(raw_df)))][1]
  for (v in c("scen_col","strata_col","area_col")) {
    val <- get(v)
    if (length(val) == 0 || is.na(val)) assign(v, NA_character_)
  }
  cat("**Scenario col:**", scen_col,
      "| **Strata col:**", strata_col,
      "| **Area col:**", area_col, "  \n")

  # -- 4. Strata area registry (Project rows only, acres -> ha) ------------
  if (!is.na(strata_col) && !is.na(area_col) && !is.na(scen_col)) {
    src <- raw_df[grepl(paste0("^", project_zone_value, "$"),
                        tolower(trimws(as.character(raw_df[[scen_col]]))),
                        ignore.case = TRUE), ]
    area_num <- suppressWarnings(as.numeric(src[[area_col]]))
    sadf <- data.frame(Strata = as.character(src[[strata_col]]),
                       Strata_area_ha = area_num * ACRES_TO_HA,
                       stringsAsFactors = FALSE) |>
      dplyr::group_by(Strata) |>
      dplyr::summarise(Strata_area_ha = dplyr::first(Strata_area_ha), .groups = "drop") |>
      dplyr::filter(!is.na(Strata_area_ha))
    strata_reg[[pai_id]] <- sadf
    cat("**Strata areas (ha):**",
        paste(sadf$Strata, "=", round(sadf$Strata_area_ha, 1), collapse = " | "), "  \n")
  }

  # -- 5. Strata lookup: Sample_ID -> Strata (pre-rename column names) -----
  strata_lkp <- list()
  if (!is.na(strata_col) && "Sample ID" %in% names(raw_df)) {
    tmp <- raw_df[!duplicated(raw_df[["Sample ID"]]), c("Sample ID", strata_col)]
    for (i in seq_len(nrow(tmp)))
      strata_lkp[[as.character(tmp[["Sample ID"]][i])]] <- as.character(tmp[[strata_col]][i])
  }

  # -- 6. Prepare columns (rename + depth swap) ----------------------------
  raw_df <- prepare_columns(raw_df)

  # -- 7. Coerce numerics --------------------------------------------------
  for (nc in c("Upper_cm","Lower_cm","SOC_pct","SOM_pct","BD_g_cm3","Rep")) {
    if (nc %in% names(raw_df))
      raw_df[[nc]] <- suppressWarnings(as.numeric(raw_df[[nc]]))
  }

  miss <- req_cols[!req_cols %in% names(raw_df)]
  if (length(miss) > 0) {
    cat("::: {.warn-box}\nSKIPPED -- missing cols:", paste(miss, collapse = ", "), "\n:::\n")
    next
  }

  # -- 8. AUTO-DETECT ESM depths for this sheet ----------------------------
  # Use override if supplied, otherwise detect from observed Lower_cm values.
  # Detected depths = c(0, <all unique positive Lower_cm values>).
  # Examples: single 0-30 layer -> c(0,30); two increments -> c(0,15,30), etc.
  ESM_depths_cm <- if (!is.null(esm_depths_override)) {
    esm_depths_override
  } else {
    detect_esm_depths(raw_df)
  }
  max_depth_cm  <- max(ESM_depths_cm)
  n_increments  <- length(ESM_depths_cm) - 1L   # number of depth intervals

  cat("**ESM depths (cm):**", paste(ESM_depths_cm, collapse = " -> "),
      paste0("(", n_increments, " increment", if(n_increments!=1) "s" else "", ")"), "  \n")

  sheet_depth_log[[sheet_name]] <- data.frame(
    Sheet       = sheet_name,
    PAI_ID      = pai_id,
    ESM_depths  = paste(ESM_depths_cm, collapse = ","),
    N_increments = n_increments,
    stringsAsFactors = FALSE
  )

  # Depth sanity check (Upper < Lower for all rows)
  depth_ok <- all(raw_df$Upper_cm < raw_df$Lower_cm, na.rm = TRUE)
  cat("**Depth check:** Upper =",
      paste(sort(unique(na.omit(raw_df$Upper_cm))), collapse = ","),
      "| Lower =",
      paste(sort(unique(na.omit(raw_df$Lower_cm))), collapse = ","),
      if (depth_ok) "-- OK  \n" else "-- WARNING (Upper >= Lower found)  \n")

  # -- 9. Years ------------------------------------------------------------
  if (year_col_name %in% names(raw_df)) {
    raw_df[[year_col_name]] <- suppressWarnings(as.integer(raw_df[[year_col_name]]))
    sampling_years <- sort(unique(na.omit(raw_df[[year_col_name]])))
  } else {
    sampling_years <- NA_integer_
  }
  cat("**Years:**", paste(sampling_years, collapse = ", "), "  \n\n")

  # -- 10. Year loop -------------------------------------------------------
  for (yr in sampling_years) {
    yr_label <- if (!is.na(yr)) as.character(yr) else "All"
    year_df  <- if (!is.na(yr))
      raw_df[!is.na(raw_df[[year_col_name]]) & raw_df[[year_col_name]] == yr, ]
    else raw_df

    cat("### Year:", yr_label, "\n\n")

    # -- 11. Zone loop (Project AND Control) --------------------------------
    for (zone_val in c(project_zone_value, control_zone_value)) {

      zone_label <- tools::toTitleCase(zone_val)

      zone_df <- if (!is.na(scen_col) && scen_col %in% names(year_df)) {
        year_df[grepl(paste0("^", zone_val, "$"),
                      tolower(trimws(as.character(year_df[[scen_col]]))),
                      ignore.case = TRUE), ]
      } else {
        if (zone_val == project_zone_value) year_df else data.frame()
      }

      if (nrow(zone_df) == 0) {
        cat("  Zone:", zone_label, "-- no rows, skipped  \n"); next
      }

      fd_core <- zone_df[, req_cols, drop = FALSE]
      v_errs  <- character(0)
      if (anyNA(fd_core$ID) || anyNA(fd_core$Ref_ID) || anyNA(fd_core$Rep))
        v_errs <- c(v_errs, "NA in ID/Ref_ID/Rep")
      if (anyNA(fd_core$Upper_cm) || anyNA(fd_core$Lower_cm))
        v_errs <- c(v_errs, "NA in depths")
      if (length(v_errs) > 0) {
        cat("  Zone:", zone_label, "-- skipped:", paste(v_errs, collapse = "; "), "  \n")
        run_log[[paste0(sheet_name,"_",yr_label,"_",zone_label)]] <- data.frame(
          PAI_ID=pai_id, Sheet=sheet_name, Year=yr_label, Zone=zone_label,
          Status="SKIPPED", Notes=paste(v_errs, collapse="; "),
          Rows_in=nrow(zone_df), FD_records=0L, ESM_records=0L,
          ESM_depths=paste(ESM_depths_cm, collapse=","),
          stringsAsFactors=FALSE)
        next
      }

      # -- Fixed-depth pre-processing -------------------------------------
      fd_work      <- fd_core
      fd_work$Type <- "FD"
      fd_work      <- fd_work[, c("Type","ID","Rep","Ref_ID",
                                   "Upper_cm","Lower_cm",
                                   "SOC_pct","SOM_pct","BD_g_cm3")]

      # Keep only profiles starting at surface (Upper_cm == 0)
      filtered_FD <- fd_work |>
        dplyr::group_by(ID, Rep) |>
        dplyr::filter(min(Upper_cm, na.rm = TRUE) == 0) |>
        dplyr::ungroup() |>
        tidyr::drop_na() |>
        dplyr::arrange(ID, Rep, Upper_cm, Lower_cm)

      # Handle non-contiguous depth sequences: keep only the contiguous
      # leading section up to the first gap
      all_contig <- filtered_FD |>
        dplyr::group_by(ID, Rep) |>
        dplyr::filter(all(Upper_cm == dplyr::lag(Lower_cm, default = 0))) |>
        dplyr::ungroup()
      noncontig <- filtered_FD |>
        dplyr::group_by(ID, Rep) |>
        dplyr::filter(any(Upper_cm != dplyr::lag(Lower_cm, default = 0))) |>
        dplyr::ungroup()
      rmv_nc <- noncontig |>
        dplyr::group_by(ID, Rep) |>
        dplyr::filter(Upper_cm < Upper_cm[
          which(Upper_cm != dplyr::lag(Lower_cm, default = 0))[1]]) |>
        dplyr::ungroup()

      filtered_FD <- dplyr::bind_rows(all_contig, rmv_nc) |>
        dplyr::group_by(ID, Rep) |>
        dplyr::filter(max(Lower_cm, na.rm = TRUE) >= min_core_length_cm) |>
        dplyr::ungroup()

      if (nrow(filtered_FD) == 0) {
        cat("  Zone:", zone_label, "-- no rows after FD filtering  \n"); next
      }

      # Prepend synthetic zero row for each (ID, Ref_ID, Rep)
      zero_rows <- filtered_FD |>
        dplyr::distinct(ID, Ref_ID, Rep) |>
        dplyr::mutate(Type="FD", Upper_cm=0, Lower_cm=0,
                      SOC_pct=0, SOM_pct=0, BD_g_cm3=0)

      modified_FD <- dplyr::bind_rows(zero_rows, filtered_FD) |>
        dplyr::arrange(ID, Ref_ID, Rep, Upper_cm, Lower_cm) |>
        dplyr::mutate(
          Soil_g_cm2     = (Lower_cm - Upper_cm) * BD_g_cm3,
          SOC_g_cm2      = (SOC_pct  / 100) * Soil_g_cm2,
          SOM_g_cm2      = (SOM_pct  / 100) * Soil_g_cm2,
          Min_Soil_g_cm2 = Soil_g_cm2 - SOM_g_cm2
        )

      cumulative_FD <- modified_FD |>
        dplyr::group_by(ID, Rep) |>
        dplyr::mutate(
          Cum_Soil_g_cm2     = cumsum(Soil_g_cm2),
          Cum_SOC_g_cm2      = cumsum(SOC_g_cm2),
          Cum_SOM_g_cm2      = cumsum(SOM_g_cm2),
          Cum_Min_Soil_g_cm2 = cumsum(Min_Soil_g_cm2)
        ) |>
        dplyr::ungroup()

      # -- ESM calculation ------------------------------------------------
      # For each (sample, rep):
      #   1. Build mean reference cumulative mass profile at ESM_depths_cm
      #      (average across all reps of the reference sample at each depth).
      #   2. Spline-interpolate the sample's cumulative SOC / SOM onto those
      #      reference masses (mass-for-mass equivalence).
      #   3. Store the ESM SOC at the deepest reference depth.
      cumulative_ESM  <- data.frame()
      distinct_combos <- dplyr::distinct(cumulative_FD, ID, Ref_ID, Rep)
      n_skip          <- 0L

      for (i in seq_len(nrow(distinct_combos))) {
        cv   <- distinct_combos[i, ]
        crep <- dplyr::filter(cumulative_FD,
                               ID == cv$ID, Ref_ID == cv$Ref_ID, Rep == cv$Rep)

        # Reference profile: mean cumulative min-soil mass across reps at each
        # ESM depth node.  Filter to nodes that are within the observed depth range.
        cref_mean <- cumulative_FD |>
          dplyr::filter(ID == cv$Ref_ID, Lower_cm %in% ESM_depths_cm) |>
          dplyr::group_by(Upper_cm, Lower_cm) |>
          dplyr::summarise(Cum_Min_Soil_g_cm2 = mean(Cum_Min_Soil_g_cm2, na.rm = TRUE),
                           .groups = "drop") |>
          dplyr::mutate(Type="ESM", ID=cv$ID, Ref_ID=cv$Ref_ID, Rep=cv$Rep,
                        Soil_g_cm2=NA_real_, SOC_g_cm2=NA_real_,
                        SOM_g_cm2=NA_real_,  Min_Soil_g_cm2=NA_real_,
                        BD_g_cm3=NA_real_,   SOC_pct=NA_real_, SOM_pct=NA_real_,
                        Cum_Soil_g_cm2=NA_real_, Cum_SOC_g_cm2=NA_real_,
                        Cum_SOM_g_cm2=NA_real_)

        # Restrict reference nodes to within the measured depth / mass range
        cref_filt <- if (!extrapolation)
          cref_mean[cref_mean$Cum_Min_Soil_g_cm2 <=
                      max(crep$Cum_Min_Soil_g_cm2, na.rm = TRUE), ]
        else
          cref_mean[cref_mean$Lower_cm <= max(crep$Lower_cm, na.rm = TRUE), ]

        # Require at least 2 reference nodes and 2 sample points for interpolation
        if (nrow(cref_filt) < 2 || nrow(crep) < 2) { n_skip <- n_skip + 1L; next }

        # Spline: sample's cumulative mass (x) -> cumulative SOC/SOM (y)
        # evaluated at each reference mass node
        cref_filt$Cum_SOC_g_cm2 <- spline_safe(
          crep$Cum_Min_Soil_g_cm2, crep$Cum_SOC_g_cm2, cref_filt$Cum_Min_Soil_g_cm2)
        cref_filt$Cum_SOM_g_cm2 <- spline_safe(
          crep$Cum_Min_Soil_g_cm2, crep$Cum_SOM_g_cm2, cref_filt$Cum_Min_Soil_g_cm2)

        # Back-calculate layer properties from cumulative ESM values
        cref_final <- cref_filt |>
          dplyr::arrange(Lower_cm) |>
          dplyr::mutate(
            Min_Soil_g_cm2 = Cum_Min_Soil_g_cm2 - dplyr::lag(Cum_Min_Soil_g_cm2, default=0),
            SOC_g_cm2      = Cum_SOC_g_cm2      - dplyr::lag(Cum_SOC_g_cm2,      default=0),
            SOM_g_cm2      = Cum_SOM_g_cm2      - dplyr::lag(Cum_SOM_g_cm2,      default=0),
            Soil_g_cm2     = Min_Soil_g_cm2 + SOM_g_cm2,
            BD_g_cm3       = dplyr::if_else((Lower_cm - Upper_cm) > 0,
                               Soil_g_cm2 / (Lower_cm - Upper_cm), NA_real_),
            SOC_pct        = dplyr::if_else(Soil_g_cm2 > 0,
                               SOC_g_cm2 / Soil_g_cm2 * 100, NA_real_),
            SOM_pct        = dplyr::if_else(Soil_g_cm2 > 0,
                               SOM_g_cm2 / Soil_g_cm2 * 100, NA_real_),
            Cum_Soil_g_cm2 = cumsum(Soil_g_cm2)
          )

        cumulative_ESM <- dplyr::bind_rows(cumulative_ESM, cref_final)

        # Store per-sample ESM SOC at the deepest reference depth (max_depth_cm)
        # ASSUMPTION A4: Cum_SOC_g_cm2 * G_CM2_TO_T_HA = t C/ha; * C_TO_CO2e = t CO2e/ha
        esm_at_max <- cref_final[cref_final$Lower_cm == max_depth_cm, ]
        if (nrow(esm_at_max) > 0) {
          cum_soc <- esm_at_max$Cum_SOC_g_cm2[1]
          esm_samps[[length(esm_samps) + 1L]] <- data.frame(
            PAI_ID             = pai_id,
            Year               = yr_label,
            Zone               = zone_label,
            Sample_ID          = as.character(cv$ID),
            Ref_ID             = as.character(cv$Ref_ID),
            Rep                = cv$Rep,
            Strata             = null_val(strata_lkp[[as.character(cv$ID)]], "Unknown"),
            ESM_max_depth_cm   = max_depth_cm,
            Cum_SOC_g_cm2      = cum_soc,
            SOC_tC_ha          = cum_soc * G_CM2_TO_T_HA,
            SOC_tCO2e_ha       = cum_soc * G_CM2_TO_T_HA * C_TO_CO2e,
            SOC_pct_ESM        = esm_at_max$SOC_pct[1],
            stringsAsFactors   = FALSE
          )
        }
      }  # end ESM sample loop

      # Post-processing: tag with PAI, Year, Zone; drop synthetic zero rows.
      # FIX v1.8 (ALL_FD/ALL_ESM empty): openxlsx::writeData fails on dplyr
      # tibbles carrying residual readxl attributes. Wrapping in as.data.frame()
      # strips all dplyr/tibble class info so writeData writes rows correctly.
      fd_out <- as.data.frame(
        dplyr::mutate(
          dplyr::filter(cumulative_FD, !(Upper_cm == 0 & Lower_cm == 0)),
          PAI_ID = pai_id, Year = yr_label, Zone = zone_label, .before = 1
        )
      )
      if (nrow(cumulative_ESM) > 0) {
        esm_out <- as.data.frame(
          dplyr::mutate(
            dplyr::filter(cumulative_ESM, !(Upper_cm == 0 & Lower_cm == 0)),
            PAI_ID = pai_id, Year = yr_label, Zone = zone_label, .before = 1
          )
        )
      } else {
        esm_out <- data.frame()
      }
      key <- paste0(sheet_name, "_", yr_label, "_", zone_label)
      all_fd_rows[[key]]  <- fd_out
      all_esm_rows[[key]] <- esm_out

      run_log[[key]] <- data.frame(
        PAI_ID=pai_id, Sheet=sheet_name, Year=yr_label, Zone=zone_label,
        Status="OK",
        Notes=if (n_skip > 0) paste0(n_skip, " combos skipped (<2 ref/sample pts)") else "",
        Rows_in=nrow(zone_df), FD_records=nrow(fd_out), ESM_records=nrow(esm_out),
        ESM_depths=paste(ESM_depths_cm, collapse=","),
        stringsAsFactors=FALSE)

      cat("  Zone:", zone_label, "--",
          nrow(fd_out), "FD |", nrow(esm_out), "ESM |",
          "depths:", paste(ESM_depths_cm, collapse="-"),
          if (n_skip > 0) paste0("| ", n_skip, " skipped") else "", "  \n")

    }  # end zone loop
    cat("\n")
  }    # end year loop
}      # end PAI loop

4.1 PAI: PAI3

PAI ID: PAI3
Scenario col: Scenario | Strata col: Strata | Area col: Strata_area
Strata areas (ha): Stratum 0 = 209.2 | Stratum 1 = 328 | Stratum 2 = 270.3
ESM depths (cm): 0 -> 1.03 -> 1.19 -> 1.31571428571429 -> 30 (4 increments)
Depth check: Upper = 0 | Lower = 1.03,1.19,1.31571428571429,30 – OK
Years: 2022, 2024

4.1.1 Year: 2022

Zone: Project – 70 FD | 70 ESM | depths: 0-1.03-1.19-1.31571428571429-30
Zone: Control – 15 FD | 12 ESM | depths: 0-1.03-1.19-1.31571428571429-30 | 3 skipped

4.1.2 Year: 2024

Zone: Project – 70 FD | 70 ESM | depths: 0-1.03-1.19-1.31571428571429-30
Zone: Control – 11 FD | 11 ESM | depths: 0-1.03-1.19-1.31571428571429-30


4.2 PAI: PAI5

PAI ID: PAI5
Scenario col: Management zone | Strata col: Strata_ID | Area col: Strata_area
Strata areas (ha): Stratum 0 = 87.6 | Stratum 1 = 56.3 | Stratum 2 = 105.3
ESM depths (cm): 0 -> 30 (1 increment)
Depth check: Upper = 0 | Lower = 30 – OK
Years: 2022, 2024

4.2.1 Year: 2022

Zone: Project – 30 FD | 30 ESM | depths: 0-30
Zone: Control – 10 FD | 8 ESM | depths: 0-30 | 2 skipped

4.2.2 Year: 2024

Zone: Project – 24 FD | 24 ESM | depths: 0-30
Zone: Control – 10 FD | 8 ESM | depths: 0-30 | 2 skipped


4.3 PAI: PAI9

PAI ID: PAI9
Scenario col: Management zone | Strata col: Strata_ID | Area col: Strata_area
Strata areas (ha): Stratum 0 = 79.8 | Stratum 1 = 97.6 | Stratum 2 = 14.1
ESM depths (cm): 0 -> 30 (1 increment)
Depth check: Upper = 0 | Lower = 30 – OK
Years: 2022, 2024

4.3.1 Year: 2022

Zone: Project – 24 FD | 24 ESM | depths: 0-30
Zone: Control – 6 FD | 4 ESM | depths: 0-30 | 2 skipped

4.3.2 Year: 2024

Zone: Project – 24 FD | 24 ESM | depths: 0-30
Zone: Control – 6 FD | 4 ESM | depths: 0-30 | 2 skipped


4.4 PAI: PAI11

PAI ID: PAI11
Scenario col: Management zone | Strata col: Strata_ID | Area col: Strata_area
Strata areas (ha): Stratum 0 = 132 | Stratum 1 = 100.2 | Stratum 2 = 148.9
ESM depths (cm): 0 -> 30 (1 increment)
Depth check: Upper = 0 | Lower = 30 – OK
Years: 2022, 2024

4.4.1 Year: 2022

Zone: Project – 42 FD | 42 ESM | depths: 0-30
Zone: Control – 6 FD | 4 ESM | depths: 0-30 | 2 skipped

4.4.2 Year: 2024

Zone: Project – 42 FD | 42 ESM | depths: 0-30
Zone: Control – 6 FD | 4 ESM | depths: 0-30 | 2 skipped


4.5 PAI: PAI15

PAI ID: PAI15
Scenario col: Management zone | Strata col: Strata_ID | Area col: Strata_area
Strata areas (ha): Stratum 0 = 83.3 | Stratum 1 = 858.4 | Stratum 2 = 1101.7
ESM depths (cm): 0 -> 30 (1 increment)
Depth check: Upper = 0 | Lower = 30 – OK
Years: 2022, 2024

4.5.1 Year: 2022

Zone: Project – 210 FD | 210 ESM | depths: 0-30
Zone: Control – 6 FD | 4 ESM | depths: 0-30 | 2 skipped

4.5.2 Year: 2024

Zone: Project – 275 FD | 275 ESM | depths: 0-30
Zone: Control – 6 FD | 4 ESM | depths: 0-30 | 2 skipped


4.6 PAI: PAI16

PAI ID: PAI16
Scenario col: Management zone | Strata col: Strata_ID | Area col: Strata_area
Strata areas (ha): Stratum 1 = 38.4 | Stratum 2 = 58.5
ESM depths (cm): 0 -> 30 (1 increment)
Depth check: Upper = 0 | Lower = 30 – OK
Years: 2022, 2024

4.6.1 Year: 2022

Zone: Project – 12 FD | 12 ESM | depths: 0-30
Zone: Control – 8 FD | 4 ESM | depths: 0-30 | 4 skipped

4.6.2 Year: 2024

Zone: Project – 12 FD | 12 ESM | depths: 0-30
Zone: Control – 8 FD | 4 ESM | depths: 0-30 | 4 skipped


5 Section 4: Summary & Area-Weighted Calculations

# ==============================================================================
# Step 1: Combine all per-sample ESM records
# ==============================================================================
df_samps <- if (length(esm_samps) > 0) dplyr::bind_rows(esm_samps) else data.frame()
if (nrow(df_samps) == 0) stop("No ESM sample records produced. Check input data and RUN_LOG.")

# ==============================================================================
# Step 2: Average analytical reps per Sample_ID (anti-pseudoreplication)
# Each physical sample location contributes one value to strata statistics.
# ==============================================================================
df_by_samp <- df_samps |>
  dplyr::group_by(PAI_ID, Year, Zone, Sample_ID, Strata, ESM_max_depth_cm) |>
  dplyr::summarise(
    n_reps       = dplyr::n(),
    SOC_tCO2e_ha = mean(SOC_tCO2e_ha, na.rm = TRUE),
    SOC_tC_ha    = mean(SOC_tC_ha,    na.rm = TRUE),
    SOC_pct_ESM  = mean(SOC_pct_ESM,  na.rm = TRUE),
    .groups = "drop")

# ==============================================================================
# Step 3: Attach project-zone strata areas to sample-level data
# (Control samples receive NA for Strata_area_ha -- used in reporting only)
# ==============================================================================
if (length(strata_reg) > 0) {
  area_lkp <- dplyr::bind_rows(lapply(names(strata_reg), function(p) {
    d <- strata_reg[[p]]; d$PAI_ID <- p; d }))
  df_by_samp <- dplyr::left_join(df_by_samp, area_lkp, by = c("PAI_ID","Strata"))
}

# ==============================================================================
# Step 4: SUMMARY_SOC -- mean / n / SD per PAI x Year x Zone x Strata
# ==============================================================================
summary_soc <- df_by_samp |>
  dplyr::group_by(PAI_ID, Year, Zone, Strata, Strata_area_ha, ESM_max_depth_cm) |>
  dplyr::summarise(
    n_samples         = dplyr::n(),
    Mean_SOC_tCO2e_ha = mean(SOC_tCO2e_ha, na.rm = TRUE),
    SD_SOC_tCO2e_ha   = sd(SOC_tCO2e_ha,   na.rm = TRUE),
    SE_SOC_tCO2e_ha   = SD_SOC_tCO2e_ha / sqrt(n_samples),
    Mean_SOC_tC_ha    = mean(SOC_tC_ha,    na.rm = TRUE),
    SD_SOC_tC_ha      = sd(SOC_tC_ha,      na.rm = TRUE),
    Mean_SOC_pct      = mean(SOC_pct_ESM,  na.rm = TRUE),
    SD_SOC_pct        = sd(SOC_pct_ESM,    na.rm = TRUE),
    .groups = "drop"
  ) |>
  dplyr::arrange(PAI_ID, Year, Zone, Strata)
cat("SUMMARY_SOC rows:", nrow(summary_soc), "\n")
## SUMMARY_SOC rows: 62
# ==============================================================================
# Step 5: AREA_WT_SOC -- area-weighted mean SOC per PAI x Year x Zone
#
# TWO uncertainty estimates are computed and reported:
#
#   PAI_UNC_fraction   : Eq.74 pooled within a single PAI's strata
#                        (for transparency / audit trail only)
#
#   Group_UNC_fraction : Eq.74 pooled across ALL PAIs for the same Year x Zone
#                        (used for the actual deduction in DELTA_NET_SOC)
#
# ASSUMPTION A7: Using the group-level UNC means that uncertainty is assessed
# at the programme level rather than per-parcel, which is more conservative
# and consistent with pooled project-scale reporting under VM0042.
# ==============================================================================

# --- 5a. Per-PAI area-weighted SOC and PAI-level UNC -----------------------
pai_zone_wt <- summary_soc |>
  dplyr::filter(!is.na(Strata_area_ha)) |>
  dplyr::group_by(PAI_ID, Year, Zone) |>
  dplyr::summarise(
    ESM_max_depth_cm     = dplyr::first(ESM_max_depth_cm),
    Total_area_ha        = sum(Strata_area_ha),
    AreaWt_SOC_tCO2e_ha  = sum(Mean_SOC_tCO2e_ha * Strata_area_ha) / sum(Strata_area_ha),
    Pooled_Var_PAI       = pooled_var_aw(
                             Strata_area_ha,
                             ifelse(is.na(SD_SOC_tCO2e_ha), 0, SD_SOC_tCO2e_ha)^2,
                             n_samples),
    n_samples_total      = sum(n_samples),
    n_strata             = dplyr::n(),
    n_strata_no_SD       = sum(is.na(SD_SOC_tCO2e_ha) | n_samples == 1L),
    .groups = "drop"
  ) |>
  dplyr::mutate(
    PAI_UNC_fraction = mapply(eq74_unc, Pooled_Var_PAI, AreaWt_SOC_tCO2e_ha),
    PAI_UNC_pct      = PAI_UNC_fraction * 100,
    Total_SOC_tCO2e  = AreaWt_SOC_tCO2e_ha * Total_area_ha,
    PAI_Notes        = ifelse(n_strata_no_SD > 0,
                         paste0(n_strata_no_SD, " strata n=1 (SD=NA)"), "")
  )

# --- 5b. Group (project-wide) UNC per Year x Zone --------------------------
# Pool variance across all PAIs treating each PAI as a "stratum" in the
# project-level area-weighted mean.
#
# Var_group = SUM_p [ (A_p / A_total)^2 * Var_PAI_p ]
# where Var_PAI_p is the within-PAI pooled variance of the area-weighted mean.
#
# Then: Group_UNC = min(1, sqrt(Var_group) / |mean_group_SOC| * t0.667)
group_unc_tbl <- pai_zone_wt |>
  dplyr::group_by(Year, Zone) |>
  dplyr::summarise(
    Group_Total_area_ha      = sum(Total_area_ha),
    Group_AreaWt_SOC_tCO2e_ha = sum(AreaWt_SOC_tCO2e_ha * Total_area_ha) /
                                  sum(Total_area_ha),
    # Pool PAI-level variances weighted by area^2
    Group_Pooled_Var         = pooled_var_aw(
                                 Total_area_ha,
                                 Pooled_Var_PAI,
                                 rep(1L, dplyr::n())),
    n_PAIs                   = dplyr::n(),
    .groups = "drop"
  ) |>
  dplyr::mutate(
    Group_UNC_fraction = mapply(eq74_unc, Group_Pooled_Var, Group_AreaWt_SOC_tCO2e_ha),
    Group_UNC_pct      = Group_UNC_fraction * 100
  )

# --- 5c. Join group UNC back to per-PAI table --------------------------------
pai_zone_wt <- dplyr::left_join(
  pai_zone_wt,
  dplyr::select(group_unc_tbl, Year, Zone,
                Group_UNC_fraction, Group_UNC_pct,
                Group_Pooled_Var,
                Group_Total_area_ha, Group_AreaWt_SOC_tCO2e_ha),
  by = c("Year","Zone")
) |>
  # Deduct using GROUP uncertainty (not PAI-level)
  dplyr::mutate(
    SOC_net_tCO2e_ha = AreaWt_SOC_tCO2e_ha *
                       (1 - ifelse(is.na(Group_UNC_fraction), 0, Group_UNC_fraction)),
    SOC_net_tCO2e    = SOC_net_tCO2e_ha * Total_area_ha
  )

# --- 5d. PROJECT summary row (one per Year x Zone) --------------------------
proj_level <- pai_zone_wt |>
  dplyr::group_by(Year, Zone) |>
  dplyr::summarise(
    PAI_ID                   = "PROJECT",
    ESM_max_depth_cm         = dplyr::first(ESM_max_depth_cm),
    Total_area_ha            = sum(Total_area_ha),
    AreaWt_SOC_tCO2e_ha      = dplyr::first(Group_AreaWt_SOC_tCO2e_ha),
    Pooled_Var_PAI           = dplyr::first(Group_Pooled_Var),
    n_samples_total          = sum(n_samples_total),
    n_strata                 = sum(n_strata),
    n_strata_no_SD           = sum(n_strata_no_SD),
    PAI_UNC_fraction         = dplyr::first(Group_UNC_fraction),
    PAI_UNC_pct              = dplyr::first(Group_UNC_pct),
    Total_SOC_tCO2e          = AreaWt_SOC_tCO2e_ha * Total_area_ha,
    PAI_Notes                = "PROJECT row: group pooled",
    Group_UNC_fraction       = dplyr::first(Group_UNC_fraction),
    Group_UNC_pct            = dplyr::first(Group_UNC_pct),
    Group_Total_area_ha      = Total_area_ha,
    Group_AreaWt_SOC_tCO2e_ha = AreaWt_SOC_tCO2e_ha,
    SOC_net_tCO2e_ha         = AreaWt_SOC_tCO2e_ha *
                               (1 - ifelse(is.na(dplyr::first(Group_UNC_fraction)), 0,
                                           dplyr::first(Group_UNC_fraction))),
    SOC_net_tCO2e            = SOC_net_tCO2e_ha * Total_area_ha,
    .groups = "drop"
  )

area_wt_soc <- dplyr::bind_rows(pai_zone_wt, proj_level) |>
  dplyr::arrange(Year, Zone, PAI_ID)

cat("AREA_WT_SOC rows:", nrow(area_wt_soc), "\n")
## AREA_WT_SOC rows: 28
# ==============================================================================
# Step 6: DELTA_NET_SOC -- WP minus BSL per PAI per Year
#
# Deduction uses Group_UNC_fraction (same value for all PAIs in same Year x Zone).
# ==============================================================================
delta_net_soc <- if (nrow(area_wt_soc) > 0) {

  aw_pai <- area_wt_soc |>
    dplyr::filter(PAI_ID != "PROJECT") |>
    dplyr::select(PAI_ID, Year, Zone, Total_area_ha,
                  AreaWt_SOC_tCO2e_ha, Group_UNC_fraction, Total_SOC_tCO2e)

  wp  <- aw_pai |> dplyr::filter(Zone == "Project") |>
    dplyr::rename(WP_area_ha   = Total_area_ha,
                  WP_SOC_ha    = AreaWt_SOC_tCO2e_ha,
                  WP_Group_UNC = Group_UNC_fraction,
                  WP_SOC_tCO2e = Total_SOC_tCO2e) |>
    dplyr::select(-Zone)

  bsl <- aw_pai |> dplyr::filter(Zone == "Control") |>
    dplyr::rename(BSL_area_ha   = Total_area_ha,
                  BSL_SOC_ha    = AreaWt_SOC_tCO2e_ha,
                  BSL_Group_UNC = Group_UNC_fraction,
                  BSL_SOC_tCO2e = Total_SOC_tCO2e) |>
    dplyr::select(-Zone)

  delta <- dplyr::left_join(wp, bsl, by = c("PAI_ID","Year")) |>
    dplyr::mutate(
      Year_int      = suppressWarnings(as.integer(Year)),

      # -- Stock comparison at each sampled year (for reporting / audit) ------
      Delta_SOC_ha  = WP_SOC_ha - BSL_SOC_ha,        # t CO2e/ha stock difference
      Net_SOC_tCO2e = Delta_SOC_ha * WP_area_ha,      # t CO2e total stock difference

      # ASSUMPTION A6: Group_UNC_combined = max(WP_Group_UNC, BSL_Group_UNC)
      # Taking the larger of the two zone UNCs is the conservative choice.
      Group_UNC_combined = pmax(ifelse(is.na(WP_Group_UNC), 0, WP_Group_UNC),
                                ifelse(is.na(BSL_Group_UNC), 0, BSL_Group_UNC))
    ) |>
    dplyr::arrange(Year_int, PAI_ID) |>
    dplyr::select(-Year_int)

  # -- Eqs.46/47/44/45/40/37/43: annual-rate calculation per PAI -------------
  # Group by PAI_ID so the t0 stock lookup is PAI-scoped (each PAI's own baseline).
  years_avail <- sort(unique(suppressWarnings(as.integer(delta$Year))))
  t0_yr       <- min(years_avail)

  delta <- delta |>
    dplyr::group_by(PAI_ID) |>
    dplyr::mutate(
      # Monitoring gap from the project baseline year (t0)
      Monitoring_gap_yrs = suppressWarnings(as.integer(Year)) - t0_yr,

      # Eq.46: annual SOC stock change -- BSL (Control zone)  [t CO2e/yr]
      # dC_BSL = (SOC_bsl,t - SOC_bsl,t0) / gap * A_PAI
      dSOC_BSL_ha_yr  = ifelse(Monitoring_gap_yrs > 0,
                          (BSL_SOC_ha - BSL_SOC_ha[Year == as.character(t0_yr)]) /
                            Monitoring_gap_yrs, 0),
      dC_BSL_tCO2e_yr = dSOC_BSL_ha_yr * WP_area_ha,

      # Eq.47: annual SOC stock change -- WP (Project zone)   [t CO2e/yr]
      # dC_WP  = (SOC_wp,t  - SOC_wp,t0)  / gap * A_PAI
      dSOC_WP_ha_yr   = ifelse(Monitoring_gap_yrs > 0,
                          (WP_SOC_ha  - WP_SOC_ha[Year == as.character(t0_yr)]) /
                            Monitoring_gap_yrs, 0),
      dC_WP_tCO2e_yr  = dSOC_WP_ha_yr * WP_area_ha,

      # I_sign (Eqs.44/45): +1 if WP annual rate >= BSL annual rate, -1 otherwise
      # When WP > BSL the UNC deduction reduces both values (conservative);
      # when WP < BSL the sign reverses to add uncertainty to the loss.
      I_sign          = ifelse(dC_WP_tCO2e_yr >= dC_BSL_tCO2e_yr, 1L, -1L),

      # Eq.44: UNC-adjusted annual BSL stock change
      # dC_BSL_unc = dC_BSL * (1 - Group_UNC * I_sign)
      dC_BSL_unc      = dC_BSL_tCO2e_yr * (1 - Group_UNC_combined * I_sign),

      # Eq.45: UNC-adjusted annual WP stock change
      # dC_WP_unc  = dC_WP  * (1 - Group_UNC * I_sign)
      dC_WP_unc       = dC_WP_tCO2e_yr  * (1 - Group_UNC_combined * I_sign),

      # I_cumwp: switches ON (=1) once cumulative UNC-adjusted WP stock > 0
      # (i.e., the project has accumulated a net carbon gain above baseline)
      I_cumwp         = as.integer(cumsum(dC_WP_unc) > 0),

      # Eq.40: Gross carbon removals (t CO2e/yr)
      # CR_t = I_cumwp x (MAX(0, dC_WP_unc) - MAX(0, dC_BSL_unc))
      CR_t_tCO2e      = I_cumwp *
                          (pmax(0, dC_WP_unc) - pmax(0, dC_BSL_unc)),

      # Eq.37: Gross GHG emission reductions -- SOC component only [t CO2e/yr]
      # Non-SOC components (fuel, lime, N2O) = 0; include via VCU model.
      # ER_t = I_cumwp   * (MIN(0,dC_WP_unc) - MIN(0,dC_BSL_unc))
      #      + (1-I_cumwp)* (MIN(0,dC_WP_unc) - MIN(0,dC_BSL_unc)
      #                      + MAX(0,dC_WP_unc) - MAX(0,dC_BSL_unc))
      ER_t_tCO2e      = I_cumwp *
                          (pmin(0, dC_WP_unc) - pmin(0, dC_BSL_unc)) +
                        (1L - I_cumwp) *
                          (pmin(0, dC_WP_unc) - pmin(0, dC_BSL_unc) +
                           pmax(0, dC_WP_unc) - pmax(0, dC_BSL_unc)),

      # Eq.43: Net ERR = ER_NET + CR_NET (leakage = 0; add via VCU model)
      ERR_NET_tCO2e   = ER_t_tCO2e + CR_t_tCO2e
    ) |>
    dplyr::ungroup() |>
    dplyr::arrange(suppressWarnings(as.integer(Year)), PAI_ID)

  delta
} else data.frame()
cat("DELTA_NET_SOC rows:", nrow(delta_net_soc), "\n")
## DELTA_NET_SOC rows: 12
# ==============================================================================
# Step 7: Print group UNC summary
# ==============================================================================
cat("\n--- Group UNC Summary (applied to all PAIs) ---\n")
## 
## --- Group UNC Summary (applied to all PAIs) ---
print(as.data.frame(group_unc_tbl))
##   Year    Zone Group_Total_area_ha Group_AreaWt_SOC_tCO2e_ha Group_Pooled_Var
## 1 2022 Control            3218.101                  218.7796         1.781098
## 2 2022 Project            3769.558                  151.5702        20.246901
## 3 2024 Control            3218.101                  225.0019         3.702000
## 4 2024 Project            3769.558                  197.0401        12.057920
##   n_PAIs Group_UNC_fraction Group_UNC_pct
## 1      6        0.002627314     0.2627314
## 2      6        0.012786167     1.2786167
## 3      6        0.003683043     0.3683043
## 4      6        0.007590257     0.7590257

6 Section 5: Write Excel Workbook

# ==============================================================================
# Styles
# ==============================================================================
wb      <- openxlsx::createWorkbook()
hdr_sty <- openxlsx::createStyle(fontName="Arial", fontSize=10, fontColour="white",
             fgFill="#1F3864", textDecoration="bold", halign="center", wrapText=TRUE)
alt_sty <- openxlsx::createStyle(fgFill="#F2F6FC")
num4_sty <- openxlsx::createStyle(numFmt="0.0000", halign="right", fontName="Arial", fontSize=10)
num2_sty <- openxlsx::createStyle(numFmt="0.00",   halign="right", fontName="Arial", fontSize=10)
num0_sty <- openxlsx::createStyle(numFmt="#,##0",  halign="right", fontName="Arial", fontSize=10)
pct2_sty <- openxlsx::createStyle(numFmt="0.00%",  halign="right", fontName="Arial", fontSize=10)
txt_sty  <- openxlsx::createStyle(fontName="Arial", fontSize=10, halign="left")
ttl_sty  <- openxlsx::createStyle(fontName="Arial", fontSize=13,
             fontColour="#1F3864", textDecoration="bold")
proj_sty <- openxlsx::createStyle(fontName="Arial", fontSize=10,
             fontColour="white", fgFill="#1F3864", textDecoration="bold")
grn_sty  <- openxlsx::createStyle(fontName="Arial", fontSize=10,
             fontColour="white", fgFill="#375623", textDecoration="bold")
red_sty  <- openxlsx::createStyle(fontName="Arial", fontSize=10,
             fontColour="white", fgFill="#C00000", textDecoration="bold")
grp_sty  <- openxlsx::createStyle(fontName="Arial", fontSize=10,   # group UNC highlight
             fontColour="white", fgFill="#7030A0", textDecoration="bold")

write_ws <- function(wb, sname, df, title=NULL,
                     id_cols=3, freeze_col=4, num_fmt=NULL) {
  dr <- if (!is.null(title)) {
    openxlsx::writeData(wb, sname, title, startRow=1, startCol=1)
    openxlsx::addStyle(wb, sname, ttl_sty, rows=1, cols=1)
    3L
  } else 2L

  openxlsx::writeData(wb, sname, df, startRow=dr-1L, startCol=1,
    headerStyle=hdr_sty, rowNames=FALSE,
    borders="all", borderStyle="thin", borderColour="#CCCCCC")
  nr <- nrow(df); nc <- ncol(df)
  data_rows <- dr:(dr + nr - 1L)
  even_rows <- data_rows[seq(2, nr, 2)]
  if (length(even_rows))
    openxlsx::addStyle(wb, sname, alt_sty, rows=even_rows, cols=1:nc,
                       gridExpand=TRUE, stack=TRUE)
  if (id_cols > 0)
    openxlsx::addStyle(wb, sname, txt_sty, rows=data_rows,
                       cols=1:min(id_cols, nc), gridExpand=TRUE, stack=TRUE)
  num_idx <- which(sapply(df, is.numeric))
  for (ci in num_idx) {
    sty <- if (!is.null(num_fmt) && !is.null(num_fmt[[ci]])) num_fmt[[ci]] else num4_sty
    openxlsx::addStyle(wb, sname, sty, rows=data_rows, cols=ci,
                       gridExpand=TRUE, stack=TRUE)
  }
  openxlsx::setColWidths(wb, sname, cols=1:nc,
    widths=c(rep(16, min(id_cols,nc)), rep(14, max(0, nc-id_cols))))
  openxlsx::freezePane(wb, sname, firstActiveRow=dr, firstActiveCol=freeze_col)
}

# Sheet order
snames <- c("README","EQ_REF","RUN_LOG","ALL_FD","ALL_ESM",
            "SUMMARY_SOC","AREA_WT_SOC","DELTA_NET_SOC")
for (sn in snames) openxlsx::addWorksheet(wb, sn, gridLines=FALSE)

# ==============================================================================
# EQ_REF
# ==============================================================================
eq_ref <- data.frame(
  Equation = c("Eq.46","Eq.47","Eq.74","Eq.44","Eq.45",
               "Eq.37","Eq.38","Eq.39","Eq.40","Eq.41","Eq.42","Eq.43",
               "Eq.75","Eq.76","Eq.77","Eq.78","Eq.79"),
  Symbol   = c("dC_BSL,s,t","dC_WP,s,t","UNC_t",
               "dC_BSL_unc,t","dC_WP_unc,t",
               "ER_t","ER_NET,t","LK_ER,t","CR_t","CR_NET,t","LK_CR,t","ERR_NET,t",
               "BuER,t","BuCR,t","VCU_ER,t","VCU_CR,t","VCU_t"),
  VM0042_Section = c("8.2","8.2","8.6","8.2","8.2",
                     "8.5","8.5","8.5","8.5","8.5","8.5","8.5",
                     "8.7","8.7","8.7","8.7","8.7"),
  Formula  = c(
    "dC_BSL = (SOC_BSL_t - SOC_BSL_t-1) / verif_x",
    "dC_WP  = (SOC_WP_t  - SOC_WP_t-1)  / verif_x",
    "UNC = min(1, sqrt(Var_aw) / |mean_SOC| * t_0.667)  [applied at GROUP level in v1.8]",
    "dC_BSL_unc = dC_BSL * (1 - Group_UNC * I_sign)",
    "dC_WP_unc  = dC_WP  * (1 - Group_UNC * I_sign);  I_sign=+1 if dC_WP>=dC_BSL else -1",
    "ER = I_cumwp*(GHG+min(0,dC_WP_unc)-min(0,dC_BSL_unc)) + (1-I_cumwp)*same+max terms",
    "ER_NET = ER - LK_ER",
    "LK_ER  = Leakage * ER / (ER+CR)",
    "CR = I_cumwp * (max(0,dC_WP_unc) - max(0,dC_BSL_unc))",
    "CR_NET = CR - LK_CR",
    "LK_CR  = Leakage * CR / (ER+CR)",
    "ERR_NET = ER_NET + CR_NET",
    "BuER = NPR_pct * [I*(min(0,WP)-min(0,BSL)) + (1-I)*all_terms]",
    "BuCR = NPR_pct * I * (max(0,WP) - max(0,BSL))",
    "VCU_ER = max(0, ER_NET - BuER)",
    "VCU_CR = max(0, CR_NET - BuCR)",
    "VCU = VCU_ER + VCU_CR"
  ),
  Description = c(
    "Annual SOC stock change -- BSL (Control) zone, t CO2e/ha/yr",
    "Annual SOC stock change -- WP  (Project) zone, t CO2e/ha/yr",
    paste0("Uncertainty deduction fraction; pooled at GROUP (all-PAI) level in v1.8; t_0.667=", t0667),
    "BSL SOC change adjusted for group uncertainty (conservative deduction)",
    "WP  SOC change adjusted for group uncertainty (conservative deduction)",
    "Gross GHG emission reductions (SOC + non-SOC components)",
    "Net GHG emission reductions after leakage deduction",
    "Leakage allocated to reductions",
    "Gross carbon removals (WP SOC increase above BSL)",
    "Net carbon removals after leakage deduction",
    "Leakage allocated to removals",
    "Net ERR = net emission reductions + net carbon removals",
    "Buffer pool deduction from reductions (non-permanence risk)",
    "Buffer pool deduction from removals (non-permanence risk)",
    "VCUs from reductions (after buffer)",
    "VCUs from removals (after buffer)",
    "Total annual VCU issuance"
  ),
  R_Object = c("dSOC_BSL_ha_yr / dC_BSL_tCO2e_yr","dSOC_WP_ha_yr / dC_WP_tCO2e_yr",
               "Group_UNC_fraction / Group_UNC_combined",
               "dC_BSL_unc","dC_WP_unc",
               "ER_t_tCO2e (SOC only)","-- see VCU model --",
               "-- see VCU model --","CR_t_tCO2e","-- see VCU model --",
               "-- see VCU model --","ERR_NET_tCO2e",
               "-- see VCU model --","-- see VCU model --",
               "-- see VCU model --","-- see VCU model --","-- see VCU model --"),
  Where_Calculated = c(
    "DELTA_NET_SOC","DELTA_NET_SOC","AREA_WT_SOC (Group_UNC_fraction col)",
    "DELTA_NET_SOC","DELTA_NET_SOC",
    "DELTA_NET_SOC (SOC only)","VCU Model","VCU Model",
    "DELTA_NET_SOC","VCU Model","VCU Model","DELTA_NET_SOC / VCU Model",
    "VCU Model","VCU Model","VCU Model","VCU Model","VCU Model"
  ),
  stringsAsFactors = FALSE
)

openxlsx::writeData(wb, "EQ_REF",
  paste0("VM0042 v2.2 Equation Reference | Project 4842 SAS | ",
         "For IQC / VVB Audit | t0.667=", t0667, " | ACRES_TO_HA=", ACRES_TO_HA,
         " | v1.8: UNC applied at GROUP level"),
  startRow=1, startCol=1)
openxlsx::addStyle(wb, "EQ_REF", ttl_sty, rows=1, cols=1)
openxlsx::writeData(wb, "EQ_REF", eq_ref, startRow=3, startCol=1,
  headerStyle=hdr_sty, rowNames=FALSE,
  borders="all", borderStyle="thin", borderColour="#CCCCCC")
nr_eq <- nrow(eq_ref)
for (row_i in seq_len(nr_eq)) {
  if (row_i %% 2 == 0)
    openxlsx::addStyle(wb, "EQ_REF", alt_sty, rows=row_i+3, cols=1:ncol(eq_ref),
                       gridExpand=TRUE, stack=FALSE)
}
key_eq_rows <- which(eq_ref$Equation %in% c("Eq.46","Eq.47","Eq.74","Eq.43")) + 3L
openxlsx::addStyle(wb, "EQ_REF", proj_sty, rows=key_eq_rows, cols=1:ncol(eq_ref),
                   gridExpand=TRUE, stack=FALSE)
openxlsx::setColWidths(wb, "EQ_REF", cols=1:ncol(eq_ref), widths=c(10,18,14,80,55,25,25))
openxlsx::freezePane(wb, "EQ_REF", firstActiveRow=4, firstActiveCol=1)

# ==============================================================================
# README
# ==============================================================================
run_log_df  <- if (length(run_log)>0) dplyr::bind_rows(run_log) else
               data.frame(Status="none", stringsAsFactors=FALSE)
depth_sum   <- if (length(sheet_depth_log)>0) dplyr::bind_rows(sheet_depth_log) else
               data.frame()
depth_modes <- if (nrow(depth_sum)>0)
               paste(unique(depth_sum$ESM_depths), collapse=" / ") else "n/a"

readme_df <- data.frame(
  Field = c(
    "Project","Script","Version","Method","Reference",
    "Input file","Output file","Run date-time",
    "PAIs","Years","Zones",
    "","Sheets","README","EQ_REF","RUN_LOG","ALL_FD","ALL_ESM",
    "SUMMARY_SOC","AREA_WT_SOC","DELTA_NET_SOC",
    "","Assumptions",
    "A1","A2","A3","A4","A5","A6","A7",
    "","Column mapping (V1_0 file)",
    "Sample ID","Rep_ID","Rep_old/Rep.1","Lower_cm(=0)","Upper_cm(=30)",
    "","ESM depth detection",
    "Mode","Detected depths",
    "","Key VM0042 constants",
    "t0.667","ACRES_TO_HA","G_CM2_TO_T_HA","C_TO_CO2e",
    "","Changes vs v1.7",
    "Change 1","Change 2","Change 3","Change 4","Change 5"
  ),
  Value = c(
    "4842 SAS -- Soil & Agriculture Sustainability",
    "4842_ESM_SOC_ALL_PAIs_v1_8.Rmd","v1.8",
    "Equivalent Soil Mass (ESM) -- Haden et al. 2020",
    "von Haden AC, Yang WH, DeLucia EH (2020). Soil Biology & Biochemistry.",
    input_file_name, basename(output_filename),
    format(Sys.time(), "%Y-%m-%d %H:%M:%S %Z"),
    paste(pai_sheets, collapse=", "),
    paste(unique(run_log_df$Year[run_log_df$Status=="OK"]), collapse=", "),
    "Project (WP) and Control (BSL) -- both processed",
    "","Description",
    "Version, method, assumptions, column mapping",
    "VM0042 v2.2 equation reference for IQC/VVB audit",
    "Processing status per PAI x Year x Zone (includes detected ESM_depths)",
    "Combined fixed-depth data -- both zones",
    "Combined ESM-corrected data -- both zones",
    "Mean / n / SD (t CO2e/ha) + Mean_SOC_pct + SD_SOC_pct per PAI x Year x Zone x Strata",
    paste0("Area-weighted SOC per PAI x Year x Zone; PAI_UNC and Group_UNC both shown;",
           " Group_UNC used for SOC_net deduction"),
    "Net SOC removal (CR) and reduction (ER) per PAI using Group_UNC_combined",
    "","VM0042 v2.2 / VCS v4.7",
    paste0("t0.667=",t0667," (one-sided t at 66.7% CI, large-n limit, VM0042 Eq.74)"),
    "verif_period_yrs: auto-derived from (monitoring year - baseline year)",
    "Strata_area in source file in ACRES; converted using NIST exact 1/2.47105381",
    "Cum_SOC_g_cm2 * 100 = t C/ha (1 g/cm2=10000 g/m2/1e6 g/t*10000 m2/ha=100 t/ha)",
    "t C/ha * (44/12) = t CO2e/ha (IPCC molar mass ratio)",
    "Group_UNC_combined = max(WP_Group_UNC, BSL_Group_UNC) conservatively",
    paste0("Group UNC pooled across all PAIs for same Year x Zone;",
           " applied uniformly to every PAI (not individual PAI UNC)"),
    "","Applied in prepare_columns()",
    "-> ID (sample identifier)",
    "-> Ref_ID (ESM reference profile for spline)",
    "-> Rep (only renamed if Rep_old/Rep.1 present; V1_0 'Rep' kept as-is)",
    "-> Upper_cm = 0 cm surface (file INVERTED; swap applied)",
    "-> Lower_cm = 30 cm bottom (file INVERTED; swap applied)",
    "","",
    if (is.null(esm_depths_override)) "AUTO-DETECT (esm_depths_override = NULL)" else
      paste("OVERRIDE:", paste(esm_depths_override, collapse=",")),
    depth_modes,
    "","",
    as.character(t0667),
    as.character(round(ACRES_TO_HA, 10)),
    as.character(G_CM2_TO_T_HA),
    as.character(round(C_TO_CO2e, 6)),
    "","Relative to v1.5",
    paste0("Auto-detect ESM depths per sheet from unique Lower_cm values;",
           " works for 0-30 single layer and multi-increment profiles (0-15-30, etc.)"),
    paste0("Group (project-wide) UNC pooled across all PAIs and applied uniformly;",
           " per-PAI UNC retained for transparency in AREA_WT_SOC;",
           " DELTA_NET_SOC uses Group_UNC_combined"),
    paste0("FIX v1.8: Wrapped fd_out/esm_out in as.data.frame() to fix openxlsx tibble",
           " write bug; ALL_FD and ALL_ESM now correctly populated"),
    paste0("ADD v1.8: Mean_SOC_pct and SD_SOC_pct columns in SUMMARY_SOC tab",
           " (ESM-corrected SOC percent per stratum per PAI per year)"),
    paste0("FIX v1.8: DELTA_NET_SOC sorted by Year then Zone then PAI_ID",
           " so all BSL/WP rows for a given year appear together")
  ),
  stringsAsFactors = FALSE
)

openxlsx::writeData(wb, "README", readme_df, headerStyle=hdr_sty, rowNames=FALSE)
openxlsx::setColWidths(wb, "README", cols=1:2, widths=c(28, 80))
openxlsx::freezePane(wb, "README", firstActiveRow=2, firstActiveCol=1)

# ==============================================================================
# RUN_LOG
# ==============================================================================
openxlsx::writeData(wb, "RUN_LOG", run_log_df, headerStyle=hdr_sty, rowNames=FALSE)
openxlsx::setColWidths(wb, "RUN_LOG", cols=1:ncol(run_log_df),
                       widths=rep(15, ncol(run_log_df)))
openxlsx::freezePane(wb, "RUN_LOG", firstActiveRow=2, firstActiveCol=1)

# ==============================================================================
# ALL_FD and ALL_ESM
# ==============================================================================
all_fd  <- if (length(all_fd_rows)>0)  as.data.frame(dplyr::bind_rows(all_fd_rows))  else data.frame()
all_esm <- if (length(all_esm_rows)>0) as.data.frame(dplyr::bind_rows(all_esm_rows)) else data.frame()
if (nrow(all_fd)>0)
  write_ws(wb, "ALL_FD",  all_fd,
    title="All PAIs & Years & Zones -- Fixed-Depth Data | 4842 SAS ESM v1.8",
    id_cols=5, freeze_col=6)
if (nrow(all_esm)>0)
  write_ws(wb, "ALL_ESM", all_esm,
    title="All PAIs & Years & Zones -- ESM-Corrected Data | 4842 SAS ESM v1.8",
    id_cols=5, freeze_col=6)
cat("ALL_FD rows:", nrow(all_fd), "| ALL_ESM rows:", nrow(all_esm), "\n")
## ALL_FD rows: 933 | ALL_ESM rows: 906
# ==============================================================================
# SUMMARY_SOC
# ==============================================================================
if (nrow(summary_soc) > 0) {
  nc_s   <- ncol(summary_soc)
  nfmt_s <- vector("list", nc_s)
  for (ci in which(sapply(summary_soc, is.numeric))) nfmt_s[[ci]] <- num2_sty
  n_idx <- which(names(summary_soc) == "n_samples")
  if (length(n_idx)) nfmt_s[[n_idx]] <- num0_sty
  d_idx <- which(names(summary_soc) == "ESM_max_depth_cm")
  if (length(d_idx)) nfmt_s[[d_idx]] <- num0_sty
  pct_idx <- which(names(summary_soc) %in% c("Mean_SOC_pct","SD_SOC_pct"))
  for (ci in pct_idx) nfmt_s[[ci]] <- num4_sty

  write_ws(wb, "SUMMARY_SOC", summary_soc,
    title=paste0("ESM SOC (t CO2e/ha) + SOC% | PAI x Year x Zone x Strata | ",
                 "VM0042 v2.2 Eq.46/47 inputs | Reps averaged per sample before statistics"),
    id_cols=4, freeze_col=5, num_fmt=nfmt_s)
  ctrl_rows <- which(summary_soc$Zone == "Control") + 2L
  ctrl_sty  <- openxlsx::createStyle(fgFill="#FFF2CC", fontName="Arial", fontSize=10)
  if (length(ctrl_rows))
    openxlsx::addStyle(wb, "SUMMARY_SOC", ctrl_sty, rows=ctrl_rows,
                       cols=1:nc_s, gridExpand=TRUE, stack=TRUE)
}
cat("SUMMARY_SOC rows:", nrow(summary_soc), "\n")
## SUMMARY_SOC rows: 62
# ==============================================================================
# AREA_WT_SOC
# Column highlight: Group_UNC columns in purple to distinguish from PAI_UNC
# ==============================================================================
if (nrow(area_wt_soc) > 0) {
  nc_a   <- ncol(area_wt_soc)
  nfmt_a <- vector("list", nc_a)
  for (ci in which(sapply(area_wt_soc, is.numeric))) nfmt_a[[ci]] <- num2_sty
  unc_cols <- which(names(area_wt_soc) %in%
                    c("PAI_UNC_fraction","Group_UNC_fraction",
                      "Pooled_Var_PAI","Group_Pooled_Var"))
  for (ci in unc_cols) nfmt_a[[ci]] <- num4_sty
  pct_cols <- which(names(area_wt_soc) %in% c("PAI_UNC_pct","Group_UNC_pct"))
  for (ci in pct_cols) nfmt_a[[ci]] <- num2_sty
  n_ci <- which(names(area_wt_soc) == "n_samples_total")
  if (length(n_ci)) nfmt_a[[n_ci]] <- num0_sty

  write_ws(wb, "AREA_WT_SOC", area_wt_soc,
    title=paste0("Area-Weighted SOC (t CO2e/ha) & Uncertainty | Both Zones | ",
                 "PAI_UNC = per-parcel (audit reference) | ",
                 "Group_UNC = project-wide pooled (used for deduction) | t0.667=", t0667),
    id_cols=3, freeze_col=4, num_fmt=nfmt_a)

  # Highlight GROUP UNC columns in purple
  grp_unc_col_idx <- which(names(area_wt_soc) %in%
                           c("Group_UNC_fraction","Group_UNC_pct",
                             "Group_Total_area_ha","Group_AreaWt_SOC_tCO2e_ha",
                             "SOC_net_tCO2e_ha","SOC_net_tCO2e"))
  if (length(grp_unc_col_idx)) {
    all_data_rows <- (3):(2 + nrow(area_wt_soc))
    openxlsx::addStyle(wb, "AREA_WT_SOC",
      openxlsx::createStyle(fgFill="#EAD1FF", fontName="Arial", fontSize=10),
      rows=all_data_rows, cols=grp_unc_col_idx,
      gridExpand=TRUE, stack=TRUE)
    # Header row highlight for group cols (row 2 = header when title in row 1)
    openxlsx::addStyle(wb, "AREA_WT_SOC",
      openxlsx::createStyle(fgFill="#7030A0", fontColour="white",
                            fontName="Arial", fontSize=10, textDecoration="bold",
                            halign="center", wrapText=TRUE),
      rows=2, cols=grp_unc_col_idx, gridExpand=TRUE, stack=FALSE)
  }

  # PROJECT rows dark blue
  proj_rows_a <- which(area_wt_soc$PAI_ID == "PROJECT") + 2L
  if (length(proj_rows_a))
    openxlsx::addStyle(wb, "AREA_WT_SOC", proj_sty, rows=proj_rows_a,
                       cols=1:nc_a, gridExpand=TRUE, stack=FALSE)
  # Control rows yellow
  ctrl_rows_a <- which(area_wt_soc$Zone == "Control" &
                       area_wt_soc$PAI_ID != "PROJECT") + 2L
  ctrl_sty2 <- openxlsx::createStyle(fgFill="#FFF2CC", fontName="Arial", fontSize=10)
  if (length(ctrl_rows_a))
    openxlsx::addStyle(wb, "AREA_WT_SOC", ctrl_sty2, rows=ctrl_rows_a,
                       cols=1:nc_a, gridExpand=TRUE, stack=TRUE)
}
cat("AREA_WT_SOC rows:", nrow(area_wt_soc), "\n")
## AREA_WT_SOC rows: 28
# ==============================================================================
# DELTA_NET_SOC
# ==============================================================================
if (nrow(delta_net_soc) > 0) {
  nc_d   <- ncol(delta_net_soc)
  nfmt_d <- vector("list", nc_d)
  for (ci in which(sapply(delta_net_soc, is.numeric))) nfmt_d[[ci]] <- num2_sty
  # 4-dp for UNC fractions
  unc_d <- which(names(delta_net_soc) %in%
                 c("WP_Group_UNC","BSL_Group_UNC","Group_UNC_combined"))
  if (length(unc_d)) for (ci in unc_d) nfmt_d[[ci]] <- num4_sty
  # Integer for gap and indicators
  int_d <- which(names(delta_net_soc) %in% c("Monitoring_gap_yrs","I_sign","I_cumwp"))
  if (length(int_d)) for (ci in int_d) nfmt_d[[ci]] <- num0_sty

  write_ws(wb, "DELTA_NET_SOC", delta_net_soc,
    title=paste0(
      "Net SOC Removal (CR_t) and Reduction (ER_t) per PAI | ",
      "VM0042 v2.2 Eqs.37,40,43,44,45,46,47,74 | ",
      "Annual rates (Eq.46/47) -> UNC-adjusted (Eq.44/45) -> CR/ER/ERR_NET (Eq.40/37/43) | ",
      "Non-SOC GHG (fuel, lime, N2O) = 0 -- add via VCU Model"),
    id_cols=2, freeze_col=3, num_fmt=nfmt_d)

  # Green rows where CR>0 (net removal), red where ER>0 (reduction)
  if ("CR_t_tCO2e" %in% names(delta_net_soc)) {
    cr_rows <- which(!is.na(delta_net_soc$CR_t_tCO2e) &
                      delta_net_soc$CR_t_tCO2e > 0) + 2L
    er_rows <- which(!is.na(delta_net_soc$ER_t_tCO2e) &
                      delta_net_soc$ER_t_tCO2e > 0) + 2L
    if (length(cr_rows))
      openxlsx::addStyle(wb, "DELTA_NET_SOC", grn_sty, rows=cr_rows,
                         cols=1:nc_d, gridExpand=TRUE, stack=FALSE)
    if (length(er_rows))
      openxlsx::addStyle(wb, "DELTA_NET_SOC", red_sty, rows=er_rows,
                         cols=1:nc_d, gridExpand=TRUE, stack=FALSE)
  }
}
cat("DELTA_NET_SOC rows:", nrow(delta_net_soc), "\n")
## DELTA_NET_SOC rows: 12
# ==============================================================================
# SAVE
# ==============================================================================
tryCatch({
  openxlsx::saveWorkbook(wb, output_filename, overwrite=TRUE)
  cat("\n========================================================\n")
  cat("SAVED:", output_filename, "\n")
  cat("Sheets:", paste(openxlsx::sheets(wb), collapse=" | "), "\n")
  cat("========================================================\n")
}, error = function(e) warning("Save failed: ", conditionMessage(e)))
## 
## ========================================================
## SAVED: C:/Users/Quan/Carbon Friendly/Carbon Friendly - Verra/Project 4842 Sentry Ag Services Carbon Project/Validation/DATA/ESM_SOC_ALL_PAIs_20260402_185811.xlsx 
## Sheets: README | EQ_REF | RUN_LOG | ALL_FD | ALL_ESM | SUMMARY_SOC | AREA_WT_SOC | DELTA_NET_SOC 
## ========================================================

7 In-Report Tables

## ### Group (Project-Wide) Uncertainty Applied to All PAIs
Group UNC (VM0042 Eq.74) pooled across all PAIs – this value is applied to every individual PAI in DELTA_NET_SOC
Year Zone Group_Total_area_ha Group_AreaWt_SOC_tCO2e_ha Group_Pooled_Var n_PAIs Group_UNC_fraction Group_UNC_pct
2022 Control 3218.101 218.7796 1.7811 6 0.0026 0.2627
2022 Project 3769.558 151.5702 20.2469 6 0.0128 1.2786
2024 Control 3218.101 225.0019 3.7020 6 0.0037 0.3683
2024 Project 3769.558 197.0401 12.0579 6 0.0076 0.7590
SUMMARY_SOC: ESM SOC (t CO2e/ha) by PAI x Year x Zone x Strata
PAI_ID Year Zone Strata Strata_area_ha ESM_max_depth_cm n_samples Mean_SOC_tCO2e_ha SD_SOC_tCO2e_ha SE_SOC_tCO2e_ha Mean_SOC_tC_ha SD_SOC_tC_ha Mean_SOC_pct SD_SOC_pct
PAI11 2022 Control Stratum 0 132.0 30 1 291.12 NA NA 79.39511 NA 2.0100000 NA
PAI11 2022 Control Stratum 2 148.9 30 1 291.12 NA NA 79.39502 NA 2.0100000 NA
PAI11 2022 Project Stratum 0 132.0 30 5 189.86 70.90 31.71 51.78031 19.337486 1.2520000 0.5333104
PAI11 2022 Project Stratum 1 100.2 30 3 258.45 50.83 29.35 70.48600 13.862739 1.7700000 0.3296968
PAI11 2022 Project Stratum 2 148.9 30 4 188.98 7.56 3.78 51.53938 2.062870 1.2650000 0.0900000
PAI11 2024 Control Stratum 0 132.0 30 1 282.43 NA NA 77.02511 NA 1.9500000 NA
PAI11 2024 Control Stratum 2 148.9 30 1 282.43 NA NA 77.02502 NA 1.9500000 NA
PAI11 2024 Project Stratum 0 132.0 30 5 219.09 90.44 40.44 59.75051 24.664455 1.4560000 0.6577462
PAI11 2024 Project Stratum 1 100.2 30 3 288.06 50.31 29.05 78.56134 13.720416 1.9733333 0.3257811
PAI11 2024 Project Stratum 2 148.9 30 4 230.03 13.62 6.81 62.73463 3.713413 1.5400000 0.1283225
PAI15 2022 Control Stratum 1 858.4 30 1 247.75 NA NA 67.56798 NA 1.6400000 NA
PAI15 2022 Control Stratum 2 1101.7 30 1 247.75 NA NA 67.56806 NA 1.6400000 NA
PAI15 2022 Project Stratum 0 83.3 30 3 168.85 68.84 39.74 46.04900 18.774089 1.0500000 0.4396590
PAI15 2022 Project Stratum 1 858.4 30 20 152.02 48.46 10.84 41.45955 13.216482 0.9535000 0.3328391
PAI15 2022 Project Stratum 2 1101.7 30 27 125.71 50.11 9.64 34.28422 13.665674 0.7792593 0.3578668
PAI15 2024 Control Stratum 1 858.4 30 1 238.69 NA NA 65.09598 NA 1.5800000 NA
PAI15 2024 Control Stratum 2 1101.7 30 1 238.69 NA NA 65.09606 NA 1.5800000 NA
PAI15 2024 Project Stratum 0 83.3 30 7 228.37 45.90 17.35 62.28329 12.517007 1.4671429 0.3015358
PAI15 2024 Project Stratum 1 858.4 30 32 200.14 43.80 7.74 54.58478 11.946430 1.2721875 0.3090228
PAI15 2024 Project Stratum 2 1101.7 30 34 176.65 40.08 6.87 48.17753 10.930268 1.1023529 0.2939600
PAI16 2022 Control Stratum 0 NA 30 1 253.02 NA NA 69.00546 NA 1.5700000 NA
PAI16 2022 Control Stratum 1 38.4 30 1 253.02 NA NA 69.00553 NA 1.5700000 NA
PAI16 2022 Control Stratum 2 58.5 30 1 253.02 NA NA 69.00556 NA 1.5700000 NA
PAI16 2022 Project Stratum 1 38.4 30 2 223.62 15.37 10.87 60.98764 4.192687 1.4400000 0.0989949
PAI16 2022 Project Stratum 2 58.5 30 2 223.62 15.37 10.87 60.98764 4.192687 1.4400000 0.0989949
PAI16 2024 Control Stratum 0 NA 30 1 251.41 NA NA 68.56593 NA 1.5600000 NA
PAI16 2024 Control Stratum 1 38.4 30 1 251.41 NA NA 68.56601 NA 1.5600000 NA
PAI16 2024 Control Stratum 2 58.5 30 1 251.41 NA NA 68.56603 NA 1.5600000 NA
PAI16 2024 Project Stratum 1 38.4 30 2 243.03 34.04 24.07 66.28171 9.283806 1.5650000 0.2192031
PAI16 2024 Project Stratum 2 58.5 30 2 243.03 34.04 24.07 66.28171 9.283806 1.5650000 0.2192031
PAI3 2022 Control Stratum 0 209.2 30 1 126.23 NA NA 34.42501 NA 0.8100000 NA
PAI3 2022 Control Stratum 1 328.0 30 2 139.32 18.52 13.10 37.99650 5.050851 0.8850000 0.1060660
PAI3 2022 Project Stratum 0 209.2 30 5 161.75 23.26 10.40 44.11440 6.342769 1.0320000 0.1583351
PAI3 2022 Project Stratum 1 328.0 30 6 138.13 13.21 5.39 37.67050 3.602834 0.8783333 0.0870441
PAI3 2022 Project Stratum 2 270.3 30 7 176.05 26.33 9.95 48.01457 7.181778 1.1485714 0.1546424
PAI3 2024 Control Stratum 0 209.2 30 1 187.00 NA NA 51.00001 NA 1.2000000 NA
PAI3 2024 Control Stratum 1 328.0 30 2 168.12 26.70 18.88 45.85100 7.281801 1.0700000 0.1838478
PAI3 2024 Project Stratum 0 209.2 30 5 186.54 30.47 13.63 50.87320 8.310263 1.1900000 0.2040833
PAI3 2024 Project Stratum 1 328.0 30 6 161.97 8.82 3.60 44.17233 2.404995 1.0300000 0.0622896
PAI3 2024 Project Stratum 2 270.3 30 7 201.49 23.44 8.86 54.95242 6.392877 1.3157143 0.1387873
PAI5 2022 Control Stratum 0 87.6 30 1 127.16 NA NA 34.67995 NA 0.8000000 NA
PAI5 2022 Control Stratum 1 56.3 30 1 131.74 NA NA 35.92777 NA 0.8600000 NA
PAI5 2022 Control Stratum 2 105.3 30 1 125.14 NA NA 34.12938 NA 0.8600000 NA
PAI5 2022 Project Stratum 0 87.6 30 2 115.72 5.29 3.74 31.56091 1.441884 0.7250000 0.1060660
PAI5 2022 Project Stratum 1 56.3 30 2 92.89 27.00 19.09 25.33384 7.364523 0.5600000 0.1272792
PAI5 2022 Project Stratum 2 105.3 30 3 135.03 13.65 7.88 36.82511 3.723323 0.8533333 0.0472582
PAI5 2024 Control Stratum 0 87.6 30 1 170.08 NA NA 46.38444 NA 1.0700000 NA
PAI5 2024 Control Stratum 1 56.3 30 1 194.54 NA NA 53.05614 NA 1.2700000 NA
PAI5 2024 Control Stratum 2 105.3 30 1 184.80 NA NA 50.40036 NA 1.2700000 NA
PAI5 2024 Project Stratum 0 87.6 30 1 238.92 NA NA 65.16096 NA 1.6000000 NA
PAI5 2024 Project Stratum 1 56.3 30 1 204.12 NA NA 55.66860 NA 1.3000000 NA
PAI5 2024 Project Stratum 2 105.3 30 3 215.65 24.57 14.18 58.81456 6.699599 1.3733333 0.2300725
PAI9 2022 Control Stratum 0 79.8 30 1 88.80 NA NA 24.21884 NA 0.6100000 NA
PAI9 2022 Control Stratum 2 14.1 30 1 88.80 NA NA 24.21885 NA 0.6100000 NA
PAI9 2022 Project Stratum 0 79.8 30 2 171.99 88.61 62.66 46.90727 24.165985 1.1750000 0.7141778
PAI9 2022 Project Stratum 1 97.6 30 4 162.06 58.89 29.45 44.19802 16.061732 1.1150000 0.4670831
PAI9 2022 Project Stratum 2 14.1 30 1 119.27 NA NA 32.52823 NA 0.8000000 NA
PAI9 2024 Control Stratum 0 79.8 30 1 138.30 NA NA 37.71787 NA 0.9500000 NA
PAI9 2024 Control Stratum 2 14.1 30 1 138.30 NA NA 37.71789 NA 0.9500000 NA
PAI9 2024 Project Stratum 0 79.8 30 2 189.97 7.87 5.57 51.81082 2.147498 1.2650000 0.1909188
PAI9 2024 Project Stratum 1 97.6 30 4 225.11 62.20 31.10 61.39231 16.963625 1.5350000 0.4912908
PAI9 2024 Project Stratum 2 14.1 30 1 202.76 NA NA 55.29799 NA 1.3600000 NA
AREA_WT_SOC: Area-Weighted SOC + Uncertainty | Purple = Group UNC columns (used for deduction)
PAI_ID Year Zone ESM_max_depth_cm Total_area_ha AreaWt_SOC_tCO2e_ha Pooled_Var_PAI n_samples_total n_strata n_strata_no_SD PAI_UNC_fraction PAI_UNC_pct Total_SOC_tCO2e PAI_Notes Group_UNC_fraction Group_UNC_pct Group_Pooled_Var Group_Total_area_ha Group_AreaWt_SOC_tCO2e_ha SOC_net_tCO2e_ha SOC_net_tCO2e
PAI11 2022 Control 30 280.9044 291.1152 0.0000 2 2 2 0.0000 0.0000 81775.56 2 strata n=1 (SD=NA) 0.0026 0.2627 1.7811 3218.101 218.7796 290.3504 81560.712
PAI15 2022 Control 30 1960.0504 247.7494 0.0000 2 2 2 0.0000 0.0000 485601.38 2 strata n=1 (SD=NA) 0.0026 0.2627 1.7811 3218.101 218.7796 247.0985 484325.552
PAI16 2022 Control 30 96.8372 253.0203 0.0000 2 2 2 0.0000 0.0000 24501.79 2 strata n=1 (SD=NA) 0.0026 0.2627 1.7811 3218.101 218.7796 252.3556 24437.415
PAI3 2022 Control 30 537.1838 134.2201 63.9207 3 2 1 0.0257 2.5655 72100.84 1 strata n=1 (SD=NA) 0.0026 0.2627 1.7811 3218.101 218.7796 133.8674 71911.412
PAI5 2022 Control 30 249.2418 127.3394 0.0000 3 3 3 0.0000 0.0000 31738.31 3 strata n=1 (SD=NA) 0.0026 0.2627 1.7811 3218.101 218.7796 127.0048 31654.920
PAI9 2022 Control 30 93.8830 88.8024 0.0000 2 2 2 0.0000 0.0000 8337.04 2 strata n=1 (SD=NA) 0.0026 0.2627 1.7811 3218.101 218.7796 88.5691 8315.136
PROJECT 2022 Control 30 3218.1007 218.7796 1.7811 14 13 12 0.0026 0.2627 704054.92 PROJECT row: group pooled 0.0026 0.2627 NA 3218.101 218.7796 218.2048 702205.147
PAI11 2022 Project 30 381.1532 207.5555 182.3171 12 3 0 0.0280 2.8019 79110.42 0.0128 1.2786 20.2469 3769.558 151.5702 204.9016 78098.906
PAI15 2022 Project 30 2043.3347 138.5193 50.3770 50 3 0 0.0221 2.2069 283041.29 0.0128 1.2786 20.2469 3769.558 151.5702 136.7482 279422.277
PAI16 2022 Project 30 96.8372 223.6214 61.6212 4 2 0 0.0151 1.5119 21654.87 0.0128 1.2786 20.2469 3769.558 151.5702 220.7621 21377.989
PAI3 2022 Project 30 807.5057 156.9439 23.1612 18 3 0 0.0132 1.3207 126733.12 0.0128 1.2786 20.2469 3769.558 151.5702 154.9372 125112.693
PAI5 2022 Project 30 249.2418 118.7273 31.4047 7 3 0 0.0203 2.0329 29591.81 0.0128 1.2786 20.2469 3769.558 151.5702 117.2092 29213.444
PAI9 2022 Project 30 191.4851 163.0467 906.7311 7 3 1 0.0795 7.9543 31221.02 1 strata n=1 (SD=NA) 0.0128 1.2786 20.2469 3769.558 151.5702 160.9620 30821.822
PROJECT 2022 Project 30 3769.5577 151.5702 20.2469 98 17 1 0.0128 1.2786 571352.54 PROJECT row: group pooled 0.0128 1.2786 NA 3769.558 151.5702 149.6322 564047.131
PAI11 2024 Control 30 280.9044 282.4252 0.0000 2 2 2 0.0000 0.0000 79334.50 2 strata n=1 (SD=NA) 0.0037 0.3683 3.7020 3218.101 225.0019 281.3850 79042.307
PAI15 2024 Control 30 1960.0504 238.6854 0.0000 2 2 2 0.0000 0.0000 467835.46 2 strata n=1 (SD=NA) 0.0037 0.3683 3.7020 3218.101 225.0019 237.8063 466112.405
PAI16 2024 Control 30 96.8372 251.4087 0.0000 2 2 2 0.0000 0.0000 24345.73 2 strata n=1 (SD=NA) 0.0037 0.3683 3.7020 3218.101 225.0019 250.4828 24256.060
PAI3 2024 Control 30 537.1838 175.4736 132.8587 3 2 1 0.0283 2.8292 94261.58 1 strata n=1 (SD=NA) 0.0037 0.3683 3.7020 3218.101 225.0019 174.8273 93914.406
PAI5 2024 Control 30 249.2418 181.8221 0.0000 3 3 3 0.0000 0.0000 45317.68 3 strata n=1 (SD=NA) 0.0037 0.3683 3.7020 3218.101 225.0019 181.1525 45150.774
PAI9 2024 Control 30 93.8830 138.2989 0.0000 2 2 2 0.0000 0.0000 12983.92 2 strata n=1 (SD=NA) 0.0037 0.3683 3.7020 3218.101 225.0019 137.7895 12936.095
PROJECT 2024 Control 30 3218.1007 225.0019 3.7020 14 13 12 0.0037 0.3683 724078.86 PROJECT row: group pooled 0.0037 0.3683 NA 3218.101 225.0019 224.1732 721412.047
PAI11 2024 Project 30 381.1532 241.5013 261.5610 12 3 0 0.0288 2.8843 92048.97 0.0076 0.7590 12.0579 3769.558 197.0401 239.6682 91350.298
PAI15 2024 Project 30 2043.3347 188.6282 24.8140 73 3 0 0.0114 1.1374 385430.56 0.0076 0.7590 12.0579 3769.558 197.0401 187.1965 382505.046
PAI16 2024 Project 30 96.8372 243.0329 302.1325 4 2 0 0.0308 3.0804 23534.64 0.0076 0.7590 12.0579 3769.558 197.0401 241.1883 23356.002
PAI3 2024 Project 30 807.5057 181.5633 23.4003 18 3 0 0.0115 1.1475 146613.41 0.0076 0.7590 12.0579 3769.558 197.0401 180.1852 145500.580
PAI5 2024 Project 30 249.2418 221.2313 35.9331 5 3 2 0.0117 1.1670 55140.11 2 strata n=1 (SD=NA) 0.0076 0.7590 12.0579 3769.558 197.0401 219.5521 54721.581
PAI9 2024 Project 30 191.4851 208.8220 256.6674 7 3 1 0.0330 3.3043 39986.30 1 strata n=1 (SD=NA) 0.0076 0.7590 12.0579 3769.558 197.0401 207.2370 39682.795
PROJECT 2024 Project 30 3769.5577 197.0401 12.0579 119 17 3 0.0076 0.7590 742754.00 PROJECT row: group pooled 0.0076 0.7590 NA 3769.558 197.0401 195.5445 737116.302
DELTA_NET_SOC: VM0042 Eqs.46/47 (annual rates) -> Eq.44/45 (UNC adj) -> Eq.40 CR_t / Eq.37 ER_t / Eq.43 ERR_NET | Group UNC applied
PAI_ID Year WP_area_ha WP_SOC_ha BSL_SOC_ha Delta_SOC_ha Net_SOC_tCO2e Group_UNC_combined Monitoring_gap_yrs dC_WP_tCO2e_yr dC_BSL_tCO2e_yr I_sign dC_WP_unc dC_BSL_unc I_cumwp CR_t_tCO2e ER_t_tCO2e ERR_NET_tCO2e
PAI11 2022 381.15 207.56 291.12 -83.56 -31849.07 0.01 0 0.00 0.00 1 0.00 0.00 0 0.00 0.00 0.00
PAI15 2022 2043.33 138.52 247.75 -109.23 -223193.73 0.01 0 0.00 0.00 1 0.00 0.00 0 0.00 0.00 0.00
PAI16 2022 96.84 223.62 253.02 -29.40 -2846.92 0.01 0 0.00 0.00 1 0.00 0.00 0 0.00 0.00 0.00
PAI3 2022 807.51 156.94 134.22 22.72 18349.65 0.01 0 0.00 0.00 1 0.00 0.00 0 0.00 0.00 0.00
PAI5 2022 249.24 118.73 127.34 -8.61 -2146.50 0.01 0 0.00 0.00 1 0.00 0.00 0 0.00 0.00 0.00
PAI9 2022 191.49 163.05 88.80 74.24 14216.68 0.01 0 0.00 0.00 1 0.00 0.00 0 0.00 0.00 0.00
PAI11 2024 381.15 241.50 282.43 -40.92 -15598.29 0.01 2 6469.27 -1656.11 1 6420.17 -1643.54 1 6420.17 1643.54 8063.71
PAI15 2024 2043.33 188.63 238.69 -50.06 -102283.65 0.01 2 51194.64 -9260.40 1 50806.06 -9190.11 1 50806.06 9190.11 59996.17
PAI16 2024 96.84 243.03 251.41 -8.38 -811.09 0.01 2 939.88 -78.03 1 932.75 -77.44 1 932.75 77.44 1010.19
PAI3 2024 807.51 181.56 175.47 6.09 4917.48 0.01 2 9940.14 16656.23 -1 10015.59 16782.66 1 -6767.07 0.00 -6767.07
PAI5 2024 249.24 221.23 181.82 39.41 9822.43 0.01 2 12774.15 6789.69 1 12677.19 6738.15 1 5939.04 0.00 5939.04
PAI9 2024 191.49 208.82 138.30 70.52 13504.13 0.01 2 4382.64 4738.92 -1 4415.91 4774.89 1 -358.98 0.00 -358.98

8 Session Info

## Script  : 4842_ESM_SOC_ALL_PAIs_v1_8.Rmd
## Version : v1.8
## R       : R version 4.4.1 (2024-06-14 ucrt)
## Run     : 2026-04-02 18:58:24 AEST
## Input   : C:/Users/Quan/Carbon Friendly/Carbon Friendly - Verra/Project 4842 Sentry Ag Services Carbon Project/Validation/DATA/4842_SOC_data_for_ESM_V1_0.xlsx
## Output  : C:/Users/Quan/Carbon Friendly/Carbon Friendly - Verra/Project 4842 Sentry Ag Services Carbon Project/Validation/DATA/ESM_SOC_ALL_PAIs_20260402_185811.xlsx
## PAIs    : PAI3, PAI5, PAI9, PAI11, PAI15, PAI16
## t0.667  : 0.4307  (VM0042 Eq.74)
## ESM depths : AUTO-DETECT per sheet
## UNC mode   : GROUP (project-wide pooled, applied uniformly to all PAIs)
## Unit    : g/cm2 * 100 = t C/ha; * 3.6667 = t CO2e/ha