Single Farm Analysis (cowfootR)

knitr::opts_chunk$set(
  fig.alt = "Figure generated by this vignette; see the surrounding text for details.",
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 5,
  warning = FALSE,
  message = FALSE
)

library(cowfootR)
library(ggplot2)
library(dplyr)
library(knitr)

Comprehensive Single Farm Carbon Footprint Analysis

This vignette shows a complete, end-to-end carbon footprint assessment for one dairy farm using cowfootR. The goal is to demonstrate how individual emission sources can be calculated, combined, visualized, and summarized into intensity metrics.

Farm Profile: Estancia Las Flores

We will analyze a medium-sized dairy farm with a mixed grazing system and supplementation:

Step 1: Define system boundaries

Purpose. System boundaries specify which emission sources are included in the assessment (e.g., farm-gate vs cradle-to-farm-gate). This affects which functions are relevant and how totals are interpreted.

# Most common scope: farm-gate boundaries
boundaries <- set_system_boundaries("farm_gate")
boundaries

You can also explore other boundary configurations:

# Includes upstream emissions (e.g., purchased inputs)
boundaries_extended <- set_system_boundaries("cradle_to_farm_gate")
boundaries_extended

# Custom selection of sources
boundaries_partial <- set_system_boundaries(
  scope = "partial",
  include = c("enteric", "manure", "soil")
)
boundaries_partial

For the rest of this vignette, we use farm-gate boundaries because it is a common scope in farm-level reporting.

Step 2: Provide farm data (inputs)

Purpose. cowfootR functions require inputs describing the herd, production, land use, energy consumption, and purchased inputs. In real applications, this information often comes from farm records, advisory systems, or templates.

Herd Composition and production

herd_data <- list(
  dairy_cows_milking = 120,
  dairy_cows_dry = 30,
  heifers_total = 45,
  calves_total = 35,
  bulls_total = 3,
  body_weight_cows = 580,
  body_weight_heifers = 380,
  body_weight_calves = 180,
  body_weight_bulls = 750,
  milk_yield_per_cow = 6300,
  annual_milk_litres = 950000,
  fat_percent = 3.7,
  protein_percent = 3.3,
  milk_density = 1.032
)

herd_data

Feed and nutrition

feed_data <- list(
  concentrate_kg = 220000,
  grain_dry_kg = 80000,
  grain_wet_kg = 45000,
  ration_kg = 60000,
  byproducts_kg = 25000,
  proteins_kg = 35000,
  dry_matter_intake_cows = 19.5,
  dry_matter_intake_heifers = 11.0,
  dry_matter_intake_calves = 6.0,
  dry_matter_intake_bulls = 14.0,
  ym_percent = 6.3
)

feed_data

Land use and soil management

land_data <- list(
  area_total = 200,
  area_productive = 185,
  area_fertilized = 160,
  pasture_permanent = 140,
  pasture_temporary = 30,
  crops_feed = 12,
  crops_cash = 3,
  infrastructure = 8,
  woodland = 7,
  soil_type = "well_drained",
  climate_zone = "temperate",
  n_fertilizer_synthetic = 2400,
  n_fertilizer_organic = 500,
  n_excreta_pasture = 15000,
  n_crop_residues = 800
)

land_data

Energy consumption

energy_data <- list(
  diesel_litres = 12000,
  petrol_litres = 1800,
  lpg_kg = 600,
  natural_gas_m3 = 0,
  electricity_kwh = 48000,
  country = "UY"
)

energy_data

Additional purchased inputs

other_inputs <- list(
  plastic_kg = 450,
  transport_km = 120,
  fert_type = "mixed",
  plastic_type = "mixed",
  region = "global"
)

other_inputs

Step 3: Calculate emissions by source

Purpose. Here we compute each emission source separately. This is useful for transparency, debugging, and hotspot analysis.

Enteric fermentation

Key arguments.

-   n_animals, cattle_category: define the livestock group
-   Tier 2 options include avg_body_weight, dry_matter_intake, ym_percent
-   boundaries ensures calculations align with scope
enteric_cows <- calc_emissions_enteric(
  n_animals = herd_data$dairy_cows_milking + herd_data$dairy_cows_dry,
  cattle_category = "dairy_cows",
  avg_milk_yield = herd_data$milk_yield_per_cow,
  avg_body_weight = herd_data$body_weight_cows,
  dry_matter_intake = feed_data$dry_matter_intake_cows,
  ym_percent = feed_data$ym_percent,
  tier = 2,
  boundaries = boundaries
)

