Calculation of holding income

MtM ABM description

Author

David O’Sullivan

Published

July 2, 2024

Baseline income equation for holdings

The baseline profit Z of each hectare of a holding is given by

Z=Y-X

where Y is gross income, and X is total costs. These are in turn given by

\begin{array}{rcl} Y &=& pq \\ X &=& x+te \end{array}

where

  • p is the price obtained per unit,
  • q is the quantity produced per hectare,
  • x is the cost per hectare to operate,
  • e is the greenhouse gas emissions per hectare, and
  • t is the tax on each unit of emissions.

Each of these parameters may depend on the farm type F and the land use capability (LUC), L, such that the full calculation of profit for each hectare of a holding is Z_{FL} = p_Fq_{FL} - x_{FL} - te_{FL} and total holding profit is obtaining by summing this quantity across all hectares of the holding. Overall profit of a farm is determined by summation across all its constituent holdings. Note that while currently all cells in a holding have the same LUC this is not a requirement and the calculation will work equally well if LUC were to vary across a holding.

Parameter values

The parameter values are stored in a number of CSV files which are read in by the model at start up from the .data/market/<scenario> folder specified.

Prices

p_F and the greenhouse gas tax (i.e. carbon price), t, are in the prices.csv file:

Code
prices <- read.csv(str_glue("{base_dir}prices.csv"), 
                   header = T, row.names = 1) |> 
  slice(1:2) # only rows 1 & 2 relevant
prices |> 
  kable() |> 
  kable_styling("striped", full_width = F)
SNB Dairy Forest Crop
Price_Commodity 5 7.5 157 0.5
Price_GhG 25 25.0 25 25.0

Yields

Yields by farm type and land use capability, q_{FL}, are in the commodity-yields.csv file:

Code
yields <- read.csv(str_glue("{base_dir}commodity-yields.csv"), 
                   header = T, row.names = 1)
yields |> 
  kable() |> 
  kable_styling("striped", full_width = F) |>
  scroll_box(height = "250px")
SNB Dairy Forest Crop
LUC1_Mean 768.505660 1503.0000 30.0 9667.000
LUC1_SD 153.701132 300.6000 6.0 1933.400
LUC2_Mean 581.247332 1283.0000 29.0 9183.650
LUC2_SD 116.249466 256.6000 5.8 1836.730
LUC3_Mean 372.371287 1174.3000 28.0 8724.468
LUC3_SD 74.474257 234.8600 5.6 1744.893
LUC4_Mean 332.178378 923.3900 27.0 8288.244
LUC4_SD 66.435676 184.6780 5.4 1657.649
LUC5_Mean 259.480336 877.7308 26.0 7873.832
LUC5_SD 51.896067 175.5462 5.2 1574.766
LUC6_Mean 207.386971 845.7812 25.0 7480.140
LUC6_SD 41.477394 169.1562 5.0 1496.028
LUC7_Mean 193.188428 700.0000 24.0 7106.133
LUC7_SD 38.637686 140.0000 4.8 1421.227
LUC8_Mean 49.945208 661.0000 23.0 6750.827
LUC8_SD 9.989042 132.2000 4.6 1350.165

Costs

Base costs per ha. by farm type and land use capability, x_{FL}, are in the input-costs.csv file:

Code
costs <- read.csv(str_glue("{base_dir}input-costs.csv"), 
                   header = T, row.names = 1)
costs |> 
  kable() |> 
  kable_styling("striped", full_width = F) |>
  scroll_box(height = "250px")
SNB Dairy Forest Crop
LUC1_Mean 3500 9500 4000 3000
LUC1_SD 700 1900 800 600
LUC2_Mean 2650 8050 4000 2850
LUC2_SD 530 1610 800 570
LUC3_Mean 1625 7350 3850 2675
LUC3_SD 325 1470 770 535
LUC4_Mean 1450 5500 3700 2500
LUC4_SD 290 1100 740 500
LUC5_Mean 1110 5200 3550 2400
LUC5_SD 222 1040 710 480
LUC6_Mean 875 5000 3500 2200
LUC6_SD 175 1000 700 440
LUC7_Mean 810 4000 3375 2100
LUC7_SD 162 800 675 420
LUC8_Mean 125 4000 3250 2000
LUC8_SD 25 800 650 400

Emissions

Emissions per ha. by farm type and land use capability, e_{FL}, are in the ghg-emissions.csv file:

Code
emissions <- read.csv(str_glue("{base_dir}ghg-emissions.csv"), 
                      header = T, row.names = 1)
