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
1 change: 1 addition & 0 deletions changelog.d/uprating-series-mappings.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Point dollar inputs at closer uprating series: unemployment compensation follows CBO unemployment outlays instead of taxable Social Security, alimony follows the SOI alimony series, capital gains components follow capital gains, pay-linked inputs follow employment income, general health insurance premiums follow CMS per-capita spending, veterans benefits follow the Social Security cost-of-living index, and rent, child care, tuition, utilities, mortgage interest and student loan interest follow CPI-U.
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. Child support paid and received, the energy subsidy, real estate taxes and household assets follow [CPI-U](https://github.com/PolicyEngine/policyengine-us/blob/main/policyengine_us/parameters/gov/bls/cpi/cpi_u.yaml). Every other dollar input, including rent and child care expenses, 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, 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.

## What the model does not do

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ values:
2021-01-01: 8_507_104_000
metadata:
unit: currency-USD
label: SOI short-term capital gains
label: SOI alimony income
uprating: calibration.gov.cbo.income_by_source.adjusted_gross_income
reference:
- title: IRS SOI Tax Stats - Individual Statistical Tables by Size of Adjusted Gross Income
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ values:
metadata:
unit: currency-USD
label: SOI unemployment compensation
uprating: calibration.gov.cbo.income_by_source.taxable_social_security
# CBO's unemployment compensation outlays begin in 2022, a year after the
# last SOI total, so growth is applied from 2022.
uprating:
parameter: calibration.gov.cbo.unemployment_compensation
start_instant: 2022-01-01
reference:
- title: IRS SOI Tax Stats - Individual Statistical Tables by Size of Adjusted Gross Income
href: https://www.irs.gov/statistics/soi-tax-stats-individual-statistical-tables-by-size-of-adjusted-gross-income
10 changes: 10 additions & 0 deletions policyengine_us/parameters/uprating_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,16 @@ def set_all_uprating_parameters(parameters: ParameterNode) -> ParameterNode:
period_day=1,
)

# Unemployment compensation inputs age with CBO's unemployment
# compensation outlays, which anchor the SOI series past its last total.
extend_parameter_values(
parameters.calibration.gov.cbo.unemployment_compensation,
last_projected_year=2036,
end_year=END_YEAR,
period_month=1,
period_day=1,
)

