model 05 — overlapping generations · psl-og · UK · local
Model long-run behavioural change.
Estimate how reforms affect work, saving, investment, and public finances over decades, for the UK.
From a few lines of Python to a full UK transition path.
OG-UK is open source, and every step below that carries logic ships as
a runnable file under /olg/code/
on this site. Pick a step, run it on its own, swap the reform. Full context lives in
PSL's OG-UK repository.
score_reform with model="og",
dynamic_reform_impact) return install/CLI instructions
instead of results. Locally, until
OG-UK#68
lands, oguk pins policyengine-uk==2.88.0 and
needs its own environment:
pe-macro og-score --reform '...' --json > og.json
there, then
pe-macro dynamic-score --reform '...' --og-payload og.json
in the main one. Both need a HUGGING_FACE_TOKEN with
access to the gated UK microdata. What the pin means for results is on
the Validation tab.
Install
A Python package (3.11+); the recommended setup uses
uv. The PolicyEngine
enhanced-FRS microdata downloads on first run and is gated: you need
a HUGGING_FACE_TOKEN with read access to
policyengine/policyengine-uk-data.
runnable file: 01_install.sh.
prefer conda?
conda env create -f environment.yml && conda activate oguk-dev && pip install -e .
works too.
Define a reform
Reforms use the PolicyEngine API: pick a parameter from the UK tax-and-benefit rule book, give it a new value and a start date. Anything PolicyEngine can represent — rates, thresholds, allowance tapers, new benefits — flows straight through. To simulate a different reform, swap the parameter path and value; the rest of the pipeline does not change.
runnable file: 02_reform.py —
every later script imports REFORM from it.
Stack several ParameterValues in one Policy
to score a package. Common parameter paths:
gov.hmrc.income_tax.rates.uk[0..2].rate (basic 20%,
higher 40%, additional 45%),
gov.hmrc.income_tax.allowances.personal_allowance.amount
(£12,570), and
gov.hmrc.national_insurance.class_1.rates.employee.main
/ .higher.
Shocks that are not tax-and-benefit statute go through
param_overrides, a dict of OG-Core parameters applied on
top of everything else. Corporation tax lives here, not in
PolicyEngine — it is a structural parameter of the macro model.
Z (TFP) for productivity shocks — never
g_y_annual, which is the balanced-growth normalisation
the model detrends by, not a productivity lever.
Solve the long-run steady state
The fastest way to see what a reform does.
solve_steady_state finds the long-run equilibrium of the
UK economy under a given policy: the prices, quantities and tax
revenues that emerge once the economy has fully adjusted. Run it once
for the baseline, once for the reform; the difference is the answer.
The output shown is illustrative — the format matches 03_steady_state.py, but the numbers depend on the calibration date and your data release. On the fast configuration, budget ~17 minutes per steady state.
The main configuration dials and their trade-offs:
solve_steady_state()
long-run answer, ~17 min per solve
run_transition_path()
full 60-year path, multiple hours for baseline + reform
"pooled"
one tax function for all ages (fastest, most stable)
"brackets"
one per age bracket (4 groups, split at state pension age)
"each"
one per single year of age (80 functions, slowest)
False
one production sector
True
eight industries calibrated from ONS Blue Book supply-and-use tables by SIC section; needed for sector-level questions like energy price shocks
start with pooled + single-sector and scale up only once
the fast run answers your question. non-converging solve? raise
max_iter (default 250), fall back to pooled,
and check whether the reform is extreme — OLG models can diverge on
very large shocks.
Run the year-by-year transition path
The steady state tells you where the economy ends up. The transition path tells you how it gets there — year by year, 60 periods by default (configurable). This is what produced the reform paths in the Showcase tab. The transition costs more compute — the model solves every cohort's lifetime under rational expectations — so OG-UK uses Dask to parallelise across CPU cores.
runnable file: 04_transition.py.
transition paths are available through the oguk API but
are not wired into the PolicyEngine Macro CLI yet —
the CLI is steady-state only (see step 7).
From abstract units to pounds
OG-UK solves in dimensionless model units. To translate them into
figures a policymaker can read, map_to_real_world()
returns a MacroImpact carrying, for each aggregate, the
reform level, the change from baseline, and the percent change — all
current-price £bn — plus baseline and reform interest rates:
.gdp/.gdp_change/.gdp_pct.consumption,.investment,.government— same triple each.tax_revenue,.debt— same triple each.r_baseline,.r_reform— steady-state interest rates
The £bn mapping is GDP-anchored: one scale factor (real-world GDP ÷ model GDP) converts model-unit changes, with levels anchored to live ONS series (GDP, consumption, investment, government, debt ratio) and HMRC total receipts, falling back to cached values if ONS is unreachable. The transition-path variant returns NumPy arrays indexed by year.
Bring in the eight industry sectors
Pass multi_sector=True and the same call returns the
breakdown across the eight UK industry sectors (energy,
manufacturing, construction, trade & transport, info &
finance, real estate, business services, public & other) —
sector-level output, capital and labour alongside the macro
aggregates, and the basis for the industry charts in the
Showcase tab.
runnable file: 06_multi_sector.py.
Where to go next
Every step above ships as a file you can download and run on its own:
- 01_install.sh — install and token setup.
- 02_reform.py — the shared
REFORM; edit the parameter, value or start date here and every other script picks it up. - 03_steady_state.py — fastest, prints a one-line £bn impact summary.
- 04_transition.py — heavier, produces the full year-by-year path.
- 05_map_to_gbp.py — the £bn-mapping field reference.
- 06_multi_sector.py — the 8-sector calibration behind the Showcase industry views.
Through this suite, two CLI commands are wired:
pe-macro og-baseline and
pe-macro og-score --reform '{"gov.hmrc.income_tax.rates.uk[0].rate": 0.21}',
both on the fast configuration (pooled tax functions, single sector,
steady state). For more variations, the upstream
OG-UK examples
directory carries additional pipelines
(run_oguk_fast_tpi.py, run_oguk_fast_sector.py,
plot.py). Full API reference and theory documentation:
pslmodels.github.io/OG-UK.