Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/fuel-duty-litre-rate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Compute fuel duty directly as calibrated petrol and diesel litres multiplied by the statutory duty rate, with fuel-spending proxies uprated by road-fuel litres and pump prices.
3 changes: 2 additions & 1 deletion policyengine_uk/data/uprating_indices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ gov.economic_assumptions.yoy_growth.obr.consumer_price_index:
- universal_credit_reported
- winter_fuel_allowance_reported
- working_tax_credit_reported
gov.economic_assumptions.yoy_growth.obr.road_fuel_volume:
gov.economic_assumptions.yoy_growth.obr.diesel_spending_litre_proxy:
- diesel_spending
gov.economic_assumptions.yoy_growth.obr.petrol_spending_litre_proxy:
- petrol_spending
gov.economic_assumptions.yoy_growth.obr.mortgage_interest:
- mortgage_interest_repayment
Expand Down
134 changes: 134 additions & 0 deletions policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,140 @@ obr:
- title: OBR fuel-duty supplementary release, receipts by vehicle type
href: https://obr.uk/docs/dlm_uploads/Fuel-duty-supplementary-release_receipts-by-vehicle-type.pdf

petrol_spending_litre_proxy:
description: Unleaded-petrol spending proxy growth that preserves weighted road-fuel litres after dividing by model pump prices.
values:
2021-01-01: 0.09966723889828133
2022-01-01: 0.47125942029399615
2023-01-01: 0.09943596417596279
2024-01-01: -0.14810324567590538
2025-01-01: -0.0269132439679719
2026-01-01: -0.018120813771079436
2027-01-01: -0.027765858532455168
2028-01-01: -0.030878786058031737
2029-01-01: -0.04149818363243518
2030-01-01: -0.056482738187697334
2031-01-01: -0.00428158916658361
2032-01-01: -0.004182433778131878
2033-01-01: -0.004083258639577703
2034-01-01: -0.003984063745019917
2035-01-01: -0.003884849088554687
2036-01-01: -0.0037856146642757382
2037-01-01: -0.003686360466274796
2038-01-01: -0.0035870864886409226
2039-01-01: -0.0034877927254609586
2040-01-01: -0.0034877927254609586
2041-01-01: -0.003388479170819303
2042-01-01: -0.003388479170819303
2043-01-01: -0.0032891458187980227
2044-01-01: -0.0031897926634769647
2045-01-01: -0.0031897926634769647
2046-01-01: -0.003090419698933422
2047-01-01: -0.0029910269192421346
2048-01-01: -0.0028916143184762877
2049-01-01: -0.002792181890705958
2050-01-01: -0.002692729629998891
2051-01-01: -0.0024937655860348684
2052-01-01: -0.002394253790901746
2053-01-01: -0.0021951706246258196
2054-01-01: -0.0020955992415926383
2055-01-01: -0.001996007984031989
2056-01-01: -0.0018963968459926317
2057-01-01: -0.001796765821521329
2058-01-01: -0.001697114904662067
2059-01-01: -0.0015974440894569453
2060-01-01: -0.0015974440894569453
2061-01-01: -0.0015974440894569453
2062-01-01: -0.0014977533699451762
2063-01-01: -0.0014977533699451762
2064-01-01: -0.0014977533699451762
2065-01-01: -0.0014977533699451762
2066-01-01: -0.0014977533699451762
2067-01-01: -0.0014977533699451762
2068-01-01: -0.0014977533699451762
2069-01-01: -0.0014977533699451762
2070-01-01: -0.0014977533699451762
2071-01-01: -0.0014977533699451762
2072-01-01: -0.0014977533699451762
2073-01-01: -0.0013980427401638629
metadata:
unit: /1
label: Petrol spending litre-proxy growth
reference:
- title: HMRC Hydrocarbon Oils Bulletin
href: https://www.gov.uk/government/statistics/hydrocarbon-oils-bulletin
- title: OBR EFO March 2026 (fuel-duty receipts forecast)
href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2026/
- title: RAC latest average petrol price
href: https://www.rac.co.uk/drive/advice/fuel-watch/