# CBO income-by-source aggregates are used directly and as anchors for
# SOI-based income upraters. Extending them in the baseline path keeps
# long-run nominal data aging independent of scenario-specific reforms.
Expand Down
10 changes: 6 additions & 4 deletions policyengine_us/tests/core/test_per_capita_uprating.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ def test_no_variable_uprates_by_a_national_total():
"variable, expected",
[
("employment_income_before_lsr", per_capita_path(SOI_EMPLOYMENT)),
("pre_subsidy_rent", per_capita_path(CBO_AGI)),
("spm_unit_pre_subsidy_childcare_expenses", per_capita_path(CBO_AGI)),
("charitable_cash_donations", per_capita_path(CBO_AGI)),
("miscellaneous_income", per_capita_path(CBO_AGI)),
("other_medical_expenses", CMS_MOOP),
("child_support_expense", CPI_U),
("household_weight", POPULATION_PATH),
Expand Down Expand Up @@ -131,7 +131,7 @@ def _tiny_dataset():
"person_id": [1, 2, 3, 4],
"person_household_id": [1, 1, 2, 3],
"employment_income_before_lsr": [50_000.0, 0.0, 120_000.0, 30_000.0],
"pre_subsidy_rent": [12_000.0, 0.0, 30_000.0, 9_000.0],
"charitable_cash_donations": [1_200.0, 0.0, 30_000.0, 900.0],
"other_medical_expenses": [500.0, 250.0, 4_000.0, 0.0],
"child_support_expense": [0.0, 0.0, 6_000.0, 0.0],
"age": [40.0, 8.0, 55.0, 70.0],
Expand Down Expand Up @@ -180,7 +180,9 @@ def growth(column):
assert growth("employment_income_before_lsr") == pytest.approx(
_growth(SOI_EMPLOYMENT, year), rel=1e-9
)
assert growth("pre_subsidy_rent") == pytest.approx(_growth(CBO_AGI, year), rel=1e-9)
assert growth("charitable_cash_donations") == pytest.approx(
_growth(CBO_AGI, year), rel=1e-9
)
# Per-person rates and price indices: amounts follow the series and the
# weights add population growth.
assert growth("other_medical_expenses") == pytest.approx(
Expand Down
140 changes: 140 additions & 0 deletions policyengine_us/tests/core/test_uprating_series.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
"""Each dollar input follows the series closest to what it measures: its own
national series, the income source it is part of, prices for consumption
expenses and debts, CMS per-capita spending for health costs."""

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

EMPLOYMENT = per_capita_path("calibration.gov.irs.soi.employment_income")
SELF_EMPLOYMENT = per_capita_path("calibration.gov.irs.soi.self_employment_income")
CAPITAL_GAINS = per_capita_path("calibration.gov.irs.soi.long_term_capital_gains")
ALIMONY = per_capita_path("calibration.gov.irs.soi.alimony_income")
UNEMPLOYMENT = per_capita_path("calibration.gov.irs.soi.unemployment_compensation")
DEFAULT = per_capita_path("calibration.gov.cbo.income_by_source.adjusted_gross_income")
CMS = "calibration.gov.hhs.cms.moop_per_capita"
CPI_U = "gov.bls.cpi.cpi_u"
COLA = "gov.ssa.uprating"

EXPECTED = {
# Pay, and amounts set as a share of pay.
"hourly_wage": EMPLOYMENT,
"tip_income": EMPLOYMENT,
"fsla_overtime_premium": EMPLOYMENT,
"w2_wages_from_qualified_business": EMPLOYMENT,
"sstb_w2_wages_from_qualified_business": EMPLOYMENT,
"traditional_401k_contributions_desired": EMPLOYMENT,
"roth_401k_contributions_desired": EMPLOYMENT,
"traditional_403b_contributions_desired": EMPLOYMENT,
"roth_403b_contributions_desired": EMPLOYMENT,
"traditional_ira_contributions_desired": EMPLOYMENT,
"roth_ira_contributions_desired": EMPLOYMENT,
"military_basic_pay": EMPLOYMENT,
"military_service_income": EMPLOYMENT,
"state_or_federal_salary": EMPLOYMENT,
# Wage-replacement benefits.
"workers_compensation": EMPLOYMENT,
"disability_benefits": EMPLOYMENT,
"self_employed_pension_contributions_desired": SELF_EMPLOYMENT,
# Components of capital gains.
"long_term_capital_gains_on_collectibles": CAPITAL_GAINS,
"unrecaptured_section_1250_gain": CAPITAL_GAINS,
"non_sch_d_capital_gains": CAPITAL_GAINS,
"schedule_d_capital_gain_distributions": CAPITAL_GAINS,
"long_term_capital_gains_on_small_business_stock": CAPITAL_GAINS,
"long_term_capital_gains_on_assets_eligible_for_vt_exclusion": CAPITAL_GAINS,
"property_sales_net_capital_gain": CAPITAL_GAINS,
"other_net_gain": CAPITAL_GAINS,
# Own national series.
"alimony_income": ALIMONY,
"alimony_expense": ALIMONY,
"unemployment_compensation": UNEMPLOYMENT,
# Health costs.
"health_insurance_premiums": CMS,
# Benefits adjusted by the Social Security cost-of-living index.
"veterans_benefits": COLA,
# Consumption expenses, and the interest on debts, follow prices. The
# structured first and second mortgage inputs stay on the default: two are
# deprecated (issue #9275) and the file's formulas are outside the
# selective coverage job.
"pre_subsidy_rent": CPI_U,
"pre_subsidy_care_expenses": CPI_U,
"spm_unit_pre_subsidy_childcare_expenses": CPI_U,
"after_school_expenses": CPI_U,
"care_and_support_costs": CPI_U,
"qualified_tuition_expenses": CPI_U,
"tuition_and_fees": CPI_U,
"k12_tuition_and_fees": CPI_U,
"pre_subsidy_electricity_expense": CPI_U,
"gas_expense": CPI_U,
"metered_gas_expense": CPI_U,
"bottled_gas_expense": CPI_U,
"fuel_oil_expense": CPI_U,
"coal_expense": CPI_U,
"wood_expense": CPI_U,
"other_heating_fuel_expense": CPI_U,
"cooking_fuel_expense": CPI_U,
"heating_cooling_expense": CPI_U,
"heating_expense_person": CPI_U,
"water_expense": CPI_U,
"sewage_expense": CPI_U,
"trash_expense": CPI_U,
"phone_cost": CPI_U,
"broadband_cost": CPI_U,
"homeowners_insurance": CPI_U,
"homeowners_association_fees": CPI_U,
"pre_subsidy_transportation_expense": CPI_U,
"home_mortgage_interest": CPI_U,
"student_loan_interest": CPI_U,
"investment_interest_expense": CPI_U,
# Still on the default.
"charitable_cash_donations": DEFAULT,
"miscellaneous_income": DEFAULT,
}


def _value(path, year):
return get_parameter(system.parameters, path)(f"{year}-01-01")


@pytest.mark.parametrize("variable, series", sorted(EXPECTED.items()))
def test_variable_follows_expected_series(variable, series):
assert system.variables[variable].uprating == series


@pytest.mark.parametrize("series", sorted(set(EXPECTED.values())))
def test_series_is_defined_across_the_projection_window(series):
for year in (2024, 2025, 2030, 2035):
assert _value(series, year) not in (None, 0)


@pytest.mark.parametrize("year", (2025, 2030, 2035, 2036))
def test_unemployment_compensation_grows_with_cbo_unemployment_outlays(year):
soi = "calibration.gov.irs.soi.unemployment_compensation"
cbo = "calibration.gov.cbo.unemployment_compensation"
assert _value(soi, year) / _value(soi, 2024) == pytest.approx(
_value(cbo, year) / _value(cbo, 2024), rel=1e-12
)


def test_unemployment_compensation_series_do_not_flatten_after_2036():
for path in (
"calibration.gov.cbo.unemployment_compensation",
"calibration.gov.irs.soi.unemployment_compensation",
):
assert _value(path, 2100) > _value(path, 2037) > _value(path, 2036)


def test_alimony_series_is_labelled_as_alimony():
parameter = get_parameter(
system.parameters, "calibration.gov.irs.soi.alimony_income"
)
assert parameter.metadata["label"] == "SOI alimony income"


def test_rent_override_matches_the_rent_input():
assert MICRODATA_UPRATING_OVERRIDES["rent"] == CPI_U
assert system.variables["pre_subsidy_rent"].uprating == CPI_U
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class workers_compensation(Variable):
label = "worker's compensation"
unit = USD
definition_period = YEAR
uprating = "calibration.gov.irs.soi.employment_income"
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ class care_and_support_costs(Variable):
entity = Person
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
label = "Total costs for this person's care and support"
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ class after_school_expenses(Variable):
entity = Person
label = "After school childcare expenses"
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
unit = USD
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class pre_subsidy_care_expenses(Variable):
entity = Person
label = "Pre-subsidy care expenses for a disabled adult dependent or spouse"
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
unit = USD
reference = "https://www.law.cornell.edu/uscode/text/26/21#b_2"
documentation = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ class spm_unit_pre_subsidy_childcare_expenses(Variable):
entity = SPMUnit
label = "SPM unit pre subsidy child care expenses"
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
unit = USD
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class health_insurance_premiums(Variable):
label = "Health insurance premiums"
unit = USD
definition_period = YEAR
uprating = "calibration.gov.hhs.cms.moop_per_capita"
documentation = (
"Person-level health insurance premiums supplied directly as an input. "
"SPM MOOP and statutory medical expense definitions use decomposed "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class heating_expense_person(Variable):
label = "Heating cost for each person"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
documentation = (
"Deprecated: set heating_type and the matching per-fuel expense "
"(e.g. gas_expense) for the dwelling's heating bill. For Michigan "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class homeowners_association_fees(Variable):
label = "Homeowners association fees"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class homeowners_insurance(Variable):
label = "Homeowners insurance"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class pre_subsidy_rent(Variable):
label = "Pre subsidy rent"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class alimony_expense(Variable):
label = "Alimony expense"
unit = USD
definition_period = YEAR
uprating = "calibration.gov.irs.soi.alimony_income"
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ class home_mortgage_interest(Variable):
label = "Interest paid on a home mortgage"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
documentation = "Home mortgage interest, including both reported and not reported on federal Form 1098."
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class investment_interest_expense(Variable):
label = "Investment interest expense"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class k12_tuition_and_fees(Variable):
label = "K-12 Tuition and fees (from Form 8917)"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ class pre_subsidy_transportation_expense(Variable):
value_type = float
entity = Person
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
label = "Pre-subsidy transportation expense"
unit = USD
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class qualified_tuition_expenses(Variable):
label = "Qualified tuition expenses"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class student_loan_interest(Variable):
label = "Student loan interest expense"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ class tuition_and_fees(Variable):
label = "Tuition and fees (from Form 8917)"
unit = USD
definition_period = YEAR
uprating = "gov.bls.cpi.cpi_u"
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ class roth_401k_contributions_desired(Variable):
unit = USD
documentation = "Roth 401(k) contributions before statutory contribution limits."
definition_period = YEAR
uprating = "calibration.gov.irs.soi.employment_income"
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ class roth_403b_contributions_desired(Variable):
unit = USD
documentation = "Roth 403(b) contributions before statutory contribution limits."
definition_period = YEAR
uprating = "calibration.gov.irs.soi.employment_income"
Loading
Loading