Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -330,10 +330,6 @@
"reason": "Stochastic take-up flag the incumbent seeds so take-up-gated programs do not ship at 100% participation; Populace does not yet produce it.",
"issue": "PolicyEngine/populace#312"
},
"takes_up_medicaid_if_eligible": {
"reason": "Stochastic take-up flag the incumbent seeds so take-up-gated programs do not ship at 100% participation; Populace does not yet produce it.",
"issue": "PolicyEngine/populace#312"
},
"takes_up_medicare_if_eligible": {
"reason": "Stochastic take-up flag the incumbent seeds so take-up-gated programs do not ship at 100% participation; Populace does not yet produce it.",
"issue": "PolicyEngine/populace#312"
Expand Down
51 changes: 51 additions & 0 deletions packages/populace-build/src/populace/build/us/source_stages.json
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,57 @@
"selected_marketplace_plan_benchmark_ratio"
],
"notes": "When PolicyEngine-US separates observed Marketplace coverage from simulated take-up, this stage should write the simulated take-up input while reported coverage remains a CPS-carried person-level input."
},
{
"stage": "medicaid_take_up",
"survey": "CPS ASEC reported coverage + CMS Medicaid monthly enrollment snapshot",
"source": "https://data.medicaid.gov/dataset/6165f45b-ca93-5bb5-9d06-db29c692a360",
"grain": "person",
"artifacts": [
{
"kind": "public_microdata",
"format": "census_asec",
"vintage": "build_year",
"locator": "CPS ASEC current Medicaid coverage at interview (has_medicaid_health_coverage_at_interview)"
},
{
"kind": "administrative_table",
"format": "cms_enrollment_snapshot",
"vintage": "month2024_12",
"locator": "CMS Medicaid and CHIP monthly enrollment, December 2024 state snapshot (cms_medicaid.month2024_12.state_enrollment.*.total_medicaid_enrollment)"
}
],
"operations": [
{
"kind": "read_table",
"table": "person",
"weight": "person_weight"
},
{
"kind": "assign_binary_from_rate",
"output": "takes_up_medicaid_if_eligible",
"draw": "stable_person_draw",
"rate_key": "medicaid",
"rate_column": "medicaid_take_up_rate",
"reported_true_anchor": "has_medicaid_health_coverage_at_interview"
},
{
"kind": "calibrate_binary_assignment",
"variable": "takes_up_medicaid_if_eligible",
"targets": [
"cms_medicaid_enrollment_by_state"
],
"preserve_true_anchors": true,
"preserve_true_anchor": "has_medicaid_health_coverage_at_interview",
"domain": "is_medicaid_eligible",
"weight": "person_weight",
"draw": "stable_person_draw"
}
],
"outputs": [
"takes_up_medicaid_if_eligible"
],
"notes": "Anchored count-calibration (take-up contract treatment: count_calibrated; populace #331). Eligible reporters always take up; the fill draws at an in-build state rate (CMS count over weighted modeled eligibles, an assignment prior, never cited as provenance) and is greedily calibrated to the CMS December 2024 state snapshot among eligible non-anchored persons. Point-in-time (average-month) enrollment semantics per #332 - never ever-enrolled-in-year counts. The assign operation deliberately carries no eligibility mask: off-domain persons keep a draw-based propensity so eligibility-expanding reforms do not inherit a hard-coded zero response; the engine's is_medicaid_eligible gate hides the flag at baseline. States whose CMS count meets or exceeds modeled eligible weight saturate (all eligibles enroll) and are recorded as saturated in diagnostics, not failed (#170's eligibility-undercount shortfall is out of scope). CHIP deliberately excluded pending the #321 M-CHIP/separate-CHIP ledger concept split."
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
},
"doctrine": {
"engine_class": "Derived from engine metadata. model_simulated = policyengine-us computes the flag itself (formula / adds / start-date formula); populace must NOT seed it. data_seeded = a default-True input leaf policyengine-us reads from data; without a dataset value every eligible unit takes up (mechanical 100% participation). dead = no downstream variable reads the flag; seeding it changes no output.",
"populace_treatment": "Curated per-program decision. seed = populace assigns the flag from an administrative participation rate with cited provenance. rate_unsourced = the flag is data_seeded but no administrative rate is traceable to a federal agency publication, so it is left unseeded and stays a #313 parity-gate exemption until the rate is sourced (ideally as a Ledger fact); copying us-data's unsourced value would recreate the fabricated-rate class ledger#77 removed. out_of_scope = handled by another workstream. near_universal = the administrative reality is ~100% take-up, so seeding sub-100% would be the error.",
"provenance_rule": "A rate may only drive a seed stage when its exact value is traceable to an administrative source (FNS/CMS/IRS/ACF/ASPE publication) at a stated vintage. Research-institute-only estimates (e.g. NIEER, unadjusted Urban microsimulation outputs) and model-relative ratios (admin numerator over a policyengine eligibility estimate) do not clear the bar and are recorded rate_unsourced."
"populace_treatment": "Curated per-program decision. seed = populace assigns the flag from an administrative participation rate with cited provenance. count_calibrated = no administrative participation rate exists, but administrative enrollment counts do: populace anchors the flag on survey-reported coverage and calibrates the fill to those count facts (the native ACA stage pattern), so no rate is ever cited and the model-relative-ratio class stays out of the inventory; the entry's calibration block names the anchor column and target fact families. rate_unsourced = the flag is data_seeded but no administrative rate is traceable to a federal agency publication, so it is left unseeded and stays a #313 parity-gate exemption until the rate is sourced (ideally as a Ledger fact); copying us-data's unsourced value would recreate the fabricated-rate class ledger#77 removed. out_of_scope = handled by another workstream. near_universal = the administrative reality is ~100% take-up, so seeding sub-100% would be the error.",
"provenance_rule": "A rate may only drive a seed stage when its exact value is traceable to an administrative source (FNS/CMS/IRS/ACF/ASPE publication) at a stated vintage. Research-institute-only estimates (e.g. NIEER, unadjusted Urban microsimulation outputs) and model-relative ratios (admin numerator over a policyengine eligibility estimate) do not clear the bar and are recorded rate_unsourced.",
"enrollment_semantics": "Enrollment take-up flags carry point-in-time (average-month) semantics: anchored on interview-point survey coverage and calibrated to month-tagged administrative enrollment snapshots, never to ever-enrolled-in-year counts (which run ~10M+ higher for Medicaid and would overstate spending against per-full-year-equivalent cost denominators). Precedent: Urban HIPSM targets the CMS point-in-time snapshot 'rather than an annual average' (HIPSM 2020 methodology). Within-year churn is out of scope for take-up stages (longitudinal frame direction, DESIGN.md). populace #332."
},
"programs": [
{
Expand Down Expand Up @@ -67,11 +68,18 @@
"entity": "person",
"value_type": "bool",
"default": true,
"populace_treatment": "rate_unsourced",
"populace_treatment": "count_calibrated",
"consumed_via": "medicaid_enrolled (adds), gated by is_medicaid_eligible",
"rate": {"status": "model_relative", "us_data_value": "state-specific 0.53-0.99"},
"followup": "Source a state Medicaid participation rate as a Ledger fact; #170 diagnostics motivate it.",
"notes": "This is the #170 symptom (enrollment == eligibility). us-data's state values are 'derived from MACPAC enrollment targets vs modeled eligibility' - an admin count over a model eligibility estimate, i.e. a calibration ratio internal to a microsimulation, not a published participation rate; ceiling values (0.99) are ratio artifacts. KFF's cited brief carries no state participation table. Left unseeded rather than recreate an unsourced rate."
"calibration": {
"anchor": "has_medicaid_health_coverage_at_interview",
"targets": ["cms_medicaid_enrollment_by_state"],
"fact_family": "cms_medicaid.month2024_12.state_enrollment.*.total_medicaid_enrollment",
"semantics": "point_in_time_monthly_snapshot (populace #332)",
"anchor_month_note": "The ASEC anchor is interview-point (~Feb-Apr) while the target is the December snapshot: both point-in-time stocks, months deliberately unaligned. With 2024 enrollment declining through the unwinding, spring reporters can exceed a December count; the stage gate's anchor-floor rule treats that as the reachable floor, not a miss."
},
"rate": {"status": "no_administrative_rate_exists", "us_data_value": "state-specific 0.53-0.99 (rejected: model-relative ratio, MACPAC enrollment over modeled eligibility)"},
"scope_owner": "populace#331 (medicaid_take_up stage)",
"notes": "Heals the #170 symptom (enrollment == eligibility). No administrative participation rate clears the provenance bar: us-data's state values are an admin count over a model eligibility estimate (ceiling values 0.99 are ratio artifacts) and KFF's cited brief carries no state participation table. The medicaid_take_up stage therefore cites no rate: eligible reporters of has_medicaid_health_coverage_at_interview always take up, and the fill is calibrated to CMS state monthly-snapshot enrollment counts (Ledger facts), the native ACA stage pattern. States where modeled eligible weight falls below the CMS count saturate (all eligibles enrolled) and are recorded as saturated in diagnostics, not treated as landmines - the shortfall is an eligibility-undercount question, out of scope here."
},
{
"variable": "takes_up_chip_if_eligible",
Expand All @@ -82,8 +90,8 @@
"populace_treatment": "rate_unsourced",
"consumed_via": "chip_enrolled (adds), gated by is_chip_eligible",
"rate": {"status": "no_standalone_current_rate"},
"followup": "Source a standalone CHIP participation rate (Ledger fact); current MACPAC/CMS publish enrollment counts and combined Medicaid+CHIP child coverage rates, not a standalone CHIP take-up rate.",
"notes": "us-data never carried a standalone CHIP rate. The nearest published figures (Medicaid+CHIP combined child participation, MACPAC/InsureKidsNow) are 2017-vintage, combined, and children-only - not a defensible standalone CHIP take-up rate today. Left unseeded."
"followup": "Blocked on populace#321: CMS total_chip_enrollment counts M-CHIP + separate CHIP while the model materializes separate CHIP only (20 states zero-support), so count-calibration against the current fact family would calibrate to a concept the model cannot reach. Once the ledger splits M-CHIP from separate-CHIP enrollment, CHIP follows the #331 medicaid_take_up count_calibrated pattern (anchor: has_other_means_tested_health_coverage_at_interview or the CHIP-specific reported column the split settles on).",
"notes": "us-data never carried a standalone CHIP rate. The nearest published figures (Medicaid+CHIP combined child participation, MACPAC/InsureKidsNow) are 2017-vintage, combined, and children-only - not a defensible standalone CHIP take-up rate today. Left unseeded pending the #321 concept split; deliberately NOT count_calibrated in the same pass as Medicaid."
},
{
"variable": "takes_up_basic_health_program_if_eligible",
Expand Down
35 changes: 35 additions & 0 deletions packages/populace-build/src/populace/build/us_runtime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,17 @@
from populace.build.us_runtime.input_mass import (
us_input_mass_totals,
)
from populace.build.us_runtime.medicaid_take_up import (
US_MEDICAID_ENROLLMENT_TARGET_TABLE,
US_MEDICAID_ENROLLMENT_TOLERANCE,
US_MEDICAID_TAKE_UP_ANCHOR,
US_MEDICAID_TAKE_UP_STAGE,
US_MEDICAID_TAKE_UP_VARIABLE,
us_medicaid_take_up_diagnostics,
us_medicaid_take_up_gate,
with_us_medicaid_take_up,
write_us_medicaid_take_up_diagnostics,
)
from populace.build.us_runtime.nonzero_shares import (
nonzero_share,
us_nonzero_shares,
Expand Down Expand Up @@ -243,6 +254,7 @@
TakeUpProgram,
assert_take_up_contract_current,
assert_take_up_treatments_consistent,
count_calibrated_take_up_programs,
load_take_up_contract,
seeded_take_up_programs,
)
Expand Down Expand Up @@ -364,10 +376,20 @@
"with_us_immigration_inputs",
"US_TAKE_UP_SHARE_BAND",
"SeededTakeUpResult",
"US_MEDICAID_ENROLLMENT_TARGET_TABLE",
"US_MEDICAID_ENROLLMENT_TOLERANCE",
"US_MEDICAID_TAKE_UP_ANCHOR",
"US_MEDICAID_TAKE_UP_STAGE",
"US_MEDICAID_TAKE_UP_VARIABLE",
"count_calibrated_take_up_programs",
"us_medicaid_take_up_diagnostics",
"us_medicaid_take_up_gate",
"us_take_up_participation_diagnostics",
"us_take_up_signal_gate",
"us_take_up_summary",
"with_us_medicaid_take_up",
"with_us_take_up_inputs",
"write_us_medicaid_take_up_diagnostics",
"write_us_take_up_participation_diagnostics",
"TakeUpContract",
"TakeUpProgram",
Expand Down Expand Up @@ -532,6 +554,18 @@ def to_manifest(self) -> dict[str, object]:
"calibration targets."
),
),
"medicaid_take_up": DonorSpec(
survey="CPS ASEC reported coverage + CMS Medicaid monthly enrollment snapshot",
source="https://data.medicaid.gov/dataset/6165f45b-ca93-5bb5-9d06-db29c692a360",
notes=(
"Medicaid take-up by anchored count-calibration (contract "
"treatment count_calibrated, populace #331): CPS-reported "
"Medicaid coverage at interview anchors the flag; the fill is "
"calibrated to CMS December 2024 state enrollment snapshots. "
"Point-in-time semantics per #332; heals the #170 "
"enrollment==eligibility degeneracy."
),
),
"prior_year_income": DonorSpec(
survey="CPS ASEC (prior year)",
source="https://www.census.gov/programs-surveys/cps.html",
Expand Down Expand Up @@ -666,6 +700,7 @@ def to_manifest(self) -> dict[str, object]:
"vehicle_assets",
"entity_placement",
"aca_marketplace_inputs",
"medicaid_take_up",
"export",
)

Expand Down
Loading
Loading