Calculation of holding income

MtM ABM description

Author

David O’Sullivan

Published

July 2, 2024

Modified

October 17, 2025

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+\sum_it_ie_i \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_i are various environmental metrics per hectare, most obviously greenhouse gas emissions, and
  • t_i are any environmental taxes associated with each environmental metric.

Environmental taxes are configured via the environmental-metrics.csv file and can be set to zero in any or all cases as required so that the tax component of costs might reduce down to the operating cost per ha. x. 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} - \sum_i t_ie_{i,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 and environmental taxes

p_F is found in the prices.csv file:

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

Environmental taxes t_i are found in the environmental-metrics.csv file in the price column. In the example below the tax associated with greenhouse gas emissions is $10 per unit per ha and no tax is levied on nitrates. For ease of model configuration metrics are excluded from consideration if they are ‘commented out’ with a # prefix.

Code
metrics <- read.csv(str_glue("{base_dir}/environmental-metrics.csv"), 
                    header = TRUE, row.names = 1)
metrics |>
  kable() |>
  kable_styling("striped", full_width = FALSE)
limit price
ghg_emissions 10 10
nitrates 11 0
sediment 10 0
phosphates 1 0
e_coli 4 0

In the remainder of this page we assume only greenhouse gases are taxed and use the simplified form te for the environmental tax burden in preference to the more complete \sum_it_ie_{i,FL} shown above.

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}/yields.csv"), header = T)
yields |> 
  mutate(across(3:6, ~ round(., 1))) |>
  arrange(LUC) |>
  kable() |> 
  kable_styling("striped", full_width = F) |>
  scroll_box(height = "250px")
mean_or_sd LUC SNB Dairy Forest Crop
Mean 1 768.5 1503.0 30.0 9667.0
SD 1 153.7 300.6 6.0 1933.4
Mean 2 581.2 1283.0 29.0 9183.6
SD 2 116.2 256.6 5.8 1836.7
Mean 3 372.4 1174.3 28.0 8724.5
SD 3 74.5 234.9 5.6 1744.9
Mean 4 332.2 923.4 27.0 8288.2
SD 4 66.4 184.7 5.4 1657.6
Mean 5 259.5 877.7 26.0 7873.8
SD 5 51.9 175.5 5.2 1574.8
Mean 6 207.4 845.8 25.0 7480.1
SD 6 41.5 169.2 5.0 1496.0
Mean 7 193.2 700.0 24.0 7106.1
SD 7 38.6 140.0 4.8 1421.2
Mean 8 49.9 661.0 23.0 6750.8
SD 8 10.0 132.2 4.6 1350.2

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}/costs.csv"), header = TRUE)
costs |> 
  mutate(across(3:6, ~ round(., 1))) |>
  kable() |> 
  kable_styling("striped", full_width = F) |>
  scroll_box(height = "250px")
mean_or_sd LUC SNB Dairy Forest Crop
Mean 1 3500 9500 4000 3000
SD 1 700 1900 800 600
Mean 2 2650 8050 4000 2850
SD 2 530 1610 800 570
Mean 3 1625 7350 3850 2675
SD 3 325 1470 770 535
Mean 4 1450 5500 3700 2500
SD 4 290 1100 740 500
Mean 5 1110 5200 3550 2400
SD 5 222 1040 710 480
Mean 6 875 5000 3500 2200
SD 6 175 1000 700 440
Mean 7 810 4000 3375 2100
SD 7 162 800 675 420
Mean 8 125 4000 3250 2000
SD 8 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 = TRUE)
emissions |> 
  mutate(across(3:6, ~ round(., 1))) |>
  kable() |> 
  kable_styling("striped", full_width = F) |>
  scroll_box(height = "250px")
mean_or_sd LUC SNB Dairy Forest Crop
Mean 1 4.0 11.0 -15.0 1.2
SD 1 1.2 3.3 4.5 0.4
Mean 2 3.8 10.5 -14.2 1.1
SD 2 1.1 3.1 4.3 0.3
Mean 3 3.5 10.0 -13.5 1.1
SD 3 1.0 3.0 4.1 0.3
Mean 4 3.2 9.5 -12.9 1.0
SD 4 1.0 2.9 3.9 0.3
Mean 5 3.0 9.0 -12.2 1.0
SD 5 0.9 2.7 3.7 0.3
Mean 6 2.8 8.5 -11.6 0.9
SD 6 0.8 2.5 3.5 0.3
Mean 7 2.5 8.0 -11.0 0.9
SD 7 0.8 2.4 3.3 0.3
Mean 8 2.2 7.5 -10.5 0.8
SD 8 0.7 2.2 3.1 0.3

 

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") |>
  mutate(carbon_tax = 10)

