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

Commit bea9230

Browse files
authored
Drop temporary CPS take-up anchors from H5 outputs
Remove temporary CPS take-up source anchors from persisted H5 outputs, add validation for retired reported/source variables, and support enhanced-CPS-only data builds for urgent policyengine.py release validation.
1 parent a7440e5 commit bea9230

9 files changed

Lines changed: 454 additions & 43 deletions

File tree

changelog.d/964.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Remove temporary CPS take-up source anchors from persisted H5 outputs and add an enhanced-CPS-only data build path.

modal_app/data_build.py

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def snapshot(self) -> dict[str, int]:
117117
# enhanced_cps.py produces both the dataset and calibration log
118118
"policyengine_us_data/datasets/cps/enhanced_cps.py": [
119119
"policyengine_us_data/storage/enhanced_cps_2024.h5",
120+
"policyengine_us_data/storage/enhanced_cps_2024.clone_diagnostics.json",
120121
"calibration_log.csv",
121122
],
122123
"policyengine_us_data/calibration/create_stratified_cps.py": (
@@ -317,12 +318,15 @@ def validate_and_maybe_upload_datasets(
317318
upload: bool,
318319
skip_enhanced_cps: bool,
319320
env: dict,
321+
require_small_enhanced_cps: bool = True,
320322
stage_only: bool = False,
321323
run_id: str = "",
322324
) -> None:
323325
validation_args = ["--validate-only"]
324326
if skip_enhanced_cps:
325327
validation_args.append("--no-require-enhanced-cps")
328+
elif not require_small_enhanced_cps:
329+
validation_args.append("--no-require-small-enhanced-cps")
326330

327331
print("=== Validating built datasets ===")
328332
run_script(
@@ -335,6 +339,8 @@ def validate_and_maybe_upload_datasets(
335339
upload_args = []
336340
if skip_enhanced_cps:
337341
upload_args.append("--no-require-enhanced-cps")
342+
elif not require_small_enhanced_cps:
343+
upload_args.append("--no-require-small-enhanced-cps")
338344
if stage_only:
339345
upload_args.append("--stage-only")
340346
if run_id:
@@ -504,6 +510,7 @@ def write_dataset_build_contract(
504510
upload_requested: bool,
505511
stage_only: bool,
506512
skip_enhanced_cps: bool,
513+
skip_stage_5: bool = False,
507514
) -> StageContract:
508515
"""Write the Stage 1 semantic handoff contract next to copied artifacts."""
509516
contract = build_dataset_build_output_contract(
@@ -518,6 +525,7 @@ def write_dataset_build_contract(
518525
upload_requested=upload_requested,
519526
stage_only=stage_only,
520527
skip_enhanced_cps=skip_enhanced_cps,
528+
skip_stage_5=skip_stage_5,
521529
)
522530
write_contract(
523531
contract,
@@ -559,6 +567,7 @@ def build_datasets(
559567
clear_checkpoints: bool = False,
560568
skip_tests: bool = False,
561569
skip_enhanced_cps: bool = False,
570+
skip_stage_5: bool = False,
562571
stage_only: bool = False,
563572
run_id: str = "",
564573
):
@@ -572,6 +581,8 @@ def build_datasets(
572581
skip_tests: Skip running the test suite (useful for calibration runs).
573582
skip_enhanced_cps: Skip enhanced_cps.py and small_enhanced_cps.py
574583
(useful for calibration runs that only need source_imputed H5).
584+
skip_stage_5: Skip source-imputed CPS and small enhanced CPS after
585+
enhanced_cps_2024.h5 is built.
575586
stage_only: Upload to HF staging only, without promoting a release.
576587
"""
577588
setup_gcp_credentials()
@@ -645,6 +656,12 @@ def build_datasets(
645656

646657
if sequential:
647658
for script, output in SCRIPT_OUTPUTS.items():
659+
if skip_stage_5 and script in (
660+
"policyengine_us_data/calibration/create_source_imputed_cps.py",
661+
"policyengine_us_data/datasets/cps/small_enhanced_cps.py",
662+
):
663+
print(f"Skipping {script} (--skip-stage-5)")
664+
continue
648665
if skip_enhanced_cps and script in (
649666
"policyengine_us_data/datasets/cps/enhanced_cps.py",
650667
"policyengine_us_data/datasets/cps/small_enhanced_cps.py",
@@ -761,33 +778,21 @@ def build_datasets(
761778
# GROUP 4: After Phase 4 - run in parallel
762779
# create_source_imputed_cps needs stratified_cps
763780
# small_enhanced_cps needs enhanced_cps
764-
print(
765-
"=== Phase 5: Building source imputed CPS "
766-
"and small enhanced CPS (parallel) ==="
767-
)
768-
phase5_futures = []
769-
with ThreadPoolExecutor(max_workers=2) as executor:
770-
phase5_futures.append(
771-
executor.submit(
772-
run_script_with_checkpoint,
773-
"policyengine_us_data/calibration/create_source_imputed_cps.py",
774-
SCRIPT_OUTPUTS[
775-
"policyengine_us_data/calibration/create_source_imputed_cps.py"
776-
],
777-
branch,
778-
checkpoint_volume,
779-
env=env,
780-
log_file=log_file,
781-
checkpoint_stats=checkpoint_stats,
782-
)
781+
if skip_stage_5:
782+
print("Skipping Phase 5 (--skip-stage-5)")
783+
else:
784+
print(
785+
"=== Phase 5: Building source imputed CPS "
786+
"and small enhanced CPS (parallel) ==="
783787
)
784-
if not skip_enhanced_cps:
788+
phase5_futures = []
789+
with ThreadPoolExecutor(max_workers=2) as executor:
785790
phase5_futures.append(
786791
executor.submit(
787792
run_script_with_checkpoint,
788-
"policyengine_us_data/datasets/cps/small_enhanced_cps.py",
793+
"policyengine_us_data/calibration/create_source_imputed_cps.py",
789794
SCRIPT_OUTPUTS[
790-
"policyengine_us_data/datasets/cps/small_enhanced_cps.py"
795+
"policyengine_us_data/calibration/create_source_imputed_cps.py"
791796
],
792797
branch,
793798
checkpoint_volume,
@@ -796,10 +801,25 @@ def build_datasets(
796801
checkpoint_stats=checkpoint_stats,
797802
)
798803
)
799-
else:
800-
print("Skipping small_enhanced_cps.py (--skip-enhanced-cps)")
801-
for future in as_completed(phase5_futures):
802-
future.result()
804+
if not skip_enhanced_cps:
805+
phase5_futures.append(
806+
executor.submit(
807+
run_script_with_checkpoint,
808+
"policyengine_us_data/datasets/cps/small_enhanced_cps.py",
809+
SCRIPT_OUTPUTS[
810+
"policyengine_us_data/datasets/cps/small_enhanced_cps.py"
811+
],
812+
branch,
813+
checkpoint_volume,
814+
env=env,
815+
log_file=log_file,
816+
checkpoint_stats=checkpoint_stats,
817+
)
818+
)
819+
else:
820+
print("Skipping small_enhanced_cps.py (--skip-enhanced-cps)")
821+
for future in as_completed(phase5_futures):
822+
future.result()
803823

804824
# Checkpoint the build log so it survives preemption
805825
log_file.flush()
@@ -857,6 +877,7 @@ def build_datasets(
857877
upload_requested=upload,
858878
stage_only=stage_only,
859879
skip_enhanced_cps=skip_enhanced_cps,
880+
skip_stage_5=skip_stage_5,
860881
)
861882
pipeline_volume.commit()
862883
print("Pipeline artifacts committed to shared volume")
@@ -871,6 +892,7 @@ def build_datasets(
871892
validate_and_maybe_upload_datasets(
872893
upload=upload,
873894
skip_enhanced_cps=skip_enhanced_cps,
895+
require_small_enhanced_cps=not skip_stage_5,
874896
env=env,
875897
stage_only=stage_only,
876898
run_id=run_id,
@@ -890,6 +912,7 @@ def main(
890912
clear_checkpoints: bool = False,
891913
skip_tests: bool = False,
892914
skip_enhanced_cps: bool = False,
915+
skip_stage_5: bool = False,
893916
stage_only: bool = False,
894917
run_id: str = "",
895918
):
@@ -905,6 +928,7 @@ def main(
905928
clear_checkpoints=clear_checkpoints,
906929
skip_tests=skip_tests,
907930
skip_enhanced_cps=skip_enhanced_cps,
931+
skip_stage_5=skip_stage_5,
908932
stage_only=stage_only,
909933
run_id=run_id,
910934
)

policyengine_us_data/datasets/cps/cps.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,16 @@ def add_rent(self, cps: h5py.File, person: DataFrame, household: DataFrame):
450450
cps["real_estate_taxes"][mask] = imputed_values["real_estate_taxes"]
451451

452452

453+
TEMPORARY_TAKEUP_SOURCE_ANCHORS = ("snap_reported", "ssi_reported")
454+
455+
456+
def _drop_persisted_dataset_variables(file_path, variable_names):
457+
with h5py.File(file_path, "a") as dataset_file:
458+
for variable_name in variable_names:
459+
if variable_name in dataset_file:
460+
del dataset_file[variable_name]
461+
462+
453463
@pipeline_node(
454464
PipelineNode(
455465
id="add_takeup",
@@ -636,10 +646,14 @@ def add_takeup(self):
636646
data["age"],
637647
)
638648

639-
for source_anchor in ("snap_reported", "ssi_reported"):
649+
for source_anchor in TEMPORARY_TAKEUP_SOURCE_ANCHORS:
640650
data.pop(source_anchor, None)
641651

642652
self.save_dataset(data)
653+
_drop_persisted_dataset_variables(
654+
self.file_path,
655+
TEMPORARY_TAKEUP_SOURCE_ANCHORS,
656+
)
643657

644658

645659
def add_marketplace_plan_benchmark_ratio(self):

policyengine_us_data/stage_contracts/dataset_build.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class _Stage1ArtifactSpec:
3131
required_for_stage_2: bool = False
3232
yearless_alias: bool = False
3333
skip_when_enhanced_cps_skipped: bool = False
34+
skip_when_stage_5_skipped: bool = False
3435

3536

3637
_STAGE_1_ARTIFACTS: tuple[_Stage1ArtifactSpec, ...] = (
@@ -84,6 +85,7 @@ class _Stage1ArtifactSpec:
8485
period=2024,
8586
substage_id="1d_enhanced_cps_reweighting",
8687
skip_when_enhanced_cps_skipped=True,
88+
skip_when_stage_5_skipped=True,
8789
),
8890
_Stage1ArtifactSpec(
8991
filename="stratified_extended_cps_2024.h5",
@@ -99,6 +101,7 @@ class _Stage1ArtifactSpec:
99101
period=2024,
100102
substage_id="1f_source_imputation",
101103
required_for_stage_2=True,
104+
skip_when_stage_5_skipped=True,
102105
),
103106
_Stage1ArtifactSpec(
104107
filename="source_imputed_stratified_extended_cps.h5",
@@ -108,6 +111,7 @@ class _Stage1ArtifactSpec:
108111
substage_id="1f_source_imputation",
109112
required_for_stage_2=True,
110113
yearless_alias=True,
114+
skip_when_stage_5_skipped=True,
111115
),
112116
_Stage1ArtifactSpec(
113117
filename="policy_data.db",
@@ -154,19 +158,22 @@ def build_dataset_build_output_contract(
154158
upload_requested: bool = False,
155159
stage_only: bool = False,
156160
skip_enhanced_cps: bool = False,
161+
skip_stage_5: bool = False,
157162
) -> StageContract:
158163
"""Build the Stage 1 handoff contract from copied pipeline artifacts."""
159164

160165
artifacts_dir = Path(artifacts_dir)
161166
parameters = {
162167
"period": 2024,
163168
"skip_enhanced_cps": skip_enhanced_cps,
169+
"skip_stage_5": skip_stage_5,
164170
"stage_only": stage_only,
165171
"upload_requested": upload_requested,
166172
}
167173
outputs = _stage_1_outputs(
168174
artifacts_dir=artifacts_dir,
169175
skip_enhanced_cps=skip_enhanced_cps,
176+
skip_stage_5=skip_stage_5,
170177
)
171178
execution = _execution_record(
172179
checkpoint_stats=checkpoint_stats,
@@ -193,6 +200,7 @@ def build_dataset_build_output_contract(
193200
substages=_stage_1_substages(
194201
outputs=outputs,
195202
skip_enhanced_cps=skip_enhanced_cps,
203+
skip_stage_5=skip_stage_5,
196204
),
197205
execution=execution,
198206
metadata={
@@ -207,12 +215,15 @@ def _stage_1_outputs(
207215
*,
208216
artifacts_dir: Path,
209217
skip_enhanced_cps: bool,
218+
skip_stage_5: bool,
210219
) -> tuple[ArtifactRef, ...]:
211220
outputs: list[ArtifactRef] = []
212221
missing_required: list[str] = []
213222
for spec in _STAGE_1_ARTIFACTS:
214223
if skip_enhanced_cps and spec.skip_when_enhanced_cps_skipped:
215224
continue
225+
if skip_stage_5 and spec.skip_when_stage_5_skipped:
226+
continue
216227
artifact_path = artifacts_dir / spec.filename
217228
if not artifact_path.exists():
218229
if spec.required:
@@ -276,6 +287,7 @@ def _stage_1_substages(
276287
*,
277288
outputs: tuple[ArtifactRef, ...],
278289
skip_enhanced_cps: bool,
290+
skip_stage_5: bool,
279291
) -> tuple[SubstageRecord, ...]:
280292
output_by_substage: dict[str, list[ArtifactRef]] = {
281293
substage_id: [] for substage_id in _SUBSTAGE_IDS
@@ -290,6 +302,8 @@ def _stage_1_substages(
290302
status = "completed"
291303
if substage_id == "1d_enhanced_cps_reweighting" and skip_enhanced_cps:
292304
status = "skipped"
305+
if substage_id == "1f_source_imputation" and skip_stage_5:
306+
status = "skipped"
293307
reuse_mode = "checkpointable"
294308
if substage_id in {
295309
"1a_raw_data_download",

0 commit comments

Comments
 (0)