Skip to content

Commit 7e93d00

Browse files
committed
Compute fuel duty from calibrated litres
1 parent b9a2173 commit 7e93d00

10 files changed

Lines changed: 246 additions & 28 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

policyengine_uk/data/uprating_indices.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ gov.economic_assumptions.yoy_growth.obr.consumer_price_index:
4848
- universal_credit_reported
4949
- winter_fuel_allowance_reported
5050
- working_tax_credit_reported
51-
gov.economic_assumptions.yoy_growth.obr.road_fuel_volume:
51+
gov.economic_assumptions.yoy_growth.obr.diesel_spending_litre_proxy:
5252
- diesel_spending
53+
gov.economic_assumptions.yoy_growth.obr.petrol_spending_litre_proxy:
5354
- petrol_spending
5455
gov.economic_assumptions.yoy_growth.obr.mortgage_interest:
5556
- mortgage_interest_repayment

policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,140 @@ obr:
322322
- title: OBR fuel-duty supplementary release, receipts by vehicle type
323323
href: https://obr.uk/docs/dlm_uploads/Fuel-duty-supplementary-release_receipts-by-vehicle-type.pdf
324324

325+
petrol_spending_litre_proxy:
326+
description: Unleaded-petrol spending proxy growth that preserves weighted road-fuel litres after dividing by model pump prices.
327+
values:
328+
2021-01-01: 0.09966723889828133
329+
2022-01-01: 0.47125942029399615
330+
2023-01-01: 0.09943596417596279
331+
2024-01-01: -0.14810324567590538
332+
2025-01-01: -0.0269132439679719
333+
2026-01-01: -0.018120813771079436
334+
2027-01-01: -0.027765858532455168
335+
2028-01-01: -0.030878786058031737
336+
2029-01-01: -0.04149818363243518
337+
2030-01-01: -0.056482738187697334
338+
2031-01-01: -0.00428158916658361
339+
2032-01-01: -0.004182433778131878
340+
2033-01-01: -0.004083258639577703
341+
2034-01-01: -0.003984063745019917
342+
2035-01-01: -0.003884849088554687
343+
2036-01-01: -0.0037856146642757382
344+
2037-01-01: -0.003686360466274796
345+
2038-01-01: -0.0035870864886409226
346+
2039-01-01: -0.0034877927254609586
347+
2040-01-01: -0.0034877927254609586
348+
2041-01-01: -0.003388479170819303
349+
2042-01-01: -0.003388479170819303
350+
2043-01-01: -0.0032891458187980227
351+
2044-01-01: -0.0031897926634769647
352+
2045-01-01: -0.0031897926634769647
353+
2046-01-01: -0.003090419698933422
354+
2047-01-01: -0.0029910269192421346
355+
2048-01-01: -0.0028916143184762877
356+
2049-01-01: -0.002792181890705958
357+
2050-01-01: -0.002692729629998891
358+
2051-01-01: -0.0024937655860348684
359+
2052-01-01: -0.002394253790901746
360+
2053-01-01: -0.0021951706246258196
361+
2054-01-01: -0.0020955992415926383
362+
2055-01-01: -0.001996007984031989
363+
2056-01-01: -0.0018963968459926317
364+
2057-01-01: -0.001796765821521329
365+
2058-01-01: -0.001697114904662067
366+
2059-01-01: -0.0015974440894569453
367+
2060-01-01: -0.0015974440894569453
368+
2061-01-01: -0.0015974440894569453
369+
2062-01-01: -0.0014977533699451762
370+
2063-01-01: -0.0014977533699451762
371+
2064-01-01: -0.0014977533699451762
372+
2065-01-01: -0.0014977533699451762
373+
2066-01-01: -0.0014977533699451762
374+
2067-01-01: -0.0014977533699451762
375+
2068-01-01: -0.0014977533699451762
376+
2069-01-01: -0.0014977533699451762
377+
2070-01-01: -0.0014977533699451762
378+
2071-01-01: -0.0014977533699451762
379+
2072-01-01: -0.0014977533699451762
380+
2073-01-01: -0.0013980427401638629
381+
metadata:
382+
unit: /1
383+
label: Petrol spending litre-proxy growth
384+
reference:
385+
- title: HMRC Hydrocarbon Oils Bulletin
386+
href: https://www.gov.uk/government/statistics/hydrocarbon-oils-bulletin
387+
- title: OBR EFO March 2026 (fuel-duty receipts forecast)
388+
href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2026/
389+
- title: RAC latest average petrol price
390+
href: https://www.rac.co.uk/drive/advice/fuel-watch/
391+
392+
diesel_spending_litre_proxy:
393+
description: Diesel spending proxy growth that preserves weighted road-fuel litres after dividing by model pump prices.
394+
values:
395+
2021-01-01: 0.13390420538515913
396+
2022-01-01: 0.3702255435452164
397+
2023-01-01: -0.01858407316867594
398+
2024-01-01: 0.03164670292538285
399+
2025-01-01: -0.0269132439679719
400+
2026-01-01: -0.018120813771079436
401+
2027-01-01: -0.027765858532455168
402+
2028-01-01: -0.030878786058031737
403+
2029-01-01: -0.04149818363243518
404+
2030-01-01: -0.056482738187697334
405+
2031-01-01: -0.00428158916658361
406+
2032-01-01: -0.004182433778131878
407+
2033-01-01: -0.004083258639577703
408+
2034-01-01: -0.003984063745019917
409+
2035-01-01: -0.003884849088554687
410+
2036-01-01: -0.0037856146642757382
411+
2037-01-01: -0.003686360466274796
412+
2038-01-01: -0.0035870864886409226
413+
2039-01-01: -0.0034877927254609586
414+
2040-01-01: -0.0034877927254609586
415+
2041-01-01: -0.003388479170819303
416+
2042-01-01: -0.003388479170819303
417+
2043-01-01: -0.0032891458187980227
418+
2044-01-01: -0.0031897926634769647
419+
2045-01-01: -0.0031897926634769647
420+
2046-01-01: -0.003090419698933422
421+
2047-01-01: -0.0029910269192421346
422+
2048-01-01: -0.0028916143184762877
423+
2049-01-01: -0.002792181890705958
424+
2050-01-01: -0.002692729629998891
425+
2051-01-01: -0.0024937655860348684
426+
2052-01-01: -0.002394253790901746
427+
2053-01-01: -0.0021951706246258196
428+
2054-01-01: -0.0020955992415926383
429+
2055-01-01: -0.001996007984031989
430+
2056-01-01: -0.0018963968459926317
431+
2057-01-01: -0.001796765821521329
432+
2058-01-01: -0.001697114904662067
433+
2059-01-01: -0.0015974440894569453
434+
2060-01-01: -0.0015974440894569453
435+
2061-01-01: -0.0015974440894569453
436+
2062-01-01: -0.0014977533699451762
437+
2063-01-01: -0.0014977533699451762
438+
2064-01-01: -0.0014977533699451762
439+
2065-01-01: -0.0014977533699451762
440+
2066-01-01: -0.0014977533699451762
441+
2067-01-01: -0.0014977533699451762
442+
2068-01-01: -0.0014977533699451762
443+
2069-01-01: -0.0014977533699451762
444+
2070-01-01: -0.0014977533699451762
445+
2071-01-01: -0.0014977533699451762
446+
2072-01-01: -0.0014977533699451762
447+
2073-01-01: -0.0013980427401638629
448+
metadata:
449+
unit: /1
450+
label: Diesel spending litre-proxy growth
451+
reference:
452+
- title: HMRC Hydrocarbon Oils Bulletin
453+
href: https://www.gov.uk/government/statistics/hydrocarbon-oils-bulletin
454+
- title: OBR EFO March 2026 (fuel-duty receipts forecast)
455+
href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2026/
456+
- title: RAC latest average diesel price
457+
href: https://www.rac.co.uk/drive/advice/fuel-watch/
458+
325459
consumer_price_index_ahc:
326460
description: Consumer price index year-on-year growth, modified to remove housing costs.
327461
values:
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import pytest
2+
3+
from policyengine_uk import Microsimulation
4+
from policyengine_uk.system import system
5+
6+
7+
def test_fuel_duty_is_litres_times_statutory_rate():
8+
year = 2025
9+
situation = {
10+
"people": {
11+
"adult": {
12+
"age": {year: 35},
13+
},
14+
},
15+
"benunits": {
16+
"benunit": {
17+
"members": ["adult"],
18+
},
19+
},
20+
"households": {
21+
"household": {
22+
"members": ["adult"],
23+
"region": {year: "LONDON"},
24+
"petrol_spending": {year: 2_000.0},
25+
"diesel_spending": {year: 1_000.0},
26+
},
27+
},
28+
}
29+
simulation = Microsimulation(situation=situation)
30+
31+
fuel_duty = simulation.calculate("fuel_duty", year).values[0]
32+
litres = (
33+
simulation.calculate("petrol_litres", year).values[0]
34+
+ simulation.calculate("diesel_litres", year).values[0]
35+
)
36+
rate = system.parameters.gov.hmrc.fuel_duty.petrol_and_diesel(year)
37+
38+
assert fuel_duty == pytest.approx(litres * rate)

