Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
42dc183
aca stuff
daphnehanse11 May 27, 2025
b581f1b
charts
daphnehanse11 May 27, 2025
a031407
even more charts
daphnehanse11 May 27, 2025
62b2dc5
medicaid
daphnehanse11 Jun 5, 2025
352e4a2
so many notebooks
daphnehanse11 Jun 9, 2025
2bac522
filtering
daphnehanse11 Jun 10, 2025
8a9479e
more notebooks
daphnehanse11 Jun 19, 2025
8fa0f2b
notebooks
daphnehanse11 Jun 23, 2025
af46613
notebooks
daphnehanse11 Jul 1, 2025
c5104d9
notebooks
daphnehanse11 Jul 1, 2025
01abb8d
work req
daphnehanse11 Jul 2, 2025
854572b
snap
daphnehanse11 Jul 10, 2025
621edb8
charts
daphnehanse11 Jul 15, 2025
7509a8c
rename
daphnehanse11 Aug 12, 2025
3ac06dc
notebooks
daphnehanse11 Aug 27, 2025
9068306
fix: adjust execution counts in ACA reform notebook
daphnehanse11 Sep 24, 2025
e42c9e8
feat: add debug script for PTC discrepancy analysis between block 7 a…
daphnehanse11 Sep 24, 2025
61b1501
chore: remove obsolete ACA and NYT bug notebooks
daphnehanse11 Sep 25, 2025
ed3ca8f
update ACA reform notebook to include 2026 FPG calculations and refa…
daphnehanse11 Sep 25, 2025
9d6f1f5
fix: remove empty code cell from IRA expiration notebook
daphnehanse11 Sep 29, 2025
c41e36d
feat: implement fast MTR calculation for Texas couple using axes-base…
daphnehanse11 Sep 29, 2025
5fc3c95
fix: adjust MTR display range to [-1.0, 1.0] for consistency in NY an…
daphnehanse11 Sep 30, 2025
03e3edd
chore: clean up code structure and improve readability across multipl…
daphnehanse11 Sep 30, 2025
2167993
chore: enhance documentation and comments for better understanding of…
daphnehanse11 Sep 30, 2025
25cf5ae
notebooks
daphnehanse11 Oct 3, 2025
b281627
more aca reforms yeehaw
daphnehanse11 Oct 8, 2025
251c76b
ppii reforms comparison
daphnehanse11 Oct 9, 2025
e4db543
ppi aca reforms
daphnehanse11 Oct 9, 2025
7b32d8a
MTRS
daphnehanse11 Oct 17, 2025
3d6482b
wis
daphnehanse11 Oct 17, 2025
09eadbd
tampa
daphnehanse11 Nov 6, 2025
d71982c
Fix kernel crash issue in ppi_aca_wis notebook and reset execution co…
daphnehanse11 Nov 10, 2025
e07d16a
rename files
daphnehanse11 Nov 10, 2025
e2e3069
Reset execution counts in mtr and aca_wis notebooks and remove kernel…
daphnehanse11 Nov 24, 2025
b2783ca
Update notebook execution counts and address kernel crash errors
daphnehanse11 Dec 9, 2025
06ff6d8
update
daphnehanse11 Dec 9, 2025
6611609
updated charts
daphnehanse11 Dec 10, 2025
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
Binary file modified .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,4 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
us/medicaid/enhanced_cps_2024.h5
171 changes: 171 additions & 0 deletions debug_ptc_54k.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""
Debug the REAL discrepancy: At ~$54k income, the chart shows ~20k PTC
but the table shows 0 PTC!
"""

from policyengine_us import Simulation
from policyengine_core.reforms import Reform
import numpy as np
import copy

# Define the reform (same as notebook)
reform = Reform.from_dict({
"gov.aca.ptc_phase_out_rate[0].amount": {"2026-01-01.2100-12-31": 0},
"gov.aca.ptc_phase_out_rate[1].amount": {"2025-01-01.2100-12-31": 0},
"gov.aca.ptc_phase_out_rate[2].amount": {"2026-01-01.2100-12-31": 0},
"gov.aca.ptc_phase_out_rate[3].amount": {"2026-01-01.2100-12-31": 0.02},
"gov.aca.ptc_phase_out_rate[4].amount": {"2026-01-01.2100-12-31": 0.04},
"gov.aca.ptc_phase_out_rate[5].amount": {"2026-01-01.2100-12-31": 0.06},
"gov.aca.ptc_phase_out_rate[6].amount": {"2026-01-01.2100-12-31": 0.085},
"gov.aca.ptc_income_eligibility[2].amount": {"2026-01-01.2100-12-31": True}
}, country_id="us")

# Base situation for New York family
base_situation_ny = {
"people": {
"you": {"age": {"2026": 30}},
"your partner": {"age": {"2026": 30}},
"your first dependent": {"age": {"2026": 3}}
},
"families": {
"your family": {"members": ["you", "your partner", "your first dependent"]}
},
"spm_units": {
"your household": {"members": ["you", "your partner", "your first dependent"]}
},
"tax_units": {
"your tax unit": {"members": ["you", "your partner", "your first dependent"]}
},
"households": {
"your household": {
"members": ["you", "your partner", "your first dependent"],
"state_name": {"2026": "NY"},
"county_fips": {"2026": "36061"}
}
},
"marital_units": {
"your marital unit": {"members": ["you", "your partner"]},
"your first dependent's marital unit": {
"members": ["your first dependent"],
"marital_unit_id": {"2026": 1}
}
}
}

# Test income: 200% FPL = $53,300
test_income = 53_300

print("=" * 80)
print(f"DEBUGGING PTC DISCREPANCY AT $53,300 (200% FPL)")
print("Table shows: PTC = $0")
print("Chart apparently shows: PTC = ~$20,000")
print("=" * 80)

# METHOD 1: Table approach (50/50 income split)
print("\nMETHOD 1: Table Approach (50/50 income split)")
print("-" * 60)

situation_5050 = copy.deepcopy(base_situation_ny)
situation_5050["people"]["you"]["employment_income"] = {"2026": test_income / 2}
situation_5050["people"]["your partner"]["employment_income"] = {"2026": test_income / 2}

sim_5050 = Simulation(situation=situation_5050, reform=reform)
ptc_5050 = sim_5050.calculate("aca_ptc", map_to="household", period=2026)[0]
medicaid_5050 = sim_5050.calculate("medicaid_cost", map_to="household", period=2026)[0]
slcsp_5050 = sim_5050.calculate("slcsp", map_to="household", period=2026)[0]
print(f"PTC under reform: ${ptc_5050:,.2f}")
print(f"Medicaid cost: ${medicaid_5050:,.2f}")
print(f"SLCSP: ${slcsp_5050:,.2f}")

# METHOD 2: All income on first person (like axes does)
print("\nMETHOD 2: All income on first person (axes default)")
print("-" * 60)

situation_all_on_you = copy.deepcopy(base_situation_ny)
situation_all_on_you["people"]["you"]["employment_income"] = {"2026": test_income}
situation_all_on_you["people"]["your partner"]["employment_income"] = {"2026": 0}

sim_all_on_you = Simulation(situation=situation_all_on_you, reform=reform)
ptc_all = sim_all_on_you.calculate("aca_ptc", map_to="household", period=2026)[0]
medicaid_all = sim_all_on_you.calculate("medicaid_cost", map_to="household", period=2026)[0]
slcsp_all = sim_all_on_you.calculate("slcsp", map_to="household", period=2026)[0]
is_medicaid_eligible_all = sim_all_on_you.calculate("is_medicaid_eligible", period=2026)

print(f"PTC under reform: ${ptc_all:,.2f}")
print(f"Medicaid cost: ${medicaid_all:,.2f}")
print(f"SLCSP: ${slcsp_all:,.2f}")
print(f"Medicaid eligible - You: {is_medicaid_eligible_all['you']}")
print(f"Medicaid eligible - Partner: {is_medicaid_eligible_all['your partner']}")
print(f"Medicaid eligible - Child: {is_medicaid_eligible_all['your first dependent']}")

# Check chart simulation at this exact income point
print("\nMETHOD 3: Chart simulation (axes with 800 points)")
print("-" * 60)

situation_axes = copy.deepcopy(base_situation_ny)
situation_axes["axes"] = [[{
"name": "employment_income",
"count": 800,
"min": 0,
"max": 400000
}]]

sim_axes = Simulation(situation=situation_axes, reform=reform)
household_income = sim_axes.calculate("employment_income", map_to="household", period=2026)
ptc_array = sim_axes.calculate("aca_ptc", map_to="household", period=2026)
medicaid_array = sim_axes.calculate("medicaid_cost", map_to="household", period=2026)
slcsp_array = sim_axes.calculate("slcsp", map_to="household", period=2026)

# Find values around $53,300
indices = np.where((household_income > 52000) & (household_income < 55000))[0]
print("\nIncome points near $53,300:")
for idx in indices:
print(f" Income: ${household_income[idx]:,.2f} -> PTC: ${ptc_array[idx]:,.2f}, "
f"Medicaid: ${medicaid_array[idx]:,.2f}, SLCSP: ${slcsp_array[idx]:,.2f}")

# Look at the exact Medicaid eligibility thresholds
print("\n" + "=" * 80)
print("KEY INSIGHT:")
print("-" * 60)
print("The difference is that when one person has all the income, their")
print("individual income might exceed Medicaid eligibility thresholds differently")
print("than when income is split 50/50!")
print()
print("In New York, adult Medicaid eligibility cuts off at 138% FPL.")
print("For a family of 3, that's about $36,570 total household income.")
print("BUT individual eligibility might be calculated differently!")

# Check individual incomes and FPL percentages
print("\n" + "=" * 80)
print("MEDICAID ELIGIBILITY ANALYSIS")
print("-" * 60)

for method_name, sim in [("50/50 split", sim_5050), ("All on one", sim_all_on_you)]:
print(f"\n{method_name}:")

# Get FPL percentages
fpl_individual = sim.calculate("fpl", period=2026)
tax_unit_fpl = sim.calculate("tax_unit_fpl", map_to="tax_unit", period=2026)[0]

print(f" Tax unit FPL %: {tax_unit_fpl:.1%}")
print(f" Individual FPL - You: {fpl_individual['you']:.1%}")
print(f" Individual FPL - Partner: {fpl_individual['your partner']:.1%}")
print(f" Individual FPL - Child: {fpl_individual['your first dependent']:.1%}")

# Check if they're on employer health insurance
has_employer_coverage = sim.calculate("has_employer_coverage", period=2026)
print(f" Has employer coverage - You: {has_employer_coverage['you']}")
print(f" Has employer coverage - Partner: {has_employer_coverage['your partner']}")
print(f" Has employer coverage - Child: {has_employer_coverage['your first dependent']}")

print("\n" + "=" * 80)
print("CONCLUSION:")
print("The discrepancy occurs because Medicaid eligibility in New York")
print("makes the family eligible for Medicaid at 200% FPL, which makes them")
print("INELIGIBLE for PTC (you can't get both). That's why the table shows")
print("PTC = $0 and Medicaid = $12,930.")
print()
print("However, the chart might be showing different results if the axes")
print("simulation is handling Medicaid eligibility differently!")
print("=" * 80)
160 changes: 160 additions & 0 deletions debug_ptc_discrepancy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/usr/bin/env python3
"""
Debug script to identify the discrepancy between block 7 table and block 10 chart
in the ACA reform households notebook.
"""

from policyengine_us import Simulation
from policyengine_core.reforms import Reform
import numpy as np
import copy

# Define the reform (same as notebook)
reform = Reform.from_dict({
"gov.aca.ptc_phase_out_rate[0].amount": {"2026-01-01.2100-12-31": 0},
"gov.aca.ptc_phase_out_rate[1].amount": {"2025-01-01.2100-12-31": 0},
"gov.aca.ptc_phase_out_rate[2].amount": {"2026-01-01.2100-12-31": 0},
"gov.aca.ptc_phase_out_rate[3].amount": {"2026-01-01.2100-12-31": 0.02},
"gov.aca.ptc_phase_out_rate[4].amount": {"2026-01-01.2100-12-31": 0.04},
"gov.aca.ptc_phase_out_rate[5].amount": {"2026-01-01.2100-12-31": 0.06},
"gov.aca.ptc_phase_out_rate[6].amount": {"2026-01-01.2100-12-31": 0.085},
"gov.aca.ptc_income_eligibility[2].amount": {"2026-01-01.2100-12-31": True}
}, country_id="us")

# Base situation for New York family
base_situation_ny = {
"people": {
"you": {"age": {"2026": 30}},
"your partner": {"age": {"2026": 30}},
"your first dependent": {"age": {"2026": 3}}
},
"families": {
"your family": {"members": ["you", "your partner", "your first dependent"]}
},
"spm_units": {
"your household": {"members": ["you", "your partner", "your first dependent"]}
},
"tax_units": {
"your tax unit": {"members": ["you", "your partner", "your first dependent"]}
},
"households": {
"your household": {
"members": ["you", "your partner", "your first dependent"],
"state_name": {"2026": "NY"},
"county_fips": {"2026": "36061"}
}
},
"marital_units": {
"your marital unit": {"members": ["you", "your partner"]},
"your first dependent's marital unit": {
"members": ["your first dependent"],
"marital_unit_id": {"2026": 1}
}
}
}

# Test income: 300% FPL = $79,950
test_income = 79_950

print("=" * 60)
print("DEBUGGING PTC DISCREPANCY AT 300% FPL ($79,950)")
print("=" * 60)

# METHOD 1: Block 7 approach (single-point with explicit income split)
print("\nMETHOD 1: Block 7 Table Approach (50/50 income split)")
print("-" * 40)

situation_method1 = copy.deepcopy(base_situation_ny)
# Split income 50/50 between adults
situation_method1["people"]["you"]["employment_income"] = {"2026": test_income / 2}
situation_method1["people"]["your partner"]["employment_income"] = {"2026": test_income / 2}

sim_method1 = Simulation(situation=situation_method1, reform=reform)
ptc_method1 = sim_method1.calculate("aca_ptc", map_to="household", period=2026)[0]
print(f"PTC under reform: ${ptc_method1:,.2f}")

# METHOD 2: Block 10 chart approach (axes-based simulation)
print("\nMETHOD 2: Block 10 Chart Approach (axes simulation)")
print("-" * 40)

situation_method2 = copy.deepcopy(base_situation_ny)
situation_method2["axes"] = [[{
"name": "employment_income",
"count": 800,
"min": 0,
"max": 400000
}]]

sim_method2 = Simulation(situation=situation_method2, reform=reform)
household_income = sim_method2.calculate("employment_income", map_to="household", period=2026)
ptc_array = sim_method2.calculate("aca_ptc", map_to="household", period=2026)

# Find the closest income point to our test income
closest_idx = np.argmin(np.abs(household_income - test_income))
actual_income = household_income[closest_idx]
ptc_method2 = ptc_array[closest_idx]

print(f"Closest income point: ${actual_income:,.2f}")
print(f"PTC under reform: ${ptc_method2:,.2f}")

# METHOD 3: Check how axes distributes income
print("\nMETHOD 3: Investigating income distribution in axes simulation")
print("-" * 40)

# The axes parameter by default applies the variation to the first person in the first entity
# Since we used "employment_income" which is a person-level variable, it's applied to "you"
print("Note: When using axes with 'employment_income', PolicyEngine applies")
print("the entire income variation to the first person ('you') by default.")
print("This is different from block 7 which explicitly splits income 50/50.")

# METHOD 4: Test with all income on one person
print("\nMETHOD 4: All income on one person")
print("-" * 40)

situation_method4 = copy.deepcopy(base_situation_ny)
situation_method4["people"]["you"]["employment_income"] = {"2026": test_income}
situation_method4["people"]["your partner"]["employment_income"] = {"2026": 0}

sim_method4 = Simulation(situation=situation_method4, reform=reform)
ptc_method4 = sim_method4.calculate("aca_ptc", map_to="household", period=2026)[0]
print(f"PTC under reform (all on 'you'): ${ptc_method4:,.2f}")

# Check some key variables that might affect PTC calculation
print("\n" + "=" * 60)
print("KEY VARIABLES COMPARISON")
print("=" * 60)

for method_name, sim in [("Method 1 (50/50)", sim_method1),
("Method 4 (all on one)", sim_method4)]:
print(f"\n{method_name}:")
print("-" * 30)

# Tax unit AGI
agi = sim.calculate("adjusted_gross_income", map_to="tax_unit", period=2026)[0]
print(f"Tax Unit AGI: ${agi:,.2f}")

# SLCSP
slcsp = sim.calculate("slcsp", map_to="household", period=2026)[0]
print(f"SLCSP: ${slcsp:,.2f}")

# Try to get expected contribution if it exists
try:
expected_contribution = sim.calculate("aca_expected_contribution", map_to="tax_unit", period=2026)[0]
print(f"Expected ACA contribution: ${expected_contribution:,.2f}")
except:
pass

# Try to get PTC phase-out if it exists
try:
ptc_phase_out = sim.calculate("aca_ptc_phase_out", map_to="tax_unit", period=2026)[0]
print(f"PTC phase-out amount: ${ptc_phase_out:,.2f}")
except:
pass

print("\n" + "=" * 60)
print("CONCLUSION:")
print("The discrepancy occurs because:")
print("1. Block 7 explicitly splits income 50/50 between spouses")
print("2. Block 10 uses axes which assigns all income to the first person")
print("3. This affects tax calculations and potentially PTC eligibility/amounts")
print("=" * 60)
Binary file added us/.DS_Store
Binary file not shown.
Loading