diesel_spending_litre_proxy:
description: Diesel spending proxy growth that preserves weighted road-fuel litres after dividing by model pump prices.
values:
2021-01-01: 0.13390420538515913
2022-01-01: 0.3702255435452164
2023-01-01: -0.01858407316867594
2024-01-01: 0.03164670292538285
2025-01-01: -0.0269132439679719
2026-01-01: -0.018120813771079436
2027-01-01: -0.027765858532455168
2028-01-01: -0.030878786058031737
2029-01-01: -0.04149818363243518
2030-01-01: -0.056482738187697334
2031-01-01: -0.00428158916658361
2032-01-01: -0.004182433778131878
2033-01-01: -0.004083258639577703
2034-01-01: -0.003984063745019917
2035-01-01: -0.003884849088554687
2036-01-01: -0.0037856146642757382
2037-01-01: -0.003686360466274796
2038-01-01: -0.0035870864886409226
2039-01-01: -0.0034877927254609586
2040-01-01: -0.0034877927254609586
2041-01-01: -0.003388479170819303
2042-01-01: -0.003388479170819303
2043-01-01: -0.0032891458187980227
2044-01-01: -0.0031897926634769647
2045-01-01: -0.0031897926634769647
2046-01-01: -0.003090419698933422
2047-01-01: -0.0029910269192421346
2048-01-01: -0.0028916143184762877
2049-01-01: -0.002792181890705958
2050-01-01: -0.002692729629998891
2051-01-01: -0.0024937655860348684
2052-01-01: -0.002394253790901746
2053-01-01: -0.0021951706246258196
2054-01-01: -0.0020955992415926383
2055-01-01: -0.001996007984031989
2056-01-01: -0.0018963968459926317
2057-01-01: -0.001796765821521329
2058-01-01: -0.001697114904662067
2059-01-01: -0.0015974440894569453
2060-01-01: -0.0015974440894569453
2061-01-01: -0.0015974440894569453
2062-01-01: -0.0014977533699451762
2063-01-01: -0.0014977533699451762
2064-01-01: -0.0014977533699451762
2065-01-01: -0.0014977533699451762
2066-01-01: -0.0014977533699451762
2067-01-01: -0.0014977533699451762
2068-01-01: -0.0014977533699451762
2069-01-01: -0.0014977533699451762
2070-01-01: -0.0014977533699451762
2071-01-01: -0.0014977533699451762
2072-01-01: -0.0014977533699451762
2073-01-01: -0.0013980427401638629
metadata:
unit: /1
label: Diesel spending litre-proxy growth
reference:
- title: HMRC Hydrocarbon Oils Bulletin
href: https://www.gov.uk/government/statistics/hydrocarbon-oils-bulletin
- title: OBR EFO March 2026 (fuel-duty receipts forecast)
href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2026/
- title: RAC latest average diesel price
href: https://www.rac.co.uk/drive/advice/fuel-watch/

consumer_price_index_ahc:
description: Consumer price index year-on-year growth, modified to remove housing costs.
values:
Expand Down
38 changes: 38 additions & 0 deletions policyengine_uk/tests/test_fuel_duty_litres.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pytest

from policyengine_uk import Microsimulation
from policyengine_uk.system import system


def test_fuel_duty_is_litres_times_statutory_rate():
year = 2025
situation = {
"people": {
"adult": {
"age": {year: 35},
},
},
"benunits": {
"benunit": {
"members": ["adult"],
},
},
"households": {
"household": {
"members": ["adult"],
"region": {year: "LONDON"},
"petrol_spending": {year: 2_000.0},
"diesel_spending": {year: 1_000.0},
},
},
}
simulation = Microsimulation(situation=situation)

fuel_duty = simulation.calculate("fuel_duty", year).values[0]
litres = (
simulation.calculate("petrol_litres", year).values[0]
+ simulation.calculate("diesel_litres", year).values[0]
)
rate = system.parameters.gov.hmrc.fuel_duty.petrol_and_diesel(year)

assert fuel_duty == pytest.approx(litres * rate)
80 changes: 66 additions & 14 deletions policyengine_uk/tests/test_road_fuel_volume_uprating.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@
from policyengine_uk.system import system


def test_petrol_and_diesel_spending_use_road_fuel_volume_not_cpi():
def test_petrol_and_diesel_spending_preserve_road_fuel_litres_not_cpi():
parameters = system.parameters
road_fuel_volume = (
parameters.gov.economic_assumptions.yoy_growth.obr.road_fuel_volume
)
petrol_proxy = (
parameters.gov.economic_assumptions.yoy_growth.obr.petrol_spending_litre_proxy
)
diesel_proxy = (
parameters.gov.economic_assumptions.yoy_growth.obr.diesel_spending_litre_proxy
)
population = parameters.gov.economic_assumptions.yoy_growth.ons.population
petrol_price = parameters.household.consumption.fuel.prices.petrol
diesel_price = parameters.household.consumption.fuel.prices.diesel
cpi = parameters.gov.economic_assumptions.yoy_growth.obr.consumer_price_index

assert road_fuel_volume(2027) < 0
assert cpi(2027) > 0
assert road_fuel_volume(2024) < 0
assert cpi(2024) > 0
assert petrol_proxy(2024) != road_fuel_volume(2024)
assert diesel_proxy(2024) != road_fuel_volume(2024)

