Skip to content

Commit 6849f4b

Browse files
committed
Calibrate fuel litre proxies to road clearances
1 parent 3dc97b4 commit 6849f4b

3 files changed

Lines changed: 93 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Calibrate imputed petrol and diesel litre proxies to HMRC/OBR road-fuel clearances before publishing enhanced FRS datasets.

policyengine_uk_data/datasets/imputations/consumption.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,58 @@ def fuel_spending_litre_proxy_uprating(
617617
)
618618

619619

620+
def _fuel_litre_proxy_mlitres(
621+
household: pd.DataFrame,
622+
fiscal_year: int,
623+
parameters=None,
624+
) -> float:
625+
"""Return weighted petrol + diesel litres represented by spending proxies."""
626+
from policyengine_uk.system import system
627+
628+
if parameters is None:
629+
parameters = system.parameters
630+
631+
total_litres = 0.0
632+
for variable, price_parameter_name in FUEL_PRICE_PARAMETER_NAME.items():
633+
price = getattr(
634+
parameters.household.consumption.fuel.prices,
635+
price_parameter_name,
636+
)(fiscal_year)
637+
total_litres += household[variable] / price
638+
return (total_litres * household["household_weight"]).sum() / 1_000_000
639+
640+
641+
def calibrate_fuel_litre_proxies_to_road_fuel(
642+
household: pd.DataFrame,
643+
fiscal_year: int,
644+
parameters=None,
645+
) -> float:
646+
"""Scale imputed fuel proxies to HMRC/OBR road-fuel litre controls.
647+
648+
PolicyEngine UK derives petrol and diesel litres from spending divided by
649+
pump prices. Applying one multiplicative factor to petrol and diesel
650+
spending preserves the household distribution while making the resulting
651+
litres reconcile to the external fuel-duty volume benchmark.
652+
"""
653+
from policyengine_uk_data.sources.road_fuel_volume import (
654+
road_fuel_clearances_mlitres,
655+
)
656+
657+
actual_mlitres = _fuel_litre_proxy_mlitres(
658+
household,
659+
fiscal_year,
660+
parameters=parameters,
661+
)
662+
if actual_mlitres <= 0:
663+
return 1.0
664+
665+
target_mlitres = road_fuel_clearances_mlitres(end_year=fiscal_year)[fiscal_year]
666+
scale = target_mlitres / actual_mlitres
667+
for variable in FUEL_PRICE_PARAMETER_NAME:
668+
household[variable] *= scale
669+
return scale
670+
671+
620672
def save_imputation_models():
621673
from policyengine_uk_data.utils.qrf import QRF
622674

@@ -739,5 +791,10 @@ def _wmean(arr, mask):
739791
dataset.household["petrol_spending"][no_fuel] = 0
740792
dataset.household["diesel_spending"][no_fuel] = 0
741793

794+
calibrate_fuel_litre_proxies_to_road_fuel(
795+
dataset.household,
796+
int(dataset.time_period),
797+
)
798+
742799
dataset.validate()
743800
return dataset

policyengine_uk_data/tests/test_road_fuel_volume_uprating.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
"""Tests for road-fuel volume uprating. See #402."""
22

33
import pandas as pd
4+
import pytest
45

56
from policyengine_uk_data.datasets.imputations.consumption import (
67
CONSUMPTION_MODEL_FILENAME,
78
IMPUTATIONS,
9+
_fuel_litre_proxy_mlitres,
10+
calibrate_fuel_litre_proxies_to_road_fuel,
811
fuel_spending_litre_proxy_uprating,
912
uprate_lcfs_table,
1013
)
@@ -148,6 +151,38 @@ def test__given_obr_2027_volume__then_rate_difference_matches_cost_benchmark():
148151
assert round(benchmark_cost_gbp_bn, 2) == 3.12
149152

150153

154+
def test__given_imputed_fuel_proxies__then_calibration_matches_road_fuel_litres():
155+
# Given
156+
from policyengine_uk.system import system
157+
158+
year = 2027
159+
target_mlitres = road_fuel_clearances_mlitres()[year]
160+
petrol_price = system.parameters.household.consumption.fuel.prices.petrol(year)
161+
diesel_price = system.parameters.household.consumption.fuel.prices.diesel(year)
162+
household = pd.DataFrame(
163+
{
164+
"household_weight": [1.0, 1.0],
165+
"petrol_spending": [
166+
target_mlitres * 1_000_000 * petrol_price,
167+
0.0,
168+
],
169+
"diesel_spending": [
170+
0.0,
171+
target_mlitres * 1_000_000 * diesel_price,
172+
],
173+
}
174+
)
175+
176+
# When
177+
scale = calibrate_fuel_litre_proxies_to_road_fuel(household, year)
178+
179+
# Then
180+
assert scale == pytest.approx(0.5)
181+
assert _fuel_litre_proxy_mlitres(household, year) == pytest.approx(
182+
target_mlitres
183+
)
184+
185+
151186
def test__given_year_after_obr_horizon__then_last_forecast_is_carried_forward():
152187
# When
153188
series = road_fuel_clearances_mlitres(end_year=END_YEAR)

0 commit comments

Comments
 (0)