policyengine_uk/tests/test_road_fuel_volume_uprating.py

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,26 @@
66
from policyengine_uk.system import system
77

88

9-
def test_petrol_and_diesel_spending_use_road_fuel_volume_not_cpi():
9+
def test_petrol_and_diesel_spending_preserve_road_fuel_litres_not_cpi():
1010
parameters = system.parameters
1111
road_fuel_volume = (
1212
parameters.gov.economic_assumptions.yoy_growth.obr.road_fuel_volume
1313
)
14+
petrol_proxy = (
15+
parameters.gov.economic_assumptions.yoy_growth.obr.petrol_spending_litre_proxy
16+
)
17+
diesel_proxy = (
18+
parameters.gov.economic_assumptions.yoy_growth.obr.diesel_spending_litre_proxy
19+
)
20+
population = parameters.gov.economic_assumptions.yoy_growth.ons.population
21+
petrol_price = parameters.household.consumption.fuel.prices.petrol
22+
diesel_price = parameters.household.consumption.fuel.prices.diesel
1423
cpi = parameters.gov.economic_assumptions.yoy_growth.obr.consumer_price_index
1524

16-
assert road_fuel_volume(2027) < 0
17-
assert cpi(2027) > 0
25+
assert road_fuel_volume(2024) < 0
26+
assert cpi(2024) > 0
27+
assert petrol_proxy(2024) != road_fuel_volume(2024)
28+
assert diesel_proxy(2024) != road_fuel_volume(2024)
1829