enteric_heifers <- calc_emissions_enteric(
  n_animals = herd_data$heifers_total,
  cattle_category = "heifers",
  avg_body_weight = herd_data$body_weight_heifers,
  dry_matter_intake = feed_data$dry_matter_intake_heifers,
  ym_percent = feed_data$ym_percent,
  tier = 2,
  boundaries = boundaries
)

enteric_calves <- calc_emissions_enteric(
  n_animals = herd_data$calves_total,
  cattle_category = "calves",
  avg_body_weight = herd_data$body_weight_calves,
  dry_matter_intake = feed_data$dry_matter_intake_calves,
  tier = 2,
  boundaries = boundaries
)

enteric_bulls <- calc_emissions_enteric(
  n_animals = herd_data$bulls_total,
  cattle_category = "bulls",
  avg_body_weight = herd_data$body_weight_bulls,
  dry_matter_intake = feed_data$dry_matter_intake_bulls,
  tier = 2,
  boundaries = boundaries
)

enteric_summary <- data.frame(
  Category = c("Dairy Cows", "Heifers", "Calves", "Bulls"),
  Animals = c(150, herd_data$heifers_total, herd_data$calves_total, herd_data$bulls_total),
  CH4_kg = c(enteric_cows$ch4_kg, enteric_heifers$ch4_kg, enteric_calves$ch4_kg, enteric_bulls$ch4_kg),
  CO2eq_kg = c(enteric_cows$co2eq_kg, enteric_heifers$co2eq_kg, enteric_calves$co2eq_kg, enteric_bulls$co2eq_kg)
)

kable(enteric_summary, caption = "Enteric emissions by animal category")

total_enteric <- enteric_summary$CO2eq_kg |> sum()

Manure management

Key arguments.

•   manure_system: a simplified representation of storage/handling
•   include_indirect: whether to include indirect emissions
total_animals <- sum(
  herd_data$dairy_cows_milking, herd_data$dairy_cows_dry,
  herd_data$heifers_total, herd_data$calves_total, herd_data$bulls_total
)

manure_emissions <- calc_emissions_manure(
  n_cows = total_animals,
  manure_system = "pasture",
  tier = 2,
  avg_body_weight = 500,
  diet_digestibility = 0.67,
  climate = "temperate",
  include_indirect = TRUE,
  boundaries = boundaries
)

manure_emissions

Soil N2O Emissions

soil_emissions <- calc_emissions_soil(
  n_fertilizer_synthetic = land_data$n_fertilizer_synthetic,
  n_fertilizer_organic = land_data$n_fertilizer_organic,
  n_excreta_pasture = land_data$n_excreta_pasture,
  n_crop_residues = land_data$n_crop_residues,
  area_ha = land_data$area_total,
  soil_type = land_data$soil_type,
  climate = land_data$climate_zone,
  include_indirect = TRUE,
  boundaries = boundaries
)

soil_emissions

Energy-Related Emissions

energy_emissions <- calc_emissions_energy(
  diesel_l = energy_data$diesel_litres,
  petrol_l = energy_data$petrol_litres,
  lpg_kg = energy_data$lpg_kg,
  natural_gas_m3 = energy_data$natural_gas_m3,
  electricity_kwh = energy_data$electricity_kwh,
  country = energy_data$country,
  include_upstream = FALSE,
  boundaries = boundaries
)

energy_emissions

Purchased Input Emissions

input_emissions <- calc_emissions_inputs(
  conc_kg = feed_data$concentrate_kg,
  fert_n_kg = land_data$n_fertilizer_synthetic,
  plastic_kg = other_inputs$plastic_kg,
  feed_grain_dry_kg = feed_data$grain_dry_kg,
  feed_grain_wet_kg = feed_data$grain_wet_kg,
  feed_ration_kg = feed_data$ration_kg,
  feed_byproducts_kg = feed_data$byproducts_kg,
  feed_proteins_kg = feed_data$proteins_kg,
  region = other_inputs$region,
  fert_type = other_inputs$fert_type,
  plastic_type = other_inputs$plastic_type,
  transport_km = other_inputs$transport_km,
  boundaries = boundaries
)

input_emissions

Step 4: Total emissions and hotspot analysis

