policyengine-simulation-mechanics
ALWAYS LOAD THIS SKILL before writing any policyengine.py microsimulation code. Contains correct import paths, environment setup, dataset loading, and analysis patterns. Triggers: "write a script", "policyengine.py", "microsimulation script", "run a simulation", "load the dataset", "FRS", "EFRS", "enhanced FRS", "CPS", "enhanced CPS", "by income decile", "by tenure", "by region", "energy spending", "domestic energy", "household net income", "output_dataset", "ensure_datasets", "uk_datasets", "us_datasets", "import datasets", "from policyengine", "Simulation(dataset=", "uk_latest", "us_latest", "plotly", "analysis script", "decile breakdown", "percentile", "groupby", "weighted", "mean", "median", "p25", "p75", "tenure type", "income band", "policy reform script".
What this skill does
# PolicyEngine Simulation Mechanics
This skill covers advanced patterns for working with policyengine.py simulations, including caching, result access, and entity mapping.
## CRITICAL: Environment Setup
**Before writing any code, check the environment.** The policyengine.py package must be installed in the project's `.venv`.
```bash
# Always run from the policyengine.py repo root:
cd /path/to/policyengine.py
uv run python script.py
# Or activate first:
source .venv/bin/activate
python script.py
# NEVER use bare `pip install` — always:
uv pip install -e ".[uk]" # for UK work
uv pip install -e ".[us]" # for US work
```
**If `from policyengine.core import Simulation` fails:**
```bash
cd /path/to/policyengine.py
uv pip install -e ".[uk]"
# Then re-run with: uv run python script.py
```
## CRITICAL: Correct Import Paths
**Only these imports exist — do not guess others:**
```python
# Core simulation
from policyengine.core import Simulation
# UK model
from policyengine.tax_benefit_models.uk import (
uk_latest, # The model version (pass as tax_benefit_model_version=)
uk_model, # The model itself
PolicyEngineUKDataset,
UKYearData,
create_datasets, # Create & cache datasets from HF source
load_datasets, # Load cached datasets from disk
ensure_datasets, # Create if missing, load if present (recommended)
)
# US model
from policyengine.tax_benefit_models.us import (
us_latest,
PolicyEngineUSDataset,
ensure_datasets,
)
# Outputs
from policyengine.outputs.aggregate import Aggregate, AggregateType
from policyengine.outputs.change_aggregate import ChangeAggregate, ChangeAggregateType
# Plotting
from policyengine.utils.plotting import COLORS, format_fig
```
**There is NO:**
- `policyengine.core.dataset_registry`
- `policyengine.datasets`
- `policyengine.core.dataset_version.DatasetVersion.list()`
## UK Datasets
### Loading UK datasets
Use `ensure_datasets()` — it returns a `dict[str, PolicyEngineUKDataset]`, building files in `./data/` on first run and loading from disk on subsequent runs.
**WARNING:** `from policyengine.tax_benefit_models.uk import datasets` gives you the Python **submodule**, not a dict. Never index it like a dict.
```python
from policyengine.tax_benefit_models.uk import ensure_datasets
uk = ensure_datasets(
datasets=[
"hf://policyengine/policyengine-uk-data/frs_2023_24.h5",
"hf://policyengine/policyengine-uk-data/enhanced_frs_2023_24.h5",
],
years=[2026],
data_folder="./data",
)
efrs = uk["enhanced_frs_2023_24_2026"]
frs = uk["frs_2023_24_2026"]
```
**Dict key format:** `"{stem}_{year}"` e.g. `"enhanced_frs_2023_24_2026"`
**To force regeneration:** delete `./data/` and call `ensure_datasets()` again.
### Loading US datasets
```python
from policyengine.tax_benefit_models.us import ensure_datasets
us = ensure_datasets(
datasets=["hf://policyengine/policyengine-us-data/enhanced_cps_2024.h5"],
years=[2026],
data_folder="./data",
)
ecps = us["enhanced_cps_2024_2026"]
```
**Default US dataset:** `enhanced_cps_2024.h5` (Enhanced CPS), years 2024–2028.
### Inspecting available variables
Always inspect the dataset to find available variable names — never guess:
```python
from policyengine.tax_benefit_models.uk import ensure_datasets
uk = ensure_datasets(years=[2026], data_folder="./data")
d = uk["enhanced_frs_2023_24_2026"]
# Input variables (present in raw data)
print("household:", list(d.data.household.columns))
print("person: ", list(d.data.person.columns))
print("benunit: ", list(d.data.benunit.columns))
```
**Input variables** are what's in the raw survey data — demographics, reported incomes, consumption, wealth, flags.
**Computed variables** (`household_net_income`, `income_tax`, `universal_credit`, etc.) are **not** in the raw dataset — they are calculated by the simulation. To see what's available after running:
```python
from policyengine.core import Simulation
from policyengine.tax_benefit_models.uk import uk_latest
sim = Simulation(dataset=d, tax_benefit_model_version=uk_latest)
sim.run()
print("household (post-sim):", list(sim.output_dataset.data.household.columns))
print("person (post-sim): ", list(sim.output_dataset.data.person.columns))
```
The computed variables available are defined by `uk_latest.entity_variables` — inspect this to see the full list without running a simulation:
```python
from policyengine.tax_benefit_models.uk import uk_latest
print(uk_latest.entity_variables) # dict: entity → [variable names]
```
## For Analysts: Core Concepts
When running simulations with policyengine.py (the microsimulation package, not the API client), you work with three key components:
1. **`Simulation.ensure()`** - Smart caching to avoid redundant computation
2. **`simulation.output_dataset.data`** - Accessing calculated results
3. **`map_to_entity()`** - Converting data between entity levels (person ↔ household)
**Note:** This is for microsimulation with policyengine.py, not the policyengine Python API client (which uses `Simulation(situation=...)`).
## Simulation Lifecycle
### The Four Methods
```python
from policyengine.core import Simulation
from policyengine.tax_benefit_models.uk import uk_latest
simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
)
# Method 1: Always run (no caching)
simulation.run()
# Method 2: Run only if needed (recommended)
simulation.ensure()
# Method 3: Save results to disk
simulation.save()
# Method 4: Load results from disk
simulation.load()
```
### When to Use Each
- **`run()`**: Use when you need fresh results or parameters changed
- **`ensure()`**: Use for iterative development (checks cache → disk → run)
- **`save()`**: Use to persist large simulation results
- **`load()`**: Use to resume from previous session
### How ensure() Works
```python
def ensure(self):
# 1. Check in-memory LRU cache (100 simulations)
cached = _cache.get(self.id)
if cached:
self.output_dataset = cached.output_dataset
return
# 2. Try loading from disk
try:
self.tax_benefit_model_version.load(self)
except Exception:
# 3. Only run if both cache and disk fail
self.run()
self.save()
# 4. Add to cache for next ensure() call
_cache.add(self.id, self)
```
**Performance impact:**
- First call: Full simulation runtime (seconds to minutes)
- Same session: Instant (in-memory cache)
- New session: Fast (disk load, no recomputation)
### Example: Reusing Baseline Across Reforms
```python
# Run baseline once
baseline = Simulation(dataset=dataset, tax_benefit_model_version=uk_latest)
baseline.ensure() # First call: runs simulation
baseline.save() # Persist to disk
# Test multiple reforms
for reform in [reform1, reform2, reform3]:
baseline.ensure() # Instant from cache!
reform_sim = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
policy=reform
)
reform_sim.run() # Only reform needs to run
# Compare results...
```
## Accessing Results: output_dataset.data
After running a simulation, all calculated variables are in `simulation.output_dataset.data`.
### Structure (UK Example)
```python
simulation.run()
# Access output container
output = simulation.output_dataset.data
# Entity-level MicroDataFrames
output.person # Person-level results
output.benunit # Benefit unit results
output.household # Household-level results
```
### US Entity Structure
```python
# US has more entities
output.person
output.tax_unit # Federal tax filing unit
output.spm_unit # Supplemental Poverty Measure unit
output.family # Census family definition
output.marital_unit # Married couple or single
output.household
```
### Available Variables
Each dataframe contains **input variables** + **calculated variables**:
```python
# Person-level (UK)
print(output.person.columns)
# ['person_iRelated in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.