1930
dataset = UKSingleYearDataset(
2031
person=pd.DataFrame(
@@ -33,25 +44,66 @@ def test_petrol_and_diesel_spending_use_road_fuel_volume_not_cpi():
3344
"tenure_type": ["OWNED_OUTRIGHT"],
3445
"council_tax": [1_500.0],
3546
"rent": [0.0],
36-
"petrol_spending": [1_000.0],
37-
"diesel_spending": [2_000.0],
47+
"household_weight": [1.0],
48+
"petrol_spending": [1_000.0 * petrol_price(2023)],
49+
"diesel_spending": [2_000.0 * diesel_price(2023)],
3850
}
3951
),
40-
fiscal_year=2026,
52+
fiscal_year=2023,
4153
)
4254

4355
extended = extend_single_year_dataset(
4456
dataset,
4557
tax_benefit_system_parameters=parameters,
46-
end_year=2027,
58+
end_year=2035,
59+
)
60+
household_2024 = extended[2024].household
61+
62+
assert household_2024["petrol_spending"].iloc[0] == pytest.approx(
63+
1_000 * petrol_price(2023) * (1 + petrol_proxy(2024))
4764
)
48-
household_2027 = extended[2027].household
65+
assert household_2024["diesel_spending"].iloc[0] == pytest.approx(
66+
2_000 * diesel_price(2023) * (1 + diesel_proxy(2024))
67+
)
68+
assert household_2024["household_weight"].iloc[0] == pytest.approx(
69+
1 + population(2024)
70+
)
71+
assert (
72+
household_2024["petrol_spending"].iloc[0]
73+
/ petrol_price(2024)
74+
* household_2024["household_weight"].iloc[0]
75+
) == pytest.approx(1_000 * (1 + road_fuel_volume(2024)))
76+
assert (
77+
household_2024["diesel_spending"].iloc[0]
78+
/ diesel_price(2024)
79+
* household_2024["household_weight"].iloc[0]
80+
) == pytest.approx(2_000 * (1 + road_fuel_volume(2024)))
81+
82+
household_2034 = extended[2034].household
83+
household_2035 = extended[2035].household
84+
85+
def weighted_litres(household, spending_variable, price_parameter, year):
86+
return (
87+
household[spending_variable].iloc[0]
88+
/ price_parameter(year)
89+
* household["household_weight"].iloc[0]
90+
)
4991

