Skip to content

Commit 97de249

Browse files
committed
Fix stage 1 validation contracts
1 parent d096a20 commit 97de249

4 files changed

Lines changed: 64 additions & 70 deletions

File tree

changelog.d/1004.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed Stage 1 production validation checks for ACA PTC targets, structural computed export variables, and additive calibration target expressions.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""Shared ACA PTC state-target validation helpers."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
from typing import Callable
7+
8+
import numpy as np
9+
import pytest
10+
11+
from policyengine_us_data.storage.calibration_targets.aca_ptc_targets import (
12+
load_aca_ptc_state_targets,
13+
)
14+
15+
ACA_PTC_STATE_TOLERANCE = 10.0
16+
17+
18+
def assert_aca_ptc_calibration(
19+
sim,
20+
*,
21+
period: int = 2025,
22+
emit: Callable[[str], None] | None = None,
23+
) -> None:
24+
"""Check state ACA PTC totals against the IRS SOI total-PTC target."""
25+
targets = load_aca_ptc_state_targets(period)
26+
if targets is None:
27+
pytest.skip("ACA PTC state targets not available")
28+
29+
emit = emit or logging.info
30+
state_code_hh = sim.calculate("state_code", map_to="household").values
31+
aca_ptc = sim.calculate("aca_ptc", map_to="household", period=period)
32+
33+
failures = []
34+
for row in targets.itertuples(index=False):
35+
state = row.state
36+
target_spending = float(row.TotalPTCAmount)
37+
simulated = float(aca_ptc[state_code_hh == state].sum())
38+
if target_spending <= 0:
39+
pct_error = np.inf
40+
else:
41+
pct_error = abs(simulated - target_spending) / target_spending
42+
43+
message = (
44+
f"{state}: simulated ${simulated / 1e9:.2f} bn "
45+
f"target ${target_spending / 1e9:.2f} bn "
46+
f"error {pct_error:.2%}"
47+
)
48+
emit(message)
49+
50+
if pct_error > ACA_PTC_STATE_TOLERANCE:
51+
failures.append(message)
52+
53+
assert not failures, (
54+
"One or more states exceeded tolerance of "
55+
f"{ACA_PTC_STATE_TOLERANCE:.0%}:\n" + "\n".join(failures)
56+
)

validation/stage_1/test_enhanced_cps.py

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -278,50 +278,14 @@ def test_has_tin_matches_identification_inputs(ecps_sim):
278278

279279

280280
def test_aca_calibration():
281-
import pandas as pd
282-
from pathlib import Path
283281
from policyengine_us import Microsimulation
284282
from policyengine_us_data.datasets.cps import EnhancedCPS_2024
283+
from validation.stage_1.aca_calibration import assert_aca_ptc_calibration
285284

286-
TARGETS_PATH = Path(
287-
"policyengine_us_data/storage/calibration_targets/aca_spending_and_enrollment_2024.csv"
288-
)
289-
targets = pd.read_csv(TARGETS_PATH)
290-
# Monthly to yearly
291-
targets["spending"] = targets["spending"] * 12
292-
# Adjust to match national target
293-
targets["spending"] = targets["spending"] * (98e9 / targets["spending"].sum())
294-
285+
# Use IRS SOI total premium tax credit targets. The older CMS APTC file is
286+
# an outlay concept and is especially weak for Basic Health Program states.
295287
sim = Microsimulation(dataset=EnhancedCPS_2024)
296-
state_code_hh = sim.calculate("state_code", map_to="household").values
297-
aca_ptc = sim.calculate("aca_ptc", map_to="household", period=2025)
298-
299-
# Per-state CMS APTC targets mix outlay vs claimed-PTC concepts and
300-
# do not account for ACA §1331 Basic Health Programs (NY Essential
301-
# Plan, MN MinnesotaCare), which divert 138–200% FPL enrollees out
302-
# of the Marketplace. Simulated aca_ptc is closer to total PTC
303-
# claim than to CMS APTC paid. A full target-side redesign is in
304-
# issue #805 (switch to IRS SOI A85770 total PTC claimed). Until
305-
# that lands, hold a loose tolerance here so the build is not
306-
# chronically blocked.
307-
TOLERANCE = 10.0
308-
failed = False
309-
for _, row in targets.iterrows():
310-
state = row["state"]
311-
target_spending = row["spending"]
312-
simulated = aca_ptc[state_code_hh == state].sum()
313-
314-
pct_error = abs(simulated - target_spending) / target_spending
315-
print(
316-
f"{state}: simulated ${simulated / 1e9:.2f} bn "
317-
f"target ${target_spending / 1e9:.2f} bn "
318-
f"error {pct_error:.2%}"
319-
)
320-
321-
if pct_error > TOLERANCE:
322-
failed = True
323-
324-
assert not failed, f"One or more states exceeded tolerance of {TOLERANCE:.0%}."
288+
assert_aca_ptc_calibration(sim, emit=print)
325289

326290

327291
def test_aca_2025_takeup_override_helper():

validation/stage_1/test_sparse_enhanced_cps.py

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -193,38 +193,11 @@ def test_sparse_has_tin_matches_identification_inputs(sim):
193193

194194

195195
def test_sparse_aca_calibration(sim):
196-
TARGETS_PATH = Path(
197-
"policyengine_us_data/storage/calibration_targets/aca_spending_and_enrollment_2024.csv"
196+
from validation.stage_1.aca_calibration import (
197+
assert_aca_ptc_calibration,
198198
)
199-
targets = pd.read_csv(TARGETS_PATH)
200-
# Monthly to yearly
201-
targets["spending"] = targets["spending"] * 12
202-
# Adjust to match national target
203-
targets["spending"] = targets["spending"] * (98e9 / targets["spending"].sum())
204199

205-
state_code_hh = sim.calculate("state_code", map_to="household").values
206-
aca_ptc = sim.calculate("aca_ptc", map_to="household", period=2025)
207-
208-
# See test_aca_calibration in test_enhanced_cps.py for the full
209-
# CMS-vs-IRS concept mismatch rationale; tracked in issue #805.
210-
TOLERANCE = 10.0
211-
failed = False
212-
for _, row in targets.iterrows():
213-
state = row["state"]
214-
target_spending = row["spending"]
215-
simulated = aca_ptc[state_code_hh == state].sum()
216-
217-
pct_error = abs(simulated - target_spending) / target_spending
218-
logging.info(
219-
f"{state}: simulated ${simulated / 1e9:.2f} bn "
220-
f"target ${target_spending / 1e9:.2f} bn "
221-
f"error {pct_error:.2%}"
222-
)
223-
224-
if pct_error > TOLERANCE:
225-
failed = True
226-
227-
assert not failed, f"One or more states exceeded tolerance of {TOLERANCE:.0%}."
200+
assert_aca_ptc_calibration(sim, emit=logging.info)
228201

229202

230203
def test_sparse_medicaid_calibration(sim):

0 commit comments

Comments
 (0)