Purpose. calc_total_emissions() aggregates multiple sources into a single result and provides a breakdown that is convenient for reporting and visualization.

enteric_combined <- list(source = "enteric", co2eq_kg = total_enteric)

total_emissions <- calc_total_emissions(
  enteric_combined,
  manure_emissions,
  soil_emissions,
  energy_emissions,
  input_emissions
)

total_emissions

Visualize emission sources (bar chart)

emission_breakdown <- data.frame(
  Source = names(total_emissions$breakdown),
  Emissions = as.numeric(total_emissions$breakdown),
  Percentage = round(as.numeric(total_emissions$breakdown) / total_emissions$total_co2eq * 100, 1)
)

ggplot(emission_breakdown, aes(x = reorder(Source, Emissions), y = Emissions)) +
  geom_col(alpha = 0.8) +
  geom_text(aes(label = paste0(Percentage, "%")), hjust = -0.1, size = 3) +
  coord_flip() +
  labs(
    title = "Farm emissions by source",
    subtitle = paste("Total:", format(round(total_emissions$total_co2eq), big.mark = ","), "kg CO₂eq/year"),
    x = "Emission source",
    y = "Emissions (kg CO₂eq/year)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 14, hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5)
  )

Step 5: Intensity calculations

Purpose. Intensity metrics normalize total emissions by production (milk) and land use (area). These are useful for comparison across farms and across scenarios, but should always be interpreted in context.

Milk Intensity (kg CO₂eq per kg FPCM)

milk_intensity <- calc_intensity_litre(
  total_emissions = total_emissions,
  milk_litres = herd_data$annual_milk_litres,
  fat = herd_data$fat_percent,
  protein = herd_data$protein_percent,
  milk_density = herd_data$milk_density
)

milk_intensity

Area Intensity (kg CO₂eq per ha)

area_breakdown <- list(
  pasture_permanent = land_data$pasture_permanent,
  pasture_temporary = land_data$pasture_temporary,
  crops_feed = land_data$crops_feed,
  crops_cash = land_data$crops_cash,
  infrastructure = land_data$infrastructure,
  woodland = land_data$woodland
)

area_intensity <- calc_intensity_area(
  total_emissions = total_emissions,
  area_total_ha = land_data$area_total,
  area_productive_ha = land_data$area_productive,
  area_breakdown = area_breakdown,
  validate_area_sum = TRUE
)

area_intensity

Benchmarking (interpretation note)

Benchmark values should be treated as indicative guidance and may vary by production system, region, and data quality.

area_benchmark <- benchmark_area_intensity(
  cf_area_intensity = area_intensity,
  region = "uruguay"
)

area_benchmark$benchmarking

Step 6: Scenario analysis (example)

Purpose. Scenario comparisons help explore mitigation options by changing one driver at a time while keeping the rest constant.

Scenario 1: Improved feed efficiency (10% reduction in concentrate)

improved_inputs <- calc_emissions_inputs(
  conc_kg = feed_data$concentrate_kg * 0.9,
  fert_n_kg = land_data$n_fertilizer_synthetic,
  plastic_kg = other_inputs$plastic_kg,
  feed_grain_dry_kg = feed_data$grain_dry_kg,
  feed_grain_wet_kg = feed_data$grain_wet_kg,
  feed_ration_kg = feed_data$ration_kg,
  feed_byproducts_kg = feed_data$byproducts_kg,
  feed_proteins_kg = feed_data$proteins_kg,
  region = other_inputs$region,
  fert_type = other_inputs$fert_type,
  plastic_type = other_inputs$plastic_type,
  transport_km = other_inputs$transport_km,
  boundaries = boundaries
)

total_improved <- calc_total_emissions(
  enteric_combined,
  manure_emissions,
  soil_emissions,
  energy_emissions,
  improved_inputs
)

scenario_comparison <- data.frame(
  Scenario = c("Baseline", "Improved Feed Efficiency"),
  Total_Emissions = c(total_emissions$total_co2eq, total_improved$total_co2eq),
  Reduction_kg = c(0, total_emissions$total_co2eq - total_improved$total_co2eq),
  Reduction_percent = c(
    0,
    round((total_emissions$total_co2eq - total_improved$total_co2eq) / total_emissions$total_co2eq * 100, 1)
  )
)

kable(scenario_comparison, caption = "Mitigation scenario analysis (illustrative)")

