Skip to content
Closed
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
4 changes: 4 additions & 0 deletions policyengine_us/parameters/gov/census/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
metadata:
propagate_metadata_to_children: true
economy: false
household: false
4 changes: 4 additions & 0 deletions policyengine_us/parameters/gov/census/spm/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
metadata:
propagate_metadata_to_children: true
economy: false
household: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
metadata:
propagate_metadata_to_children: true
economy: false
household: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
description: Weekly work-expense deduction amount used in Census Supplemental Poverty Measure calculations.
values:
2009-01-01: 28.0500
2010-01-01: 25.5000
2011-01-01: 27.1575
2012-01-01: 33.0225
2013-01-01: 39.3975
2014-01-01: 39.2530
2015-01-01: 40.0945
2016-01-01: 38.4710
2017-01-01: 36.3460
2018-01-01: 37.1025
2019-01-01: 39.7205
2020-01-01: 39.6100
2021-01-01: 38.0630
2022-01-01: 31.0080
2023-01-01: 33.4050
2024-01-01: 34.9945

metadata:
unit: currency-USD
period: year
uprating: gov.bls.cpi.cpi_u
label: Census SPM weekly work-expense deduction amount
reference:
- title: Supplemental Poverty Measure (SPM) Technical Documentation
href: https://www2.census.gov/programs-surveys/supplemental-poverty-measure/technical-documentation/spm_techdoc.pdf
- title: Poverty in the United States 2023
href: https://www2.census.gov/library/publications/2024/demo/p60-283.pdf
- title: Poverty in the United States 2024
href: https://www2.census.gov/library/publications/2025/demo/p60-287.pdf
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
period: 2025
input:
age: 65
filing_status: JOINT
tax_unit_married: true
adjusted_gross_income:
2023: 266_001 # IRMAA uses income from 2 years prior
tax_exempt_interest_income:
Expand Down Expand Up @@ -54,7 +54,7 @@
period: 2025
input:
age: 65
filing_status: JOINT
tax_unit_married: true
adjusted_gross_income:
2023: 200_000 # IRMAA uses income from 2 years prior
tax_exempt_interest_income:
Expand All @@ -67,7 +67,7 @@
period: 2025
input:
age: 65
filing_status: JOINT
tax_unit_married: true
adjusted_gross_income:
2023: 1_000_000 # IRMAA uses income from 2 years prior
tax_exempt_interest_income:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
- name: MSP part B coverage pays the standard premium for income- and asset-eligible enrollees
period: 2025
input:
medicare_enrolled: true
msp_income_eligible: true
msp_asset_eligible: true
base_part_b_premium: 2_220
output:
msp_part_b_premium_coverage: 2_220

- name: MSP part B coverage is zero for ineligible enrollees
period: 2025
input:
medicare_enrolled: true
msp_income_eligible: false
msp_asset_eligible: true
base_part_b_premium: 2_220
output:
msp_part_b_premium_coverage: 0

- name: MSP part B coverage is zero when not enrolled
period: 2025
input:
medicare_enrolled: false
msp_income_eligible: true
msp_asset_eligible: true
base_part_b_premium: 2_220
output:
msp_part_b_premium_coverage: 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import pytest

from policyengine_us import CountryTaxBenefitSystem, Simulation


SYSTEM = CountryTaxBenefitSystem()
PERIOD = "2025"


def make_simulation(
*,
medicare_enrolled: bool,
gross_part_b_premium: float,
base_part_b_premium: float,
msp_income_eligible: bool,
msp_asset_eligible: bool,
) -> Simulation:
return Simulation(
tax_benefit_system=SYSTEM,
situation={
"people": {
"person": {
"age": {PERIOD: 65},
"medicare_enrolled": {PERIOD: medicare_enrolled},
"income_adjusted_part_b_premium": {PERIOD: gross_part_b_premium},
"base_part_b_premium": {PERIOD: base_part_b_premium},
"msp_income_eligible": {f"{PERIOD}-01": msp_income_eligible},
"msp_asset_eligible": {f"{PERIOD}-01": msp_asset_eligible},
}
},
"households": {"household": {"members": ["person"]}},
"tax_units": {"tax_unit": {"members": ["person"]}},
"spm_units": {"spm_unit": {"members": ["person"]}},
"families": {"family": {"members": ["person"]}},
"marital_units": {"marital_unit": {"members": ["person"]}},
},
)


