Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6499145
Add 2025 post-calibration ACA takeup override
daphnehanse11 Mar 18, 2026
04acc8b
Fix lint in ACA takeup tests
daphnehanse11 Mar 18, 2026
e994f01
Format ACA takeup helper
daphnehanse11 Mar 18, 2026
1b0bd68
Move ACA override to Enhanced CPS path
daphnehanse11 Mar 18, 2026
24efc95
Add long-run calibration contracts
MaxGhenis Mar 31, 2026
6bc34e0
Add support diagnostics to long-run calibration audit
MaxGhenis Mar 31, 2026
4dfa539
Add late-year age aggregation for calibration
MaxGhenis Mar 31, 2026
2172962
Add long-run calibration comparison tools
MaxGhenis Mar 31, 2026
047545b
Add support concentration gates to calibration
MaxGhenis Mar 31, 2026
5525b3d
Add long-run support augmentation diagnostics
MaxGhenis Mar 31, 2026
e3d9912
Probe composite long-run support augmentation
MaxGhenis Mar 31, 2026
5b91f1e
Test appended synthetic late-year support
MaxGhenis Mar 31, 2026
c99ccba
Benchmark long-run TOB outside calibration
MaxGhenis Mar 31, 2026
642752c
Try denser late-tail approximate calibration
MaxGhenis Mar 31, 2026
e00f6e5
Fix review issues in long-run calibration harness
MaxGhenis Mar 31, 2026
9036183
Refine late-tail calibration metadata and caching
MaxGhenis Mar 31, 2026
79858d6
Add publishable horizon assessment tool
MaxGhenis Mar 31, 2026
ff099fd
Add mixed-age household support diagnostic
MaxGhenis Mar 31, 2026
7a03b8b
Prototype minimal synthetic support for 2100
MaxGhenis Apr 1, 2026
c14bbb8
Expand synthetic 2100 support prototype
MaxGhenis Apr 1, 2026
2d4ab08
Add donor-backed synthetic support probe
MaxGhenis Apr 1, 2026
de6403d
Add donor-backed late-year support mode
MaxGhenis Apr 1, 2026
d93eed8
Add structural donor composite late-tail prototype
MaxGhenis Apr 1, 2026
5643fdd
Diagnose and fix support-augmentation translation
MaxGhenis Apr 1, 2026
0b7dfce
Add long-run TOB comparison note
MaxGhenis Apr 2, 2026
40f8e45
Add Trustees bracket-indexing TOB benchmark
MaxGhenis Apr 2, 2026
f35107d
Extend TOB tax-side benchmark scenarios
MaxGhenis Apr 2, 2026
7ffc8ed
Benchmark full IRS uprating for TOB
MaxGhenis Apr 2, 2026
9f46122
Adopt core-threshold TOB baseline
MaxGhenis Apr 2, 2026
d287237
Add post-OBBBA OACT target source
MaxGhenis Apr 2, 2026
d85bc0f
Merge main into long-run calibration branch
MaxGhenis Apr 3, 2026
f618250
Format long-run calibration files
MaxGhenis Apr 3, 2026
6095df6
Merge remote-tracking branch 'upstream/main' into codex/tmp-pr669-merge
MaxGhenis Apr 3, 2026
77766c0
Merge branch 'main' into codex/us-data-calibration-contract
MaxGhenis Apr 4, 2026
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
94 changes: 94 additions & 0 deletions policyengine_us_data/datasets/cps/enhanced_cps.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
ExtendedCPS_2024_Half,
CPS_2024,
)
from policyengine_us_data.utils.randomness import seeded_rng
from policyengine_us_data.utils.takeup import (
ACA_POST_CALIBRATION_PERSON_TARGETS,
extend_aca_takeup_to_match_target,
)
import logging

try:
Expand All @@ -23,6 +28,48 @@
torch = None


def _get_period_array(period_values: dict, period: int) -> np.ndarray:
"""Get a period array from a TIME_PERIOD_ARRAYS variable dict."""
value = period_values.get(period)
if value is None:
value = period_values.get(str(period))
if value is None:
raise KeyError(f"Missing period {period}")
return np.asarray(value)


def create_aca_2025_takeup_override(
base_takeup: np.ndarray,
person_enrolled_if_takeup: np.ndarray,
person_weights: np.ndarray,
person_tax_unit_ids: np.ndarray,
tax_unit_ids: np.ndarray,
target_people: float = ACA_POST_CALIBRATION_PERSON_TARGETS[2025],
) -> np.ndarray:
"""Add 2025 ACA takers until weighted APTC enrollment hits target."""
tax_unit_id_to_idx = {
int(tax_unit_id): idx for idx, tax_unit_id in enumerate(tax_unit_ids)
}
person_tax_unit_idx = np.array(
[tax_unit_id_to_idx[int(tax_unit_id)] for tax_unit_id in person_tax_unit_ids],
dtype=np.int64,
)
enrolled_person_weights = np.zeros(len(tax_unit_ids), dtype=np.float64)
np.add.at(
enrolled_person_weights,
person_tax_unit_idx,
person_enrolled_if_takeup.astype(np.float64) * person_weights,
)
draws = seeded_rng("takes_up_aca_if_eligible").random(len(tax_unit_ids))

return extend_aca_takeup_to_match_target(
base_takeup=np.asarray(base_takeup, dtype=bool),
entity_draws=draws,
enrolled_person_weights=enrolled_person_weights,
target_people=target_people,
)


