Skip to content
This repository was archived by the owner on Jun 14, 2026. It is now read-only.

Commit 32a993c

Browse files
authored
Populate rent (ACS donor) and childcare (CPS ASEC) to close Gate-2 zero-targets (#180)
Always-enable the ACS donor (rent / real_estate_taxes imputation) and read childcare from CPS ASEC (SPM_CHILDCAREXPNS -> spm_unit_pre_subsidy_childcare_expenses), closing the two largest national Gate-2 residual targets (rent $0->~$735B, childcare $0->~$348B). Removes the now-dead include_acs flag. Independently reviewed via /cycle.
1 parent 91d9057 commit 32a993c

8 files changed

Lines changed: 31 additions & 43 deletions

File tree

src/microplex_us/data_sources/cps.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@
117117
"WICYN": "_receives_wic",
118118
"SPM_CAPHOUSESUB": "_spm_capped_housing_subsidy",
119119
"SPM_ENGVAL": "spm_unit_energy_subsidy",
120+
# Capped work childcare expenses are a PolicyEngine-computed variable
121+
# (derived from this pre-subsidy input), so only the input is exported.
122+
"SPM_CHILDCAREXPNS": "spm_unit_pre_subsidy_childcare_expenses",
120123
# Person relationship-to-householder code (eCPS cps.py:190-195, :1219).
121124
# Codes 43/44/46/47 mark an unmarried partner of the household head.
122125
"PERRP": "_person_relationship_to_householder",
@@ -200,6 +203,7 @@
200203
"social_security_survivors",
201204
"social_security_dependents",
202205
"spm_unit_energy_subsidy",
206+
"spm_unit_pre_subsidy_childcare_expenses",
203207
)
204208

205209
PERSON_ZERO_DEFAULT_VALUE_COLUMNS = (
@@ -216,6 +220,7 @@
216220
"social_security_survivors",
217221
"social_security_dependents",
218222
"spm_unit_energy_subsidy",
223+
"spm_unit_pre_subsidy_childcare_expenses",
219224
)
220225

