| name | policyengine-uk |
| description | PolicyEngine-UK tax and benefit microsimulation patterns, situation creation, and common workflows |
PolicyEngine-UK
PolicyEngine-UK models the UK tax and benefit system, including devolved variations for Scotland and Wales.
For Users 👥
What is PolicyEngine-UK?
PolicyEngine-UK is the "calculator" for UK taxes and benefits. When you use policyengine.org/uk, PolicyEngine-UK runs behind the scenes.
What it models:
Direct taxes:
- Income tax (UK-wide, Scottish, and Welsh variations)
- National Insurance (Classes 1, 2, 4)
- Capital gains tax
- Dividend tax
Property and transaction taxes:
- Council Tax
- Stamp Duty Land Tax (England/NI)
- Land and Buildings Transaction Tax (Scotland)
- Land Transaction Tax (Wales)
Universal Credit:
- Standard allowance
- Child elements
- Housing cost element
- Childcare costs element
- Carer element
- Work capability elements
Legacy benefits (being phased out):
- Working Tax Credit
- Child Tax Credit
- Income Support
- Income-based JSA/ESA
- Housing Benefit
Other benefits:
- Child Benefit
- Pension Credit
- Personal Independence Payment (PIP)
- Disability Living Allowance (DLA)
- Attendance Allowance
- State Pension
See full list: https://policyengine.org/uk/parameters
Understanding Variables
When you see results in PolicyEngine, these are variables:
Income variables:
employment_income - Gross employment earnings/salary
self_employment_income - Self-employment profits
pension_income - Private pension income
property_income - Rental income
savings_interest_income - Interest from savings
dividend_income - Dividend income
Tax variables:
income_tax - Total income tax liability
national_insurance - Total NI contributions
council_tax - Council tax liability
Benefit variables:
universal_credit - Universal Credit amount
child_benefit - Child Benefit amount
pension_credit - Pension Credit amount
working_tax_credit - Working Tax Credit (legacy)
child_tax_credit - Child Tax Credit (legacy)
Summary variables:
household_net_income - Income after taxes and benefits
disposable_income - Income after taxes
equivalised_household_net_income - Adjusted for household size
For Analysts 📊
Installation and Setup
pip install policyengine-uk
uv pip install policyengine-uk
Quick Start
from policyengine_uk import Simulation
situation = {
"people": {
"person": {
"age": {2025: 30},
"employment_income": {2025: 30000}
}
},
"benunits": {
"benunit": {
"members": ["person"]
}
},
"households": {
"household": {
"members": ["person"],
"region": {2025: "LONDON"}
}
}
}
sim = Simulation(situation=situation)
income_tax = sim.calculate("income_tax", 2025)[0]
universal_credit = sim.calculate("universal_credit", 2025)[0]
print(f"Income tax: £{income_tax:,.0f}")
print(f"Universal Credit: £{universal_credit:,.0f}")
Web App to Python
Web app URL:
policyengine.org/uk/household?household=12345
Equivalent Python (conceptually):
The household ID represents a situation dictionary. To replicate in Python, you'd create a similar situation.
When to Use This Skill
- Creating household situations for tax/benefit calculations
- Running microsimulations with PolicyEngine-UK
- Analyzing policy reforms and their impacts
- Building tools that use PolicyEngine-UK (calculators, analysis notebooks)
- Debugging PolicyEngine-UK calculations
For Contributors 💻
Repository
Location: PolicyEngine/policyengine-uk
To see current implementation:
git clone https://github.com/PolicyEngine/policyengine-uk
cd policyengine-uk
tree policyengine_uk/
Key directories:
ls policyengine_uk/
Core Concepts
1. Situation Dictionary Structure
PolicyEngine UK requires a nested dictionary defining household composition:
situation = {
"people": {
"person_id": {
"age": {2025: 35},
"employment_income": {2025: 30000},
}
},
"benunits": {
"benunit_id": {
"members": ["person_id", ...]
}
},
"households": {
"household_id": {
"members": ["person_id", ...],
"region": {2025: "SOUTH_EAST"}
}
}
}
Key Rules:
- All entities must have consistent member lists
- Use year keys for all values:
{2025: value}
- Region must be one of the ITL 1 regions (see below)
- All monetary values in pounds (not pence)
- UK tax year runs April 6 to April 5 (but use calendar year in code)
Important Entity Difference:
- UK uses benunits (benefit units): a single adult OR couple + dependent children
- This is the assessment unit for most means-tested benefits
- Unlike US which uses families/marital_units/tax_units/spm_units
2. Creating Simulations
from policyengine_uk import Simulation
simulation = Simulation(situation=situation)
income_tax = simulation.calculate("income_tax", 2025)
universal_credit = simulation.calculate("universal_credit", 2025)
household_net_income = simulation.calculate("household_net_income", 2025)
Common Variables:
Income:
employment_income - Gross employment earnings
self_employment_income - Self-employment profits
pension_income - Private pension income
property_income - Rental income
savings_interest_income - Interest income
dividend_income - Dividend income
miscellaneous_income - Other income sources
Tax Outputs:
income_tax - Total income tax liability
national_insurance - Total NI contributions
council_tax - Council tax liability
VAT - Value Added Tax paid
Benefits:
universal_credit - Universal Credit
child_benefit - Child Benefit
pension_credit - Pension Credit
working_tax_credit - Working Tax Credit (legacy)
child_tax_credit - Child Tax Credit (legacy)
personal_independence_payment - PIP
attendance_allowance - Attendance Allowance
state_pension - State Pension
Summary:
household_net_income - Income after taxes and benefits
disposable_income - Income after taxes
equivalised_household_net_income - Adjusted for household size
3. Using Axes for Parameter Sweeps
To vary a parameter across multiple values:
situation = {
"axes": [[{
"name": "employment_income",
"count": 1001,
"min": 0,
"max": 100000,
"period": 2025
}]]
}
simulation = Simulation(situation=situation)
incomes = simulation.calculate("employment_income", 2025)
taxes = simulation.calculate("income_tax", 2025)
Important: Remove axes before creating single-point simulations:
situation_single = situation.copy()
situation_single.pop("axes", None)
simulation = Simulation(situation=situation_single)
4. Policy Reforms
from policyengine_uk import Simulation
reform = {
"gov.hmrc.income_tax.rates.uk.brackets[0].rate": {
"2025-01-01.2100-12-31": 0.25
}
}
simulation = Simulation(situation=situation, reform=reform)
Common Patterns
Pattern 1: Single Person Household Calculation
from policyengine_uk import Simulation
situation = {
"people": {
"person": {
"age": {2025: 30},
"employment_income": {2025: 30000}
}
},
"benunits": {
"benunit": {
"members": ["person"]
}
},
"households": {
"household": {
"members": ["person"],
"region": {2025: "LONDON"}
}
}
}
sim = Simulation(situation=situation)
income_tax = sim.calculate("income_tax", 2025)[0]
national_insurance = sim.calculate("national_insurance", 2025)[0]
universal_credit = sim.calculate("universal_credit", 2025)[0]
Pattern 2: Couple with Children
situation = {
"people": {
"parent_1": {
"age": {2025: 35},
"employment_income": {2025: 35000}
},
"parent_2": {
"age": {2025: 33},
"employment_income": {2025: 25000}
},
"child_1": {
"age": {2025: 8}
},
"child_2": {
"age": {2025: 5}
}
},
"benunits": {
"benunit": {
"members": ["parent_1", "parent_2", "child_1", "child_2"]
}
},
"households": {
"household": {
"members": ["parent_1", "parent_2", "child_1", "child_2"],
"region": {2025: "NORTH_WEST"}
}
}
}
sim = Simulation(situation=situation)
child_benefit = sim.calculate("child_benefit", 2025)[0]
universal_credit = sim.calculate("universal_credit", 2025)[0]
Pattern 3: Marginal Tax Rate Analysis
situation_with_axes = {
"people": {
"person": {
"age": {2025: 30}
}
},
"benunits": {"benunit": {"members": ["person"]}},
"households": {
"household": {
"members": ["person"],
"region": {2025: "LONDON"}
}
},
"axes": [[{
"name": "employment_income",
"count": 1001,
"min": 0,
"max": 100000,
"period": 2025
}]]
}
sim = Simulation(situation=situation_with_axes)
incomes = sim.calculate("employment_income", 2025)
net_incomes = sim.calculate("household_net_income", 2025)
import numpy as np
mtr = 1 - (np.gradient(net_incomes) / np.gradient(incomes))
Pattern 4: Regional Comparison
regions = ["LONDON", "SCOTLAND", "WALES", "NORTH_EAST"]
results = {}
for region in regions:
situation = create_situation(region=region, income=30000)
sim = Simulation(situation=situation)
results[region] = {
"income_tax": sim.calculate("income_tax", 2025)[0],
"national_insurance": sim.calculate("national_insurance", 2025)[0],
"total_tax": sim.calculate("income_tax", 2025)[0] +
sim.calculate("national_insurance", 2025)[0]
}
Pattern 5: Policy Reform Impact
from policyengine_uk import Microsimulation, Reform
class IncreaseBasicRate(Reform):
def apply(self):
def modify_parameters(parameters):
parameters.gov.hmrc.income_tax.rates.uk.brackets[0].rate.update(
period="year:2025:10", value=0.25
)
return parameters
self.modify_parameters(modify_parameters)
baseline = Microsimulation()
reformed = Microsimulation(reform=IncreaseBasicRate)
baseline_revenue = baseline.calc("income_tax", 2025).sum()
reformed_revenue = reformed.calc("income_tax", 2025).sum()
revenue_change = (reformed_revenue - baseline_revenue) / 1e9
baseline_net_income = baseline.calc("household_net_income", 2025)
reformed_net_income = reformed.calc("household_net_income", 2025)
Helper Scripts
This skill includes helper scripts in the scripts/ directory:
from policyengine_uk_skills.situation_helpers import (
create_single_person,
create_couple,
create_family_with_children,
add_region
)
situation = create_single_person(
income=30000,
region="LONDON",
age=30
)
situation = create_couple(
income_1=35000,
income_2=25000,
region="SCOTLAND"
)
Common Pitfalls and Solutions
Pitfall 1: Member Lists Out of Sync
Problem: Different entities have different members
"benunits": {"benunit": {"members": ["parent"]}},
"households": {"household": {"members": ["parent", "child"]}}
Solution: Keep all entity member lists consistent:
all_members = ["parent", "child"]
"benunits": {"benunit": {"members": all_members}},
"households": {"household": {"members": all_members}}
Pitfall 2: Forgetting Year Keys
Problem: "age": 35 instead of "age": {2025: 35}
Solution: Always use year dictionary:
"age": {2025: 35},
"employment_income": {2025: 30000}
Pitfall 3: Wrong Region Format
Problem: Using lowercase or incorrect region names
Solution: Use uppercase ITL 1 region codes:
"region": {2025: "LONDON"}
"region": {2025: "SCOTLAND"}
"region": {2025: "WALES"}
"region": {2025: "NORTH_EAST"}
"region": {2025: "SOUTH_EAST"}
Pitfall 4: Axes Persistence
Problem: Axes remain in situation when creating single-point simulation
Solution: Remove axes before single-point simulation:
situation_single = situation.copy()
situation_single.pop("axes", None)
Pitfall 5: Missing Benunits
Problem: Forgetting to include benunits (benefit units)
Solution: Always include benunits in UK simulations:
situation = {
"people": {...},
"benunits": {"benunit": {"members": [...]}},
"households": {...}
}
Regions in PolicyEngine UK
UK uses ITL 1 (International Territorial Level 1, formerly NUTS 1) regions:
Regions:
NORTH_EAST - North East England
NORTH_WEST - North West England
YORKSHIRE - Yorkshire and the Humber
EAST_MIDLANDS - East Midlands
WEST_MIDLANDS - West Midlands
EAST_OF_ENGLAND - East of England
LONDON - London
SOUTH_EAST - South East England
SOUTH_WEST - South West England
WALES - Wales
SCOTLAND - Scotland
NORTHERN_IRELAND - Northern Ireland
Regional Tax Variations:
Scotland:
- Has devolved income tax with 6 bands (starter 19%, basic 20%, intermediate 21%, higher 42%, advanced 45%, top 47%)
- Scottish residents automatically calculated with Scottish rates
Wales:
- Has Welsh Rate of Income Tax (WRIT)
- Currently maintains parity with England/NI rates
England/Northern Ireland:
- Standard UK rates: basic 20%, higher 40%, additional 45%
Key Parameters and Values (2025/26)
Income Tax
- Personal Allowance: £12,570
- Basic rate threshold: £50,270
- Higher rate threshold: £125,140
- Rates: 20% (basic), 40% (higher), 45% (additional)
- Personal allowance tapering: £1 reduction for every £2 over £100,000
National Insurance (Class 1)
- Lower Earnings Limit: £6,396/year
- Primary Threshold: £12,570/year
- Upper Earnings Limit: £50,270/year
- Rates: 12% (between primary and upper), 2% (above upper)
Universal Credit
- Standard allowance: Varies by single/couple and age
- Taper rate: 55% (rate at which UC reduced as income increases)
- Work allowance: Amount you can earn before UC reduced
Child Benefit
- First child: Higher rate
- Subsequent children: Lower rate
- High Income Charge: Tapered withdrawal starting at £60,000
Version Compatibility
- Use
policyengine-uk>=1.0.0 for 2025 calculations
- Check version:
import policyengine_uk; print(policyengine_uk.__version__)
- Different years may require different package versions
Debugging Tips
-
Enable tracing:
simulation.trace = True
result = simulation.calculate("variable_name", 2025)
-
Check intermediate calculations:
gross_income = simulation.calculate("gross_income", 2025)
disposable_income = simulation.calculate("disposable_income", 2025)
-
Verify situation structure:
import json
print(json.dumps(situation, indent=2))
-
Test with PolicyEngine web app:
- Go to policyengine.org/uk/household
- Enter same inputs
- Compare results
Additional Resources
Examples Directory
See examples/ for complete working examples:
single_person.yaml - Single person household
couple.yaml - Couple without children
family_with_children.yaml - Family with dependents
universal_credit_sweep.yaml - Analyzing UC with axes
UK Legislation References
All UK parameters MUST have legislation.gov.uk references with exact section links.
Finding Legislation References
UK legislation is consolidated at legislation.gov.uk. Key sources:
Primary legislation (Acts of Parliament):
- Welfare Reform Act 2012 - Universal Credit
- Social Security Contributions and Benefits Act 1992
- Income Tax Act 2007
- Taxation of Chargeable Gains Act 1992
Secondary legislation (Statutory Instruments):
- Universal Credit Regulations 2013 (SI 2013/376)
- The Social Security (Claims and Payments) Regulations 1987
- Income Tax (Earnings and Pensions) Act 2003
Reference Format for UK Parameters
metadata:
reference:
- title: Universal Credit Regulations 2013, Schedule 4, Table 3
href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4
- title: Welfare Reform Act 2012, Section 8
href: https://www.legislation.gov.uk/ukpga/2012/5/section/8
Universal Credit Legislation References
Universal Credit parameters are primarily in:
- The Universal Credit Regulations 2013 (SI 2013/376)
- Schedule 1: Capital limits and disregards
- Schedule 4: Standard allowances and amounts
- Schedule 5: Work capability amounts
- Regulation 22: Work allowance
Example - Standard Allowance:
description: Standard allowance for single claimants under 25.
values:
2024-04-01: 311.68
metadata:
unit: currency-GBP
period: month
label: UC standard allowance (single, under 25)
reference:
- title: Universal Credit Regulations 2013, Schedule 4, Table 3
href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4
Uprating Orders
UK benefits are uprated annually via Statutory Instruments. Current rates are in:
- The Social Security Benefits Up-rating Order (annual)
- The Universal Credit (Transitional Provisions) (Amendment) Regulations (as needed)
When updating parameter values, reference the specific uprating order:
reference:
- title: The Social Security Benefits Up-rating Order 2024, Schedule, Part II
href: https://www.legislation.gov.uk/uksi/2024/217/schedule/part/II
Finding the Right Section
- Go to legislation.gov.uk
- Search for the regulation (e.g., "Universal Credit Regulations 2013")
- Navigate to the relevant section/schedule
- Use the URL which includes the section (e.g.,
/schedule/4 or /section/8)
- Include section details in the title for clarity
Key Differences from US System
- Benefit Units: UK uses
benunits (single/couple + children) instead of US multiple entity types
- Universal Credit: Consolidated means-tested benefit (vs separate SNAP, TANF, etc. in US)
- National Insurance: Separate from income tax with own thresholds (vs US Social Security tax)
- Devolved Taxes: Scotland and Wales have different income tax rates
- Tax Year: April 6 to April 5 (vs calendar year in US)
- No State Variation: Council Tax is local, but most taxes/benefits are national (vs 50 US states)