Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Correct the SPM methodology page's account of which series each uprated input follows, and test that the page links every series an input follows.
2 changes: 1 addition & 1 deletion docs-quarto/methodology/spm-poverty.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The [MOOP decomposition page](moop-decomposition.qmd) records the design and the

Population results use the Microcosm US dataset. The current default is Build P: 57,240 Current Population Survey Annual Social and Economic Supplement households with 166,321 people, whose weights are fitted to 5,659 administrative and demographic targets (IRS Statistics of Income by state and by income, Census population estimates by age, sex and state, CMS Medicaid and Marketplace enrollment, USDA SNAP participation and benefits, SSA SSI, TANF, state tax collections and BEA national accounts). Poverty rates are never a target. That is a rule of the calibration, not an accident: a model whose weights were fitted to survey-based poverty rates could not be checked against them.

2025 values are the 2024 survey values carried forward by the model's uprating parameters: each uprated input grows by the ratio of its uprating series between the two years. Household weights grow with total population, so an input that follows a national total grows by that total divided by population ([`per_capita_uprating.py`](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/tools/per_capita_uprating.py)); its weighted total then tracks the national total. The main income sources (employment, self-employment and partnership income, Social Security, pensions and retirement account distributions, capital gains, dividends, interest, rental and farm income, and unemployment compensation) follow [IRS Statistics of Income series](https://github.com/PolicyEngine/policyengine-us/tree/main/policyengine_us/parameters/calibration/gov/irs/soi). Each series holds IRS totals through its last recorded year and a CBO projection extends it from there, so the growth applied from 2024 to 2025 is CBO's, per capita. Non-premium medical spending and the survey-reported premiums that enter SPM resources follow [CMS's projection of per-capita out-of-pocket health spending](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/calibration/gov/hhs/cms/moop_per_capita.yaml), which is already a per-person series. Rent, child care expenses, child support paid and received, the energy subsidy, real estate taxes, and household assets and debts follow [CPI-U](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/gov/bls/cpi/cpi_u.yaml). Workers' compensation follows the employment income series. Every other dollar input follows [CBO's projection of total adjusted gross income](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/calibration/gov/cbo/income_by_source.yaml), the default that [`default_uprating.py`](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/tools/default_uprating.py) assigns, per capita like every national total. Household weights follow the model's [total population series](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/calibration/gov/census/populations/total.yaml), which takes its values from CBO's demographic outlook; person and SPM unit weights read the household weight. Inputs without an uprating parameter (ages, take-up indicators, tenure, geography) carry over unchanged, so household composition is held at its 2024 state. The population is projected, not resampled.
2025 values are the 2024 survey values carried forward by the model's uprating parameters: each uprated input grows by the ratio of its uprating series between the two years. Household weights grow with total population, so an input that follows a national total grows by that total divided by population ([`per_capita_uprating.py`](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/tools/per_capita_uprating.py)); its weighted total then tracks the national total. The main income sources (employment, self-employment and partnership income, Social Security, pensions and retirement account distributions, capital gains, dividends, interest, rental and farm income, alimony received and paid, and unemployment compensation) follow [IRS Statistics of Income series](https://github.com/PolicyEngine/policyengine-us/tree/main/policyengine_us/parameters/calibration/gov/irs/soi). Each series holds IRS totals through its last recorded year and a CBO projection extends it from there, so the growth applied from 2024 to 2025 is CBO's, per capita. These health inputs follow [CMS's projection of per-capita out-of-pocket health spending](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/calibration/gov/hhs/cms/moop_per_capita.yaml), which is already a per-person series: the other medical expenses, over-the-counter health expenses and other health insurance premiums that enter SPM resources; physician services, inpatient, outpatient, emergency room, urgent care, ambulance, imaging, lab and prescription expenses; health insurance premiums in total, pre-tax, long-term and excluding Medicare Part B, and reported Medicare Part B premiums; and health savings account payroll contributions. These expenses, assets and debts follow [CPI-U](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/gov/bls/cpi/cpi_u.yaml): rent, child care and other care costs, child support paid and received, utilities, phone and broadband, tuition, homeowners insurance and association fees, transportation costs, real estate taxes and the energy subsidy; the person-level home mortgage interest input, student loan interest, investment interest expense and vehicle loan interest; bank account, stock and bond assets and the value of the primary residence; the value, equity and debt of business assets, rental property and other real estate; and vehicle equity and debt, the auto loan balance and net worth. These pay and wage-replacement inputs follow the IRS employment income series: hourly wages, tips, overtime premiums, military and government pay, the W-2 wages of qualified businesses, desired 401(k), 403(b) and IRA contributions, workers' compensation and disability benefits. Desired self-employed pension contributions follow the IRS self-employment income series. Employer-sponsored insurance premiums follow CBO's projection of employment income. Veterans' benefits follow the [Social Security cost-of-living index](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/gov/ssa/uprating.yaml). Every other uprated dollar input follows [CBO's projection of total adjusted gross income](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/calibration/gov/cbo/income_by_source.yaml), the default that [`default_uprating.py`](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/tools/default_uprating.py) assigns, per capita like every national total. Inputs on the default include tax units' first and second home mortgage interest and balances, home equity, personal property, SPM unit assets, mortgage payments, charitable donations, casualty losses, educator expenses, tax preparation fees, energy efficiency and clean energy property expenditures, state unemployment insurance wage and earnings inputs, employer payroll totals and local earnings and wage tax inputs. Household weights follow the model's [total population series](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/calibration/gov/census/populations/total.yaml), which takes its values from CBO's demographic outlook; person and SPM unit weights read the household weight. Inputs without an uprating parameter (ages, take-up indicators, tenure, geography) carry over unchanged, so household composition is held at its 2024 state. The population is projected, not resampled.

## What the model does not do

Expand Down
49 changes: 48 additions & 1 deletion policyengine_us/tests/core/test_uprating_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
national series, the income source it is part of, prices for consumption
expenses and debts, CMS per-capita spending for health costs."""

from pathlib import Path

import pytest
from policyengine_core.parameters.operations.get_parameter import get_parameter

from policyengine_us.data.economic_assumptions import MICRODATA_UPRATING_OVERRIDES
from policyengine_us.system import system
from policyengine_us.tools.per_capita_uprating import per_capita_path
from policyengine_us.tools.per_capita_uprating import DERIVED_FROM, per_capita_path

EMPLOYMENT = per_capita_path("calibration.gov.irs.soi.employment_income")
SELF_EMPLOYMENT = per_capita_path("calibration.gov.irs.soi.self_employment_income")
Expand Down Expand Up @@ -138,3 +140,48 @@ def test_alimony_series_is_labelled_as_alimony():
def test_rent_override_matches_the_rent_input():
assert MICRODATA_UPRATING_OVERRIDES["rent"] == CPI_U
assert system.variables["pre_subsidy_rent"].uprating == CPI_U


METHODOLOGY_PAGE = (
Path(__file__).parents[3] / "docs-quarto" / "methodology" / "spm-poverty.qmd"
)
PARAMETERS_FOLDER = "policyengine_us/parameters/"


def _authored_series(uprating):
"""The authored parameter behind an uprating path: a derived per-capita
series names the national total it divides."""
parameter = get_parameter(system.parameters, uprating)
return parameter.metadata.get(DERIVED_FROM, uprating)


def _series_that_inputs_follow():
return sorted(
{
_authored_series(variable.uprating)
for variable in system.variables.values()
if getattr(variable, "uprating", None)
}
)


@pytest.mark.parametrize("series", _series_that_inputs_follow())
def test_methodology_page_links_every_series_an_input_follows(series):
"""The SPM methodology page names the series each family of inputs follows
and sends every other uprated input to the default. That stays true only
while the page links every series some input follows: the parameter's own
file, the file that holds it, or the folder that holds it."""
if not METHODOLOGY_PAGE.exists():
pytest.skip("The documentation is not part of an installed package.")
page = METHODOLOGY_PAGE.read_text()
path = series.replace(".", "/")
holder = path.rpartition("/")[0]
links = (
f"{PARAMETERS_FOLDER}{path}.yaml",
f"{PARAMETERS_FOLDER}{holder}.yaml",
f"{PARAMETERS_FOLDER}{holder})",
)
assert any(link in page for link in links), (
f"{METHODOLOGY_PAGE.name} does not link {series}, which uprates at "
"least one input. Say on the page which inputs follow it."
)
Loading