emissions |> 
  kable() |> 
  kable_styling("striped", full_width = F) |>
  scroll_box(height = "250px")
SNB Dairy Forest Crop
LUC1_Mean 4.000 11.00 -15.000000 1.2000000
LUC1_SD 1.200 3.30 4.500000 0.3600000
LUC2_Mean 3.750 10.50 -14.250000 1.1400000
LUC2_SD 1.125 3.15 4.275000 0.3420000
LUC3_Mean 3.500 10.00 -13.537500 1.0830000
LUC3_SD 1.050 3.00 4.061250 0.3249000
LUC4_Mean 3.250 9.50 -12.860625 1.0288500
LUC4_SD 0.975 2.85 3.858188 0.3086550
LUC5_Mean 3.000 9.00 -12.217594 0.9774075
LUC5_SD 0.900 2.70 3.665278 0.2932223
LUC6_Mean 2.750 8.50 -11.606714 0.9285371
LUC6_SD 0.825 2.55 3.482014 0.2785611
LUC7_Mean 2.500 8.00 -11.026378 0.8821103
LUC7_SD 0.750 2.40 3.307914 0.2646331
LUC8_Mean 2.250 7.50 -10.475059 0.8380048
LUC8_SD 0.675 2.25 3.142518 0.2514014

 

Note that all of yields, base costs, and emissions are stored as a mean and a standard deviation, and that each holding will see these numbers vary every model time period by drawing from a normal distribution.

We can use these data to show the profit, gross income, and total costs for each farm type across different land use capability classes. We’ll throw away the standard deviations for simplicity and consider only mean outcomes.

Code
prices <- prices |> 
  t() |> 
  as.data.frame() |> 
  tibble::rownames_to_column("farm_type") |>
  rename(price = Price_Commodity, carbon_tax = Price_GhG) |>
  slice(rep(row_number(), 8)) |>
  mutate(LUC = rep(1:8, each = 4))

yields <- yields |>
  slice(seq(1, 15, 2)) |> 
  t() |> 
  as.data.frame() |> 
  tibble::rownames_to_column("farm_type") |>
  pivot_longer(-1) |>
  mutate(LUC = rep(1:8, 4), yield = round(value, 1)) |>
  select(farm_type, LUC, yield)

costs <- costs |>
  slice(seq(1, 15, 2)) |> 
  t() |> 
  as.data.frame() |> 
  tibble::rownames_to_column("farm_type") |>
  pivot_longer(-1) |>
  mutate(LUC = rep(1:8, 4), costs = round(value, 1)) |>
  select(farm_type, LUC, costs)

emissions <- emissions |> 
  slice(seq(1, 15, 2)) |> 
  t() |> 
  as.data.frame() |> 
  tibble::rownames_to_column("farm_type") |>
  pivot_longer(-1) |>
  mutate(LUC = rep(1:8, 4), emissions = round(value, 1)) |>
  select(farm_type, LUC, emissions)

combined_data <- prices |>
  left_join(yields) |>
  left_join(costs) |>
  left_join(emissions) |>
  mutate(gross_income = price * yield, # LUC = as.factor(LUC),
         total_costs = costs + carbon_tax * emissions, base_costs = costs,
         profit = gross_income - total_costs) |>
  select(farm_type, LUC, profit, price, yield, gross_income, base_costs,
         emissions, carbon_tax, total_costs) |>
  rename(Profit = profit, `Gross income` = gross_income, `Total costs` = total_costs)

combined_data |> 
  kable() |> 
  kable_styling("striped", full_width = F) |>
  scroll_box(height = "250px")