def reweight(
original_weights,
loss_matrix,
Expand Down Expand Up @@ -142,6 +189,7 @@ def generate(self):

sim = Microsimulation(dataset=self.input_dataset)
data = sim.dataset.load_dataset()
base_year = int(sim.default_calculation_period)
data["household_weight"] = {}
original_weights = sim.calculate("household_weight")
original_weights = original_weights.values + np.random.normal(
Expand Down Expand Up @@ -216,6 +264,52 @@ def generate(self):
f"{int(np.sum(w > 0))} non-zero"
)

if 2025 in ACA_POST_CALIBRATION_PERSON_TARGETS:
sim.set_input(
"household_weight",
base_year,
_get_period_array(data["household_weight"], base_year).astype(
np.float32
),
)
sim.set_input(
"takes_up_aca_if_eligible",
2025,
np.ones(
len(_get_period_array(data["tax_unit_id"], base_year)),
dtype=bool,
),
)
sim.delete_arrays("aca_ptc")

data["takes_up_aca_if_eligible"][2025] = create_aca_2025_takeup_override(
base_takeup=_get_period_array(
data["takes_up_aca_if_eligible"],
base_year,
),
person_enrolled_if_takeup=np.asarray(
sim.calculate(
"aca_ptc",
map_to="person",
period=2025,
use_weights=False,
)
)
> 0,
person_weights=np.asarray(
sim.calculate(
"person_weight",
period=2025,
use_weights=False,
)
),
person_tax_unit_ids=_get_period_array(
data["person_tax_unit_id"],
base_year,
),
tax_unit_ids=_get_period_array(data["tax_unit_id"], base_year),
)

logging.info("Post-generation weight validation passed")

self.save_dataset(data)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Long-Run Calibration Assumption Comparison

This note distinguishes between:

- hard microsimulation calibration targets, which directly shape household weights
- tax-side assumptions used to make those targets more comparable to the public Trustees/OACT methodology

The current long-run baseline now adopts a named tax-side assumption,
`trustees-core-thresholds-v1`, before hard-targeting TOB.

| Component | Current `policyengine-us-data` approach | Trustees / OACT published approach | Calibration use |
| --- | --- | --- | --- |
| Population by age | SSA single-year age projections | SSA single-year age projections | Hard target |
| OASDI benefits | Named long-term target source package | Trustees or OACT-patched annual OASDI path | Hard target |
| Taxable payroll | Named long-term target source package | Trustees annual taxable payroll path | Hard target |
| Social Security benefit-tax thresholds | Literal current-law statutory thresholds remain fixed in nominal dollars | Trustees also describe the statutory `$25k/$32k/$0` and `$34k/$44k` thresholds as remaining fixed in nominal dollars | Not separately targeted |
| Federal income-tax brackets | Core ordinary thresholds are wage-indexed after `2034` via `trustees-core-thresholds-v1` | Trustees assume periodic future bracket adjustments; after the tenth projection year, ordinary federal income-tax brackets are assumed to rise with average wages to avoid indefinite bracket creep | Tax-side assumption |
| Standard deduction / aged-blind addition / capital gains thresholds / AMT thresholds | Included in the same `trustees-core-thresholds-v1` bundle | Not parameterized publicly line-by-line, but these are the main additional federal thresholds most likely to affect long-run TOB | Tax-side assumption |
| OASDI TOB | Computed under the core-threshold tax assumption and targeted in `ss-payroll-tob` profiles | Trustees/OACT publish annual revenue paths or ratios, but not a full public household-level micro rule schedule | Hard target |
| HI TOB | Computed under the core-threshold tax assumption and targeted in `ss-payroll-tob` profiles | Trustees publish current-law HI TOB path; OACT OBBBA updates do not currently provide a full public annual HI replacement series | Hard target |
| OBBBA OASDI update | Available through named target source `oact_2025_08_05_provisional` | August 5, 2025 OACT letter provides annual OASDI changes through 2100 | Benchmark / target-source input |
| OBBBA HI update | Provisional bridge only in named target source | No equivalent full public annual HI replacement path located yet | Benchmark only |

## Practical interpretation

- `ss-payroll` remains the core non-TOB hard-target profile.
- `ss-payroll-tob` now means: calibrate on age + OASDI benefits + taxable payroll + TOB under `trustees-core-thresholds-v1`.
- The core-threshold bundle is a best-public approximation, not a literal public Trustees rules schedule.
- Trustees-consistent long-run TOB requires keeping two different tax-side ideas separate:
- the Social Security benefit-tax thresholds remain fixed in nominal dollars
- ordinary federal income-tax brackets are assumed to rise with average wages after the tenth projection year

## Primary-source references

- [SSA 2025 Trustees Report, V.C.7](https://www.ssa.gov/oact/tr/2025/V_C_prog.html)
- States that the law specifies fixed threshold amounts for taxation of Social Security benefits and that those thresholds remain constant in future years.
- Also states that, after the tenth year of the projection period, income-tax brackets are assumed to rise with average wages rather than with `C-CPI-U`.
- [26 U.S.C. § 86](https://www.law.cornell.edu/uscode/text/26/86)
- Statutory basis for the Social Security benefit-tax threshold structure.
- [SSA 2025 Trustees Report, Table VI.G6](https://www.ssa.gov/OACT/TR/2025/VI_G3_OASDHI_dollars.html)
- Published annual average wage index path through `2100`.
- [42 U.S.C. § 430](https://www.law.cornell.edu/uscode/text/42/430) and [20 CFR § 404.1048](https://www.law.cornell.edu/cfr/text/20/404.1048)
- Statutory and regulatory basis for deriving the Social Security contribution and benefit base from the wage index.
Loading
Loading