def test_msp_part_b_premium_coverage_pays_standard_premium():
sim = make_simulation(
medicare_enrolled=True,
gross_part_b_premium=4_440,
base_part_b_premium=2_220,
msp_income_eligible=True,
msp_asset_eligible=True,
)

assert sim.calculate("msp_part_b_premium_coverage", PERIOD)[0] == pytest.approx(
2_220
)


def test_medicare_part_b_premiums_preserve_only_irmaa_above_msp_support():
sim = make_simulation(
medicare_enrolled=True,
gross_part_b_premium=4_440,
base_part_b_premium=2_220,
msp_income_eligible=True,
msp_asset_eligible=True,
)

assert sim.calculate("medicare_part_b_premiums", PERIOD)[0] == pytest.approx(2_220)


def test_medicare_part_b_premiums_are_zero_when_msp_covers_standard_premium():
sim = make_simulation(
medicare_enrolled=True,
gross_part_b_premium=2_220,
base_part_b_premium=2_220,
msp_income_eligible=True,
msp_asset_eligible=True,
)

assert sim.calculate("medicare_part_b_premiums", PERIOD)[0] == pytest.approx(0)


def test_medicare_part_b_premiums_are_zero_when_not_enrolled():
sim = make_simulation(
medicare_enrolled=False,
gross_part_b_premium=2_220,
base_part_b_premium=2_220,
msp_income_eligible=True,
msp_asset_eligible=True,
)

assert sim.calculate("msp_part_b_premium_coverage", PERIOD)[0] == pytest.approx(0)
assert sim.calculate("medicare_part_b_premiums", PERIOD)[0] == pytest.approx(0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
- name: Combined work and childcare expenses are capped at the lower earner
period: 2024
absolute_error_margin: 0.001
input:
people:
head:
age: 35
weeks_worked: 10
employment_income: 40_000
is_household_head: true
is_tax_unit_head: true
spouse:
age: 33
weeks_worked: 20
employment_income: 2_000
is_tax_unit_spouse: true
child:
age: 8
tax_units:
tax_unit:
members: [head, spouse, child]
spm_units:
spm_unit:
members: [head, spouse, child]
spm_unit_pre_subsidy_childcare_expenses: 2_000
output:
spm_unit_capped_work_childcare_expenses: 2_000

- name: Work expenses are preserved when they already exceed the earnings cap
period: 2024
absolute_error_margin: 0.001
input:
people:
head:
age: 41
weeks_worked: 52
employment_income: 1_500
is_household_head: true
is_tax_unit_head: true
child:
age: 5
tax_units:
tax_unit:
members: [head, child]
spm_units:
spm_unit:
members: [head, child]
spm_unit_pre_subsidy_childcare_expenses: 3_000
output:
spm_unit_capped_work_childcare_expenses: 1_819.714

- name: Childcare is capped by the remaining lower-earner earnings after work expenses
period: 2024
absolute_error_margin: 0.001
input:
people:
head:
age: 46
weeks_worked: 52
employment_income: 9_098.57
is_household_head: true
is_tax_unit_head: true
spouse:
age: 46
weeks_worked: 52
employment_income: 30_000
is_tax_unit_spouse: true
tax_units:
tax_unit:
members: [head, spouse]
spm_units:
spm_unit:
members: [head, spouse]
spm_unit_pre_subsidy_childcare_expenses: 21_580
output:
spm_unit_capped_work_childcare_expenses: 9_098.57

- name: Unmarried partner counts toward the reference person's remaining childcare cap
period: 2024
absolute_error_margin: 0.001
input:
people:
head:
age: 35
weeks_worked: 10
employment_income: 40_000
is_household_head: true
is_tax_unit_head: true
partner:
age: 33
weeks_worked: 20
employment_income: 2_000
is_unmarried_partner_of_household_head: true
is_tax_unit_head: true
child:
age: 8
tax_units:
reference_unit:
members: [head, child]
partner_unit:
members: [partner]
spm_units:
spm_unit:
members: [head, partner, child]
spm_unit_pre_subsidy_childcare_expenses: 2_000
output:
spm_unit_capped_work_childcare_expenses: 2_000
Loading
Loading