farm_type LUC Profit price yield Gross income base_costs emissions carbon_tax Total costs
SNB 1 242.50 5.0 768.5 3842.50 3500 4.0 25 3600.0
Dairy 1 1497.50 7.5 1503.0 11272.50 9500 11.0 25 9775.0
Forest 1 1085.00 157.0 30.0 4710.00 4000 -15.0 25 3625.0
Crop 1 1803.50 0.5 9667.0 4833.50 3000 1.2 25 3030.0
SNB 2 161.00 5.0 581.2 2906.00 2650 3.8 25 2745.0
Dairy 2 1310.00 7.5 1283.0 9622.50 8050 10.5 25 8312.5
Forest 2 908.00 157.0 29.0 4553.00 4000 -14.2 25 3645.0
Crop 2 1714.30 0.5 9183.6 4591.80 2850 1.1 25 2877.5
SNB 3 149.50 5.0 372.4 1862.00 1625 3.5 25 1712.5
Dairy 3 1207.25 7.5 1174.3 8807.25 7350 10.0 25 7600.0
Forest 3 883.50 157.0 28.0 4396.00 3850 -13.5 25 3512.5
Crop 3 1659.75 0.5 8724.5 4362.25 2675 1.1 25 2702.5
SNB 4 131.00 5.0 332.2 1661.00 1450 3.2 25 1530.0
Dairy 4 1188.00 7.5 923.4 6925.50 5500 9.5 25 5737.5
Forest 4 861.50 157.0 27.0 4239.00 3700 -12.9 25 3377.5
Crop 4 1619.10 0.5 8288.2 4144.10 2500 1.0 25 2525.0
SNB 5 112.50 5.0 259.5 1297.50 1110 3.0 25 1185.0
Dairy 5 1157.75 7.5 877.7 6582.75 5200 9.0 25 5425.0
Forest 5 837.00 157.0 26.0 4082.00 3550 -12.2 25 3245.0
Crop 5 1511.90 0.5 7873.8 3936.90 2400 1.0 25 2425.0
SNB 6 92.00 5.0 207.4 1037.00 875 2.8 25 945.0
Dairy 6 1131.00 7.5 845.8 6343.50 5000 8.5 25 5212.5
Forest 6 715.00 157.0 25.0 3925.00 3500 -11.6 25 3210.0
Crop 6 1517.55 0.5 7480.1 3740.05 2200 0.9 25 2222.5
SNB 7 93.50 5.0 193.2 966.00 810 2.5 25 872.5
Dairy 7 1050.00 7.5 700.0 5250.00 4000 8.0 25 4200.0
Forest 7 668.00 157.0 24.0 3768.00 3375 -11.0 25 3100.0
Crop 7 1430.55 0.5 7106.1 3553.05 2100 0.9 25 2122.5
SNB 8 69.50 5.0 49.9 249.50 125 2.2 25 180.0
Dairy 8 770.00 7.5 661.0 4957.50 4000 7.5 25 4187.5
Forest 8 623.50 157.0 23.0 3611.00 3250 -10.5 25 2987.5
Crop 8 1355.40 0.5 6750.8 3375.40 2000 0.8 25 2020.0

Or visually…

Below are shown the income, costs, and net revenue per hectare for each farm type across the eight land use capability classes. These are crude estimates dating back to the ARLUNZ model1 and will be updated in due course. Even so it is expected that the relative profitability of the different broad land use classes will remain similar.

Code
plot_data <- combined_data |> 
  pivot_longer(cols = c("Gross income", "Profit", "Total costs"))

ggplot(plot_data |> filter(name %in% c("Gross income", "Total costs"))) +
  geom_col(aes(x = as.factor(LUC), y = value, group = name, fill = name),
           position = "dodge") + 
  scale_fill_manual(values = c("#999999", "#ff6666"), name = "Income vs. Costs") +
  geom_point(data = plot_data |> filter(name == "Profit"), 
            aes(x = LUC, y = value, shape = name), size = 7) +
  scale_shape_manual(values = "\u2014", name = "") + # \u2014 is an em-dash
  # The below more correct, but doesn't extend the lines to the full width of the plot
  # geom_step(data = plot_data |> filter(name == "Profit"), 
  #           aes(x = LUC, y = value, group = farm_type, colour = name), 
  #           direction = "mid", lwd = 0.65) +
  # scale_colour_manual(values = c("black"), name = "") +
  xlab("Landuse Capability (LUC)") +
  ylab("Profit, $ per ha.") +
  facet_wrap(~ farm_type) +
  theme_minimal()

Date Changes
2025-02-19 Split out from previous ‘core loop’ document.
2024-07-28 Correction in equation summarising full calculation of profit. Added note to consider the nudge calculation’s possible bias favouring changes after bad years.
2024-07-05 Corrected relative change from a percentage to a proportion.
2024-07-04 Added detail on calculation of relative change noting that profit can’t be handled this way because it is \pm and relative change is meaningless for such variables.
2024-07-03 Added bar chart of farm income/costs estimates.
2024-07-02 Initial post.

Footnotes

  1. See: Morgan FJ and AJ Daigneault. 2015. Estimating Impacts of Climate Change Policy on Land Use: An Agent-Based Modelling Approach. PLOS ONE 10(5): e0127317.↩︎