An undated State Pension rate reform moves State Pension the wrong way. Raising the new State Pension to £300 a week without a date lowers a new State Pension pensioner's modelled State Pension. The same reform with a date works.
Reproduction (origin/main 325d585)
Data: a 2-person UKSingleYearDataset for 2024-25, with these person columns:
- men aged 70 and 90
months_since_last_birthday 3
state_pension_reported £240 and £250 a week × 52
Household columns: region LONDON, tenure_type OWNED_OUTRIGHT, rent 0, council_tax 0.
Run with Microsimulation(dataset=..., reform=...) and read state_pension for 2027:
| reform |
NEW type (aged 70) |
BASIC type (aged 90) |
| baseline |
14,145.0 |
14,734.2 |
{"gov.dwp.state_pension.new_state_pension.amount": 300} |
12,480.0 |
14,734.2 |
{"...new_state_pension.amount": {"2026-01-01.2100-12-31": 300}} |
16,925.9 |
14,734.2 |
{"gov.dwp.state_pension.basic_state_pension.amount": 250} |
14,145.0 |
13,000.0 |
Mechanism
Simulation.apply_parameter_changes (policyengine_uk/simulation.py) applies a non-dict value with p.update(period="year:2000:100", ...), so it also rewrites the data year's rate.
basic_state_pension, new_state_pension and additional_state_pension split the data year's state_pension_reported at the data year's rate taken from the reformed parameters. They then uprate by the period's rate over that data-year rate.
- Under an undated reform both rates are the reformed value, so the uprating ratio is 1, and the record gets its un-uprated reported amount whatever the new rate.
- A rate of 0 moves the whole reported amount into
additional_state_pension (uprating falls back to 1), so a reform setting the flat rate to 0 leaves State Pension unchanged.
Found in review of #1922.
Fix
Split the reported amount at the baseline (legislated) data-year rate, e.g. from the simulation's baseline parameters, and scale by the reformed period rate over that baseline rate. Do this in all three components, and keep #1922's partition invariant. Add tests for undated and dated reforms (rate up, rate down, rate 0) that check state_pension, not just the components. Check how policyengine.py and the API send reforms, to judge exposure.
An undated State Pension rate reform moves State Pension the wrong way. Raising the new State Pension to £300 a week without a date lowers a new State Pension pensioner's modelled State Pension. The same reform with a date works.
Reproduction (origin/main 325d585)
Data: a 2-person
UKSingleYearDatasetfor 2024-25, with these person columns:months_since_last_birthday3state_pension_reported£240 and £250 a week × 52Household columns:
regionLONDON,tenure_typeOWNED_OUTRIGHT,rent0,council_tax0.Run with
Microsimulation(dataset=..., reform=...)and readstate_pensionfor 2027:{"gov.dwp.state_pension.new_state_pension.amount": 300}{"...new_state_pension.amount": {"2026-01-01.2100-12-31": 300}}{"gov.dwp.state_pension.basic_state_pension.amount": 250}Mechanism
Simulation.apply_parameter_changes(policyengine_uk/simulation.py) applies a non-dict value withp.update(period="year:2000:100", ...), so it also rewrites the data year's rate.basic_state_pension,new_state_pensionandadditional_state_pensionsplit the data year'sstate_pension_reportedat the data year's rate taken from the reformed parameters. They then uprate by the period's rate over that data-year rate.additional_state_pension(upratingfalls back to 1), so a reform setting the flat rate to 0 leaves State Pension unchanged.Found in review of #1922.
Fix
Split the reported amount at the baseline (legislated) data-year rate, e.g. from the simulation's baseline parameters, and scale by the reformed period rate over that baseline rate. Do this in all three components, and keep #1922's partition invariant. Add tests for undated and dated reforms (rate up, rate down, rate 0) that check
state_pension, not just the components. Check how policyengine.py and the API send reforms, to judge exposure.