yields <- yields |>
  mutate(across(3:6, ~ round(., 1))) |>
  filter(mean_or_sd == "Mean") |> 
  select(-mean_or_sd) |>
  pivot_longer(-1) |>
  rename(farm_type = name, yield = value)

costs <- costs |>
  mutate(across(3:6, ~ round(., 1))) |>
  filter(mean_or_sd == "Mean") |> 
  select(-mean_or_sd) |>
  pivot_longer(-1) |>
  rename(farm_type = name, cost = value)

emissions <- emissions |>
  mutate(across(3:6, ~ round(., 1))) |>
  filter(mean_or_sd == "Mean") |> 
  select(-mean_or_sd) |>
  pivot_longer(-1) |>
  rename(farm_type = name, emissions = value)

combined_data <- prices |>
  left_join(yields) |>
  left_join(costs) |>
  left_join(emissions) |>
  mutate(gross_income = price * yield, # LUC = as.factor(LUC),
         total_costs = cost + carbon_tax * emissions, base_costs = cost,
         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 302.50 5.0 768.5 3842.50 3500 4.0 10 3540
SNB 2 218.00 5.0 581.2 2906.00 2650 3.8 10 2688
SNB 3 202.00 5.0 372.4 1862.00 1625 3.5 10 1660
SNB 4 179.00 5.0 332.2 1661.00 1450 3.2 10 1482
SNB 5 157.50 5.0 259.5 1297.50 1110 3.0 10 1140
SNB 6 134.00 5.0 207.4 1037.00 875 2.8 10 903
SNB 7 131.00 5.0 193.2 966.00 810 2.5 10 835
SNB 8 102.50 5.0 49.9 249.50 125 2.2 10 147
Dairy 1 1662.50 7.5 1503.0 11272.50 9500 11.0 10 9610
Dairy 2 1467.50 7.5 1283.0 9622.50 8050 10.5 10 8155
Dairy 3 1357.25 7.5 1174.3 8807.25 7350 10.0 10 7450
Dairy 4 1330.50 7.5 923.4 6925.50 5500 9.5 10 5595
Dairy 5 1292.75 7.5 877.7 6582.75 5200 9.0 10 5290
Dairy 6 1258.50 7.5 845.8 6343.50 5000 8.5 10 5085
Dairy 7 1170.00 7.5 700.0 5250.00 4000 8.0 10 4080
Dairy 8 882.50 7.5 661.0 4957.50 4000 7.5 10 4075
Forest 1 860.00 157.0 30.0 4710.00 4000 -15.0 10 3850
Forest 2 695.00 157.0 29.0 4553.00 4000 -14.2 10 3858
Forest 3 681.00 157.0 28.0 4396.00 3850 -13.5 10 3715
Forest 4 668.00 157.0 27.0 4239.00 3700 -12.9 10 3571
Forest 5 654.00 157.0 26.0 4082.00 3550 -12.2 10 3428
Forest 6 541.00 157.0 25.0 3925.00 3500 -11.6 10 3384
Forest 7 503.00 157.0 24.0 3768.00 3375 -11.0 10 3265
Forest 8 466.00 157.0 23.0 3611.00 3250 -10.5 10 3145
Crop 1 1821.50 0.5 9667.0 4833.50 3000 1.2 10 3012
Crop 2 1730.80 0.5 9183.6 4591.80 2850 1.1 10 2861
Crop 3 1676.25 0.5 8724.5 4362.25 2675 1.1 10 2686
Crop 4 1634.10 0.5 8288.2 4144.10 2500 1.0 10 2510
Crop 5 1526.90 0.5 7873.8 3936.90 2400 1.0 10 2410
Crop 6 1531.05 0.5 7480.1 3740.05 2200 0.9 10 2209
Crop 7 1444.05 0.5 7106.1 3553.05 2100 0.9 10 2109
Crop 8 1367.40 0.5 6750.8 3375.40 2000 0.8 10 2008

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
  xlab("Landuse Capability (LUC)") +
  ylab("Profit, $ per ha.") +
  facet_wrap(~ farm_type) +
  theme_minimal()

Date Changes
2024-07-02 Initial post.
2024-07-03 Added bar chart of farm income/costs estimates.
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-05 Corrected relative change from a percentage to a proportion.
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.
2025-02-19 Split out from previous ‘core loop’ document.
2025-10-17 Corrected code to cope with new formatting of various datasets.

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.↩︎