Skip to content

Commit f113173

Browse files
authored
Merge pull request #1561 from PolicyEngine/codex/fix-1397
Add abolish benefit cap scenario
2 parents 4979e75 + a7151d4 commit f113173

5 files changed

Lines changed: 74 additions & 1 deletion

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added an `abolish_benefit_cap` scenario for benefit-cap removal analysis.

docs/book/usage/scenarios.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,18 @@ reformed_cap = reformed_sim.calculate("benefit_cap", 2026).mean()
268268
print(f"Benefit cap - frozen: £{baseline_cap:.0f}/year, indexed: £{reformed_cap:.0f}/year")
269269
```
270270

271+
If you want to remove the cap entirely for a poverty-analysis package, PolicyEngine
272+
UK also exposes a reusable scenario:
273+
274+
```python
275+
from policyengine_uk import Simulation
276+
from policyengine_uk.scenarios import abolish_benefit_cap
277+
278+
sim = Simulation(situation=benefit_cap_family, scenario=abolish_benefit_cap)
279+
benefit_cap_reduction = sim.calculate("benefit_cap_reduction", 2026).mean()
280+
print(f"Benefit cap reduction after abolition: £{benefit_cap_reduction:.0f}")
281+
```
282+
271283
## Advanced scenario techniques
272284

273285
### Time-varying parameters
@@ -571,4 +583,4 @@ for name, scenario in scenarios:
571583
- Consider unintended interactions between different policy areas
572584
```
573585

574-
These techniques let you model complex policy changes that go beyond simple parameter adjustments. Simulation modifiers give you complete control over how the tax-benefit system works, allowing you to implement everything from gradual phase-outs to dynamic eligibility changes. The key is to understand the underlying data structures and use them thoughtfully to represent real policy proposals.
586+
These techniques let you model complex policy changes that go beyond simple parameter adjustments. Simulation modifiers give you complete control over how the tax-benefit system works, allowing you to implement everything from gradual phase-outs to dynamic eligibility changes. The key is to understand the underlying data structures and use them thoughtfully to represent real policy proposals.

policyengine_uk/scenarios/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from .abolish_benefit_cap import abolish_benefit_cap
12
from .pip_reform import reform_pip_phase_in
23
from .reindex_benefit_cap import reindex_benefit_cap
34
from .repeal_two_child_limit import repeal_two_child_limit
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import numpy as np
2+
3+
from policyengine_uk.model_api import Scenario
4+
5+
abolish_benefit_cap = Scenario(
6+
parameter_changes={
7+
"gov.dwp.benefit_cap.single.in_london": {"year:2026:10": np.inf},
8+
"gov.dwp.benefit_cap.single.outside_london": {"year:2026:10": np.inf},
9+
"gov.dwp.benefit_cap.non_single.in_london": {"year:2026:10": np.inf},
10+
"gov.dwp.benefit_cap.non_single.outside_london": {"year:2026:10": np.inf},
11+
}
12+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import numpy as np
2+
3+
from policyengine_uk import Simulation
4+
from policyengine_uk.scenarios import abolish_benefit_cap
5+
6+
BENEFIT_CAP_FAMILY = {
7+
"people": {
8+
"parent1": {"age": {2026: 30}, "employment_income": {2026: 0}},
9+
"parent2": {"age": {2026: 28}, "employment_income": {2026: 0}},
10+
"child1": {"age": {2026: 6}},
11+
"child2": {"age": {2026: 3}},
12+
"child3": {"age": {2026: 1}},
13+
},
14+
"benunits": {
15+
"family": {"members": ["parent1", "parent2", "child1", "child2", "child3"]}
16+
},
17+
"households": {
18+
"home": {
19+
"members": ["parent1", "parent2", "child1", "child2", "child3"],
20+
"rent": {2026: 24_000},
21+
}
22+
},
23+
}
24+
25+
26+
class TestAbolishBenefitCapScenario:
27+
def test_removes_benefit_cap_reduction(self):
28+
baseline = Simulation(situation=BENEFIT_CAP_FAMILY)
29+
reformed = Simulation(
30+
situation=BENEFIT_CAP_FAMILY, scenario=abolish_benefit_cap
31+
)
32+
33+
baseline_reduction = baseline.calculate("benefit_cap_reduction", 2026)
34+
baseline_uc = baseline.calculate("universal_credit", 2026)
35+
reformed_cap = reformed.calculate("benefit_cap", 2026)
36+
reformed_reduction = reformed.calculate("benefit_cap_reduction", 2026)
37+
reformed_uc = reformed.calculate("universal_credit", 2026)
38+
reformed_uc_pre_cap = reformed.calculate(
39+
"universal_credit_pre_benefit_cap", 2026
40+
)
41+
42+
assert baseline_reduction[0] > 0
43+
assert np.isfinite(baseline.calculate("benefit_cap", 2026)[0])
44+
assert np.isinf(reformed_cap[0])
45+
assert reformed_reduction.tolist() == [0.0]
46+
assert reformed_uc.tolist() == reformed_uc_pre_cap.tolist()
47+
assert reformed_uc[0] > baseline_uc[0]

0 commit comments

Comments
 (0)