Scenario 2: Enhanced Manure Management

# Scenario: Switch from pasture to anaerobic digester
improved_manure <- calc_emissions_manure(
  n_cows = total_animals,
  manure_system = "anaerobic_digester",
  tier = 2,
  avg_body_weight = 500,
  diet_digestibility = 0.67,
  climate = "temperate",
  retention_days = 45,
  system_temperature = 35,
  include_indirect = TRUE,
  boundaries = boundaries
)

# Calculate total with improved manure management
total_improved_manure <- calc_total_emissions(
  enteric_combined,
  improved_manure,
  soil_emissions,
  energy_emissions,
  input_emissions
)

manure_comparison <- data.frame(
  System = c("Pasture", "Anaerobic Digester"),
  CH4_kg = c(manure_emissions$ch4_kg, improved_manure$ch4_kg),
  N2O_kg = c(manure_emissions$n2o_total_kg, improved_manure$n2o_total_kg),
  Total_CO2eq = c(manure_emissions$co2eq_kg, improved_manure$co2eq_kg),
  Reduction_kg = c(0, manure_emissions$co2eq_kg - improved_manure$co2eq_kg)
)

kable(manure_comparison, caption = "Manure Management Comparison")

Step 7: Two complementary visual summaries

This vignette includes two visual summaries on purpose:

  1. Farm emissions by source (bar chart) uses total_emissions$breakdown, i.e., the aggregation returned by calc_total_emissions().
  2. Multi-source breakdown (stacked bar) is a more detailed view that splits enteric emissions into cows vs. young stock and keeps other sources as separate blocks.

These charts answer different questions: the first highlights the hotspot categories in the official aggregation; the second gives a more granular interpretation for communication.

Multi-source emissions chart (stacked bar)

detailed_emissions <- data.frame(
  Source = c(
    "Enteric - Cows", "Enteric - Young Stock", "Manure Management",
    "Soil N2O", "Energy Use", "Purchased Inputs"
  ),
  Emissions = c(
    enteric_cows$co2eq_kg,
    enteric_heifers$co2eq_kg + enteric_calves$co2eq_kg + enteric_bulls$co2eq_kg,
    manure_emissions$co2eq_kg,
    soil_emissions$co2eq_kg,
    energy_emissions$co2eq_kg,
    input_emissions$total_co2eq_kg
  )
)

ggplot(detailed_emissions, aes(x = "Farm emissions", y = Emissions, fill = Source)) +
  geom_col() +
  labs(
    title = "Single farm carbon footprint breakdown (detailed view)",
    subtitle = paste("Total:", format(round(total_emissions$total_co2eq), big.mark = ","), "kg CO₂eq/year"),
    x = "",
    y = "Emissions (kg CO₂eq/year)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 14, hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5),
    axis.text.x = element_blank(),
    legend.position = "right"
  )

Step 8: Summary table (KPI-style)

Purpose. This table provides a compact summary of key metrics for reporting. At the moment, we build it explicitly as a data.frame to keep the workflow transparent; a dedicated helper function could be added in future versions if needed.

kpi_summary <- data.frame(
  Metric = c(
    "Milk intensity (kg CO₂eq/kg FPCM)",
    "Area intensity - total (kg CO₂eq/ha)",
    "Area intensity - productive (kg CO₂eq/ha)",
    "Land use efficiency (%)",
    "Milk yield (L/cow/year)",
    "Stocking rate (cows/ha)"
  ),
  Value = c(
    round(milk_intensity$intensity_co2eq_per_kg_fpcm, 3),
    round(area_intensity$intensity_per_total_ha, 0),
    round(area_intensity$intensity_per_productive_ha, 0),
    round(area_intensity$land_use_efficiency * 100, 1),
    round(herd_data$annual_milk_litres / 150, 0),
    round(150 / land_data$area_total, 2)
  )
)

kable(kpi_summary, caption = "Key performance indicators (illustrative)")

Conclusion

This vignette demonstrated a complete single-farm workflow:

For multi-farm processing, see the Batch Farm Assessment vignette.

This analysis used cowfootR version r packageVersion("cowfootR") following IDF 2022 and IPCC 2019 standards.



Try the cowfootR package in your browser

Any scripts or data that you put into this service are public.

cowfootR documentation built on Jan. 13, 2026, 5:07 p.m.