50-
assert household_2027["petrol_spending"].iloc[0] == pytest.approx(
51-
1_000 * (1 + road_fuel_volume(2027))
92+
assert weighted_litres(
93+
household_2035,
94+
"petrol_spending",
95+
petrol_price,
96+
2035,
97+
) == pytest.approx(
98+
weighted_litres(household_2034, "petrol_spending", petrol_price, 2034)
99+
* (1 + road_fuel_volume(2035))
52100
)
53-
assert household_2027["diesel_spending"].iloc[0] == pytest.approx(
54-
2_000 * (1 + road_fuel_volume(2027))
101+
assert weighted_litres(
102+
household_2035,
103+
"diesel_spending",
104+
diesel_price,
105+
2035,
106+
) == pytest.approx(
107+
weighted_litres(household_2034, "diesel_spending", diesel_price, 2034)
108+
* (1 + road_fuel_volume(2035))
55109
)
56-
assert household_2027["petrol_spending"].iloc[0] < 1_000
57-
assert household_2027["diesel_spending"].iloc[0] < 2_000
Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
from policyengine_uk.model_api import *
22

3-
STATUTORY_CONSUMER_INCIDENCE = 0.5
4-
ECONOMIC_CONSUMER_INCIDENCE = 1
5-
63

74
class fuel_duty(Variable):
85
label = "Fuel duty (cars only)"
@@ -15,9 +12,4 @@ def formula(household, period, parameters):
1512
fd = parameters(period).gov.hmrc.fuel_duty
1613
petrol_litres = household("petrol_litres", period.this_year) / MONTHS_IN_YEAR
1714
diesel_litres = household("diesel_litres", period.this_year) / MONTHS_IN_YEAR
18-
return (
19-
fd.petrol_and_diesel
20-
* (petrol_litres + diesel_litres)
21-
/ STATUTORY_CONSUMER_INCIDENCE
22-
* ECONOMIC_CONSUMER_INCIDENCE
23-
)
15+
return fd.petrol_and_diesel * (petrol_litres + diesel_litres)

policyengine_uk/variables/household/consumption/diesel_litres.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class diesel_litres(Variable):
77
entity = Household
88
definition_period = YEAR
99
value_type = float
10-
unit = GBP
10+
unit = "litre"
1111

1212
def formula(household, period, parameters):
1313
return household("diesel_spending", period) / household("diesel_price", period)

policyengine_uk/variables/household/consumption/petrol_litres.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class petrol_litres(Variable):
77
entity = Household
88
definition_period = YEAR
99
value_type = float
10-
unit = GBP
10+
unit = "litre"
1111

1212
def formula(household, period, parameters):
1313
return household("petrol_spending", period) / household("petrol_price", period)

policyengine_uk/variables/input/consumption/diesel_spending.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class diesel_spending(Variable):
1010
value_type = float
1111
unit = GBP
1212
quantity_type = FLOW
13-
uprating = "gov.economic_assumptions.indices.obr.road_fuel_volume"
13+
uprating = "gov.economic_assumptions.indices.obr.diesel_spending_litre_proxy"

policyengine_uk/variables/input/consumption/petrol_spending.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class petrol_spending(Variable):
1010
value_type = float
1111
unit = GBP
1212
quantity_type = FLOW
13-
uprating = "gov.economic_assumptions.indices.obr.road_fuel_volume"
13+
uprating = "gov.economic_assumptions.indices.obr.petrol_spending_litre_proxy"

0 commit comments

Comments
 (0)