221226
PERSON_CACHE_REQUIRED_COLUMNS = (

src/microplex_us/pipelines/pe_us_data_rebuild.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ def default_policyengine_us_data_rebuild_source_providers(
105105
puf_demographics_path: str | Path | None = None,
106106
puf_expand_persons: bool = True,
107107
include_donor_surveys: bool = True,
108-
include_acs: bool | None = None,
109108
include_sipp: bool | None = None,
110109
include_scf: bool | None = None,
111110
acs_year: int = 2022,
@@ -155,19 +154,21 @@ def default_policyengine_us_data_rebuild_source_providers(
155154
social_security_split_strategy=SOCIAL_SECURITY_SPLIT_STRATEGY_PE_QRF,
156155
),
157156
]
158-
resolved_include_acs = include_donor_surveys if include_acs is None else include_acs
159157
resolved_include_sipp = (
160158
include_donor_surveys if include_sipp is None else include_sipp
161159
)
162160
resolved_include_scf = include_donor_surveys if include_scf is None else include_scf
163-
if resolved_include_acs:
164-
providers.append(
165-
ACSSourceProvider(
166-
year=int(acs_year),
167-
policyengine_us_data_repo=policyengine_us_data_repo,
168-
policyengine_us_data_python=policyengine_us_data_python,
169-
)
161+
# The ACS donor is always enabled. It supplies the rent and real_estate_taxes
162+
# source imputation that eCPS also draws from ACS, so omitting it leaves those
163+
# variables at zero. ACS as a population spine ("multispine") is a separate,
164+
# independently controlled feature that is not enabled here.
165+
providers.append(
166+
ACSSourceProvider(
167+
year=int(acs_year),
168+
policyengine_us_data_repo=policyengine_us_data_repo,
169+
policyengine_us_data_python=policyengine_us_data_python,
170170
)
171+
)
171172
if resolved_include_sipp:
172173
providers.extend(
173174
[

src/microplex_us/pipelines/pe_us_data_rebuild_checkpoint.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,7 +1979,6 @@ def run_policyengine_us_data_rebuild_checkpoint(
19791979
puf_demographics_path: str | Path | None = None,
19801980
puf_expand_persons: bool = True,
19811981
include_donor_surveys: bool = True,
1982-
include_acs: bool | None = None,
19831982
include_sipp: bool | None = None,
19841983
include_scf: bool | None = None,
19851984
acs_year: int = 2022,
@@ -2066,7 +2065,6 @@ def run_policyengine_us_data_rebuild_checkpoint(
20662065
puf_demographics_path=puf_demographics_path,
20672066
puf_expand_persons=puf_expand_persons,
20682067
include_donor_surveys=include_donor_surveys,
2069-
include_acs=include_acs,
20702068
include_sipp=include_sipp,
20712069
include_scf=include_scf,
20722070
acs_year=acs_year,
@@ -2289,15 +2287,6 @@ def main(argv: list[str] | None = None) -> None:
22892287
action=argparse.BooleanOptionalAction,
22902288
default=True,
22912289
)
2292-
parser.add_argument(
2293-
"--include-acs",
2294-
action=argparse.BooleanOptionalAction,
2295-
default=None,
2296-
help=(
2297-
"Include the ACS donor provider. Defaults to --include-donor-surveys; "
2298-
"use --no-include-acs for an eCPS-shaped run that keeps SIPP/SCF."
2299-
),
2300-
)
23012290
parser.add_argument(
23022291
"--include-sipp",
23032292
action=argparse.BooleanOptionalAction,
@@ -2488,7 +2477,6 @@ def main(argv: list[str] | None = None) -> None:
24882477
puf_demographics_path=args.puf_demographics_path,
24892478
puf_expand_persons=not args.no_puf_expand_persons,
24902479
include_donor_surveys=args.include_donor_surveys,
2491-
include_acs=args.include_acs,
24922480
include_sipp=args.include_sipp,
24932481
include_scf=args.include_scf,
24942482
acs_year=args.acs_year,

src/microplex_us/pipelines/us.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9891,6 +9891,7 @@ def _attach_spm_unit_source_columns(
98919891
"takes_up_snap_if_eligible": "max",
98929892
"takes_up_tanf_if_eligible": "max",
98939893
"spm_unit_energy_subsidy": "first",
9894+
"spm_unit_pre_subsidy_childcare_expenses": "first",
98949895
}
98959896
aggregations = {
98969897
column: aggregation

tests/pipelines/test_pe_us_data_rebuild.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,21 @@ def test_default_policyengine_us_data_rebuild_source_providers_use_pe_style_bund
145145
assert isinstance(providers[5], SCFSourceProvider)
146146

147147

148-
def test_default_policyengine_us_data_rebuild_source_providers_can_disable_donor_surveys() -> (
148+
def test_default_policyengine_us_data_rebuild_source_providers_keeps_acs_when_donor_surveys_disabled() -> (
149149
None
150150
):
151+
# include_donor_surveys=False disables the SIPP/SCF donors, but the ACS donor is
152+
# always enabled (it supplies the rent / real_estate_taxes imputation), so it
153+
# remains alongside the CPS spine and PUF.
151154
providers = default_policyengine_us_data_rebuild_source_providers(
152155
include_donor_surveys=False,
153156
cps_download=False,
154157
)
155158

156-
assert len(providers) == 2
159+
assert len(providers) == 3
157160
assert isinstance(providers[0], CPSASECSourceProvider)
158161
assert isinstance(providers[1], PUFSourceProvider)
162+
assert isinstance(providers[2], ACSSourceProvider)
159163

160164

161165
def test_default_policyengine_us_data_rebuild_source_providers_can_include_donor_surveys() -> (
@@ -177,26 +181,6 @@ def test_default_policyengine_us_data_rebuild_source_providers_can_include_donor
177181
assert isinstance(providers[5], SCFSourceProvider)
178182

179183

180-
def test_default_policyengine_us_data_rebuild_source_providers_can_disable_only_acs() -> (
181-
None
182-
):
183-
providers = default_policyengine_us_data_rebuild_source_providers(
184-
include_donor_surveys=True,
185-
include_acs=False,
186-
cps_download=False,
187-
)
188-
189-
assert len(providers) == 5
190-
assert isinstance(providers[0], CPSASECSourceProvider)
191-
assert isinstance(providers[1], PUFSourceProvider)
192-
assert isinstance(providers[2], SIPPSourceProvider)
193-
assert providers[2].block == "tips"
194-
assert isinstance(providers[3], SIPPSourceProvider)
195-
assert providers[3].block == "assets"
196-
assert isinstance(providers[4], SCFSourceProvider)
197-
assert not any(isinstance(provider, ACSSourceProvider) for provider in providers)
198-
199-
200184
def test_build_policyengine_us_data_rebuild_pipeline_returns_configured_pipeline() -> (
201185
None
202186
):

tests/pipelines/test_us.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,7 @@ def test_build_policyengine_entity_tables_preserves_spm_source_inputs(
10141014
"takes_up_housing_assistance_if_eligible": [False, True, False],
10151015
"takes_up_snap_if_eligible": [False, True, False],
10161016
"spm_unit_energy_subsidy": [90.0, 90.0, 0.0],
1017+
"spm_unit_pre_subsidy_childcare_expenses": [1500.0, 1500.0, 0.0],
10171018
}
10181019
)
10191020

@@ -1028,6 +1029,10 @@ def test_build_policyengine_entity_tables_preserves_spm_source_inputs(
10281029
]
10291030
assert spm_units["takes_up_snap_if_eligible"].tolist() == [True, False]
10301031
assert spm_units["spm_unit_energy_subsidy"].tolist() == [90.0, 0.0]
1032+
assert spm_units["spm_unit_pre_subsidy_childcare_expenses"].tolist() == [
1033+
1500.0,
1034+
0.0,
1035+
]
10311036

10321037
def test_build_policyengine_entity_tables_adds_deterministic_snap_takeup(
10331038
self,

tests/policyengine/test_us.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,7 @@ def __init__(self, entity):
21892189
)
21902190
spm_unit_contract_inputs = (
21912191
"receives_housing_assistance",
2192+
"spm_unit_pre_subsidy_childcare_expenses",
21922193
"spm_unit_tenure_type",
21932194
)
21942195
legacy_spm_unit_contract_inputs = (
@@ -2246,6 +2247,7 @@ class FakeSystem:
22462247
"spm_unit_id": [1000],
22472248
"household_id": [10],
22482249
"receives_housing_assistance": [True],
2250+
"spm_unit_pre_subsidy_childcare_expenses": [1500.0],
22492251
"spm_unit_tenure_type": ["RENTER"],
22502252
**{name: [1.0] for name in legacy_spm_unit_contract_inputs},
22512253
}

tests/test_cps_source_provider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ def test_load_cps_asec_derives_policyengine_value_inputs(tmp_path):
447447
"WICYN": [1, 2],
448448
"SPM_CAPHOUSESUB": [700, 0],
449449
"SPM_ENGVAL": [90, -1],
450+
"SPM_CHILDCAREXPNS": [1500, -1],
450451
"PHIP_VAL": [900, -1],
451452
"POTC_VAL": [120, -1],
452453
"PMED_VAL": [450, -1],
@@ -474,6 +475,7 @@ def test_load_cps_asec_derives_policyengine_value_inputs(tmp_path):
474475
assert persons["receives_housing_assistance"].tolist() == [True, False]
475476
assert persons["takes_up_housing_assistance_if_eligible"].tolist() == [True, False]
476477
assert persons["spm_unit_energy_subsidy"].tolist() == [90, 0]
478+
assert persons["spm_unit_pre_subsidy_childcare_expenses"].tolist() == [1500, 0]
477479
assert persons["health_insurance_premiums_without_medicare_part_b"].tolist() == [900, 0]
478480
assert persons["over_the_counter_health_expenses"].tolist() == [120, 0]
479481
assert persons["other_medical_expenses"].tolist() == [450, 0]

0 commit comments

Comments
 (0)