dataset = UKSingleYearDataset(
person=pd.DataFrame(
Expand All @@ -33,25 +44,66 @@ def test_petrol_and_diesel_spending_use_road_fuel_volume_not_cpi():
"tenure_type": ["OWNED_OUTRIGHT"],
"council_tax": [1_500.0],
"rent": [0.0],
"petrol_spending": [1_000.0],
"diesel_spending": [2_000.0],
"household_weight": [1.0],
"petrol_spending": [1_000.0 * petrol_price(2023)],
"diesel_spending": [2_000.0 * diesel_price(2023)],
}
),
fiscal_year=2026,
fiscal_year=2023,
)

extended = extend_single_year_dataset(
dataset,
tax_benefit_system_parameters=parameters,
end_year=2027,
end_year=2035,
)
household_2024 = extended[2024].household

assert household_2024["petrol_spending"].iloc[0] == pytest.approx(
1_000 * petrol_price(2023) * (1 + petrol_proxy(2024))
)
household_2027 = extended[2027].household
assert household_2024["diesel_spending"].iloc[0] == pytest.approx(
2_000 * diesel_price(2023) * (1 + diesel_proxy(2024))
)
assert household_2024["household_weight"].iloc[0] == pytest.approx(
1 + population(2024)
)
assert (
household_2024["petrol_spending"].iloc[0]
/ petrol_price(2024)
* household_2024["household_weight"].iloc[0]
) == pytest.approx(1_000 * (1 + road_fuel_volume(2024)))
assert (
household_2024["diesel_spending"].iloc[0]
/ diesel_price(2024)
* household_2024["household_weight"].iloc[0]
) == pytest.approx(2_000 * (1 + road_fuel_volume(2024)))

household_2034 = extended[2034].household
household_2035 = extended[2035].household

def weighted_litres(household, spending_variable, price_parameter, year):
return (
household[spending_variable].iloc[0]
/ price_parameter(year)
* household["household_weight"].iloc[0]
)

assert household_2027["petrol_spending"].iloc[0] == pytest.approx(
1_000 * (1 + road_fuel_volume(2027))
assert weighted_litres(
household_2035,
"petrol_spending",
petrol_price,
2035,
) == pytest.approx(
weighted_litres(household_2034, "petrol_spending", petrol_price, 2034)
* (1 + road_fuel_volume(2035))
)
assert household_2027["diesel_spending"].iloc[0] == pytest.approx(
2_000 * (1 + road_fuel_volume(2027))
assert weighted_litres(
household_2035,
"diesel_spending",
diesel_price,
2035,
) == pytest.approx(
weighted_litres(household_2034, "diesel_spending", diesel_price, 2034)
* (1 + road_fuel_volume(2035))
)
assert household_2027["petrol_spending"].iloc[0] < 1_000
assert household_2027["diesel_spending"].iloc[0] < 2_000
10 changes: 1 addition & 9 deletions policyengine_uk/variables/gov/hmrc/fuel_duty/fuel_duty.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from policyengine_uk.model_api import *

STATUTORY_CONSUMER_INCIDENCE = 0.5
ECONOMIC_CONSUMER_INCIDENCE = 1


class fuel_duty(Variable):
label = "Fuel duty (cars only)"
Expand All @@ -15,9 +12,4 @@ def formula(household, period, parameters):
fd = parameters(period).gov.hmrc.fuel_duty
petrol_litres = household("petrol_litres", period.this_year) / MONTHS_IN_YEAR
diesel_litres = household("diesel_litres", period.this_year) / MONTHS_IN_YEAR
return (
fd.petrol_and_diesel
* (petrol_litres + diesel_litres)
/ STATUTORY_CONSUMER_INCIDENCE
* ECONOMIC_CONSUMER_INCIDENCE
)
return fd.petrol_and_diesel * (petrol_litres + diesel_litres)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class diesel_litres(Variable):
entity = Household
definition_period = YEAR
value_type = float
unit = GBP
unit = "litre"

def formula(household, period, parameters):
return household("diesel_spending", period) / household("diesel_price", period)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class petrol_litres(Variable):
entity = Household
definition_period = YEAR
value_type = float
unit = GBP
unit = "litre"

def formula(household, period, parameters):
return household("petrol_spending", period) / household("petrol_price", period)
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class diesel_spending(Variable):
value_type = float
unit = GBP
quantity_type = FLOW
uprating = "gov.economic_assumptions.indices.obr.road_fuel_volume"
uprating = "gov.economic_assumptions.indices.obr.diesel_spending_litre_proxy"
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class petrol_spending(Variable):
value_type = float
unit = GBP
quantity_type = FLOW
uprating = "gov.economic_assumptions.indices.obr.road_fuel_volume"
uprating = "gov.economic_assumptions.indices.obr.petrol_spending_litre_proxy"