diff --git a/changelog.d/us-cd-crosswalk-north-carolina-117th.fixed.md b/changelog.d/us-cd-crosswalk-north-carolina-117th.fixed.md new file mode 100644 index 0000000000..3cd4597daf --- /dev/null +++ b/changelog.d/us-cd-crosswalk-north-carolina-117th.fixed.md @@ -0,0 +1 @@ +Build the packaged 117th->119th congressional-district crosswalk from the CD plan registry, so North Carolina's SOI districts map through its 2019 plan (the 117th Congress's) instead of the 2016 plan in the 2020 BAF layer. Only North Carolina's rows change; the crosswalk sha256 is now a347303f. diff --git a/docs/evidence/spec-engine/us-f0-coverage.json b/docs/evidence/spec-engine/us-f0-coverage.json index c153d52291..03b169c7db 100644 --- a/docs/evidence/spec-engine/us-f0-coverage.json +++ b/docs/evidence/spec-engine/us-f0-coverage.json @@ -1887,7 +1887,7 @@ "alpha", "zeta" ], - "sha256": "b7a336af7cb300905d09ad9a33aa1a624f878c2471352d4e252fa69db55aee17" + "sha256": "3db095ce2d1ef89028d3862f0edc827d704f099c5b3323f9b115642a3c27e007" }, "failures": [], "observed": { @@ -1911,7 +1911,7 @@ "alpha", "zeta" ], - "sha256": "b7a336af7cb300905d09ad9a33aa1a624f878c2471352d4e252fa69db55aee17" + "sha256": "3db095ce2d1ef89028d3862f0edc827d704f099c5b3323f9b115642a3c27e007" }, "status": "covered" }, @@ -2045,7 +2045,7 @@ "congressional_district_vintage_crosswalk", "puma_ladder" ], - "sha256": "f49425ca8734ac559c73cf44f6458d86d3162a48956b98a27e6e758959361585", + "sha256": "44e7f5f6cd7ceeef85d532c3d46306d03de4f4ee811376e6d29920742746b65c", "target_vintage": "119th_congress" }, "failures": [], @@ -2054,7 +2054,7 @@ "congressional_district_vintage_crosswalk", "puma_ladder" ], - "sha256": "f49425ca8734ac559c73cf44f6458d86d3162a48956b98a27e6e758959361585", + "sha256": "44e7f5f6cd7ceeef85d532c3d46306d03de4f4ee811376e6d29920742746b65c", "target_vintage": "119th_congress" }, "status": "covered" @@ -2599,7 +2599,7 @@ "country": "us", "schema_id": "country_spec", "schema_version": 1, - "spec_sha256": "1cf1f44d9a11458f59ea7d75dce68303816cab3e66668a6c6f6ecfe62d56f8dc" + "spec_sha256": "ffbb93ed5ae22a95536ae475d6912a42628568609b442360dc1cd3bcf26772cd" } }, "report_schema_version": 3, @@ -2609,7 +2609,7 @@ "country": "us", "schema_id": "country_spec", "schema_version": 1, - "spec_sha256": "1cf1f44d9a11458f59ea7d75dce68303816cab3e66668a6c6f6ecfe62d56f8dc" + "spec_sha256": "ffbb93ed5ae22a95536ae475d6912a42628568609b442360dc1cd3bcf26772cd" }, "status": "pass" } diff --git a/packages/microcosm-build/src/microcosm/build/spec_engine/inventory_coverage.py b/packages/microcosm-build/src/microcosm/build/spec_engine/inventory_coverage.py index 6c30877dce..0ac879d20d 100644 --- a/packages/microcosm-build/src/microcosm/build/spec_engine/inventory_coverage.py +++ b/packages/microcosm-build/src/microcosm/build/spec_engine/inventory_coverage.py @@ -350,10 +350,10 @@ "acs_person_predictors": "878c788a6f037d7aca12b3586ea034eff04f3034ffa11935a736493042551f25", "authority": "9d4a9672a0f03039b1fe874b9fe21ed575be0d29f14afc396d03cdf5c809bdd2", "early_families": "4aa9f736fd76e83955477ad1667e58f48f264783f05bdc7f0102cd32d61323bd", - "full_checkpoint": "b7a336af7cb300905d09ad9a33aa1a624f878c2471352d4e252fa69db55aee17", + "full_checkpoint": "3db095ce2d1ef89028d3862f0edc827d704f099c5b3323f9b115642a3c27e007", "gap_fill_schedule": "1c31f9868f7884347cc19cf1ff65da43f950b9114941a715bab168246db414a7", "graph_nodes": "40cd51ffdfe2e9d9d08d48c08e8ded9de1e4b134783bab05c4abc6ad5c72ca1e", - "geography_assignment": "f49425ca8734ac559c73cf44f6458d86d3162a48956b98a27e6e758959361585", + "geography_assignment": "44e7f5f6cd7ceeef85d532c3d46306d03de4f4ee811376e6d29920742746b65c", "late_families": "d91f9ff0eb52f43e7b6eed3d5c58c37abe1620c3a11021da15dae9c10e16d382", "late_resource_semantics": "cc34ba11288d01145fc2dc1a669df3f7c4ef2019d73afb02dd8eeee852b16d20", "late_schedule": "e59c019d3d454eac99ac0ac209b6c5b6faaf9bdfcaeee18c36a25be19bf7da2f", diff --git a/packages/microcosm-build/src/microcosm/build/us/spec/sources.yaml b/packages/microcosm-build/src/microcosm/build/us/spec/sources.yaml index 156a37cfe6..e33ffec848 100644 --- a/packages/microcosm-build/src/microcosm/build/us/spec/sources.yaml +++ b/packages/microcosm-build/src/microcosm/build/us/spec/sources.yaml @@ -86,8 +86,8 @@ sources: value: 2020_puma - id: us_congressional_district_vintage_crosswalk_117_to_119 role: congressional_district_vintage_crosswalk - sha256: c7cb040b1f57ca2ea2adcbfe60cc2b250ca23acbc4b640cd421e766fa54c1aec - byte_size: 77935 + sha256: a347303fff3fea145f758488c43cb94355df5a8cb5e512552b2ee3791d2aa233 + byte_size: 78202 loader: kernel:load_congressional_district_vintage_crosswalk vintages: - vintage:cd_117 diff --git a/packages/microcosm-build/src/microcosm/build/us_runtime/congressional_district_vintage.py b/packages/microcosm-build/src/microcosm/build/us_runtime/congressional_district_vintage.py index 3182ef056a..94b1b03435 100644 --- a/packages/microcosm-build/src/microcosm/build/us_runtime/congressional_district_vintage.py +++ b/packages/microcosm-build/src/microcosm/build/us_runtime/congressional_district_vintage.py @@ -100,9 +100,10 @@ def load_default_congressional_district_vintage_crosswalk() -> pd.DataFrame: """Load the packaged Census-built default 117th->119th CD crosswalk. This is the canonical, versioned crosswalk built by - ``tools/build_us_congressional_district_vintage_crosswalk.py`` from Census - Block Assignment Files, the 119th BEF, and 2020 P.L. 94-171 block - populations. Builds may still pass an explicit crosswalk path to override it. + ``tools/build_us_congressional_district_vintage_crosswalk.py`` from the + block -> congressional-district plan registry (117th and 119th plans on + 2020 blocks, weighted by 2020 P.L. 94-171 block populations). Builds may + still pass an explicit crosswalk path to override it. """ return load_congressional_district_vintage_crosswalk( diff --git a/packages/microcosm-build/src/microcosm/build/us_runtime/congressional_district_vintage_crosswalk.py b/packages/microcosm-build/src/microcosm/build/us_runtime/congressional_district_vintage_crosswalk.py index e487efcf35..417cb3c849 100644 --- a/packages/microcosm-build/src/microcosm/build/us_runtime/congressional_district_vintage_crosswalk.py +++ b/packages/microcosm-build/src/microcosm/build/us_runtime/congressional_district_vintage_crosswalk.py @@ -1,4 +1,4 @@ -"""Build the 117th->119th congressional-district vintage crosswalk from Census. +"""Build the 117th->119th congressional-district vintage crosswalk. Microcosm calibrates congressional-district (CD) fiscal targets from the IRS SOI CD table, whose district geography is the **117th Congress** (the pre-2020- @@ -6,22 +6,25 @@ that ``policyengine.py`` consumes is the **119th Congress** (the post-2020- apportionment plan). ``congressional_district_vintage`` translates SOI CD facts from the old vintage onto the current one through a proportional crosswalk; this -module *builds that crosswalk* from primary Census sources, so the weights are -reproducible and cited rather than an opaque artifact. - -Method — a single-vintage block overlay, so no 2010<->2020 block bridge is -needed: - -- **Old (117th) district of each 2020 block**: the 2020 Block Assignment File - ``CD`` layer (``BlockAssign_ST{fips}_{usps}_CD.txt``, ``BLOCKID|DISTRICT``). - The 2020 BAFs were published with the 2020 P.L. 94-171 release and carry the - 116th-Congress plan (identical district geography to the 117th) expressed on - **2020** tabulation blocks. -- **Current (119th) district of each 2020 block**: the 119th Congressional - District Block Equivalency File (``NationalCD119.txt``, ``GEOID,CDFP``) — the - same source ``build_us_block_ladder_artifact`` already uses for the current - CD surface. -- **Weight**: 2020 P.L. 94-171 ``POP100`` per block (the block ladder's +module *builds that crosswalk* from the block -> congressional-district plan +registry (``cd_plan_registry``), so the weights are reproducible and cited +rather than an opaque artifact. + +Method — a block overlay of two plans on the same 2020 blocks: + +- **Old (117th) district of each 2020 block**: the registry's + ``117th_congress`` plan. That is the 2020 Block Assignment File ``CD`` layer, + which carries the 116th-Congress plans on 2020 blocks, except North + Carolina: Census reports it as the only state whose districts changed + between the 116th and 117th Congress, and the registry carries its 2019 + remedial plan onto 2020 blocks (``US_CD_PLAN_REGISTRY.md``). The parsers + below read the BAF and BEF layouts directly and remain for callers that + need them. +- **Current (119th) district of each 2020 block**: the registry's + ``119th_congress`` plan, the 119th Congressional District Block Equivalency + File (``NationalCD119.txt``), which agrees exactly with the block ladder. +- **Weight**: 2020 P.L. 94-171 ``POP100`` per block, as recorded in the + registry (the block ladder's :func:`~microcosm.build.us_runtime.block_ladder_sources.parse_pl_geo_blocks` convention), because congressional apportionment and equal-population redistricting are population operations. Population is the correct default @@ -39,7 +42,7 @@ DC non-voting delegate normalize to district ``00`` (the repo-wide convention; see ``block_ladder_sources``). -This module is pure and unit-testable. Download orchestration lives in +This module is pure and unit-testable. The build lives in ``tools/build_us_congressional_district_vintage_crosswalk.py``; the derived crosswalk is a regenerable build artifact, not a Ledger fact (the fact-vs- computed boundary of PolicyEngine/ledger#71). diff --git a/packages/microcosm-build/src/microcosm/build/us_runtime/data/CONGRESSIONAL_DISTRICT_VINTAGE_CROSSWALK.md b/packages/microcosm-build/src/microcosm/build/us_runtime/data/CONGRESSIONAL_DISTRICT_VINTAGE_CROSSWALK.md index aa08e15f3f..3b9099f0a2 100644 --- a/packages/microcosm-build/src/microcosm/build/us_runtime/data/CONGRESSIONAL_DISTRICT_VINTAGE_CROSSWALK.md +++ b/packages/microcosm-build/src/microcosm/build/us_runtime/data/CONGRESSIONAL_DISTRICT_VINTAGE_CROSSWALK.md @@ -37,25 +37,31 @@ Regenerate with: ``` uv run --python 3.13 --package microcosm-build --group dev python \ tools/build_us_congressional_district_vintage_crosswalk.py \ - --out packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv \ - --cache-dir ~/.cache/populace-us-geography + --cd-plan-registry build/us/us_cd_plan_registry_2020.npz \ + --out packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv ``` -The method is a **single-vintage block overlay** — both district assignments are -read on the same 2020 census blocks, so no 2010↔2020 block bridge is needed: +The builder reads the pinned US block -> congressional-district plan registry +(`../US_CD_PLAN_REGISTRY.md`) and refuses any other file. The method is a +**block overlay** of two of its plans on the same 2020 census blocks: -| Role | Source | Format | +| Role | Registry plan | Underlying source | |---|---|---| -| Old (117th) district of each 2020 block | 2020 Block Assignment Files, `CD` layer (`BlockAssign_ST{fips}_{usps}_CD.txt`) | `BLOCKID\|DISTRICT` | -| Current (119th) district of each 2020 block | 119th Congressional District BEF (`NationalCD119.txt`) | `GEOID,CDFP` | -| Weight | 2020 P.L. 94-171 `POP100` per block (`{usps}geo2020.pl`, summary level 750) | pipe-delimited | - -The 2020 Block Assignment Files were published with the 2020 P.L. 94-171 release -and carry the **116th-Congress plan** — whose district geography is identical to -the 117th — expressed on **2020** tabulation blocks. Joining that old assignment -and the current 119th BEF on the same 2020 blocks, weighted by 2020 block -population, redistributes each old district's population across the current -districts it overlaps. +| Old (117th) district of each 2020 block | `117th_congress` | 2020 Block Assignment Files, `CD` layer; North Carolina carried from its 2019 plan | +| Current (119th) district of each 2020 block | `119th_congress` | 119th Congressional District BEF (`NationalCD119.txt`) | +| Weight | the registry's `population` | 2020 P.L. 94-171 `POP100` per block | + +The 2020 Block Assignment Files carry the **116th-Congress plans** on 2020 +tabulation blocks. Census reports North Carolina as the only state whose +districts changed between the 116th and 117th Congress. Its 117th districts +are the 2019 remedial plan, which the registry carries onto 2020 blocks from +the General Assembly's block file. On 2020 blocks the 2016 and 2019 North +Carolina plans put only 55% of the state's population in the same district. +An earlier build read the BAF layer directly and so mapped North Carolina's SOI +districts through the 2016 plan; only North Carolina's rows changed when the +builder moved to the registry. Joining the 117th and 119th assignments on the +same 2020 blocks, weighted by 2020 block population, redistributes each old +district's population across the current districts it overlaps. **Population is the correct default basis**: congressional apportionment and one-person-one-vote redistricting are population operations, and equal-population @@ -63,17 +69,12 @@ districts make an at-large state split ≈ evenly (e.g. Montana's old at-large district splits 50/50 into MT-01/MT-02). ACS income/tax proxy weights for *fiscal* targets are a documented future refinement (#205). -Source URLs and their SHA-256s, plus the crosswalk SHA-256 and full per-state -population conservation, are recorded in +The registry's SHA-256 and its 117th-plan known deviations, plus the crosswalk +SHA-256 and full per-state population conservation, are recorded in `congressional_district_vintage_crosswalk.csv.provenance.json` (written by the -builder). Base source pages: - -- 119th CD BEF: `https://www2.census.gov/programs-surveys/decennial/rdo/mapping-files/2025/119-congressional-district-befs/cd119.zip` -- 2020 Block Assignment Files: `https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST{fips}_{usps}.zip` -- 2020 P.L. 94-171 Redistricting Data: `https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/{state}/{usps}2020.pl.zip` - -The exact per-file URLs and their SHA-256s are in the provenance sidecar; these -are the templates the builder resolves. +builder). Every underlying Census and General Assembly source file, with its +URL and SHA-256, is in the registry's receipt, +`../us_cd_plan_registry.provenance.json`. ## Geoid conventions @@ -84,7 +85,7 @@ with `block_ladder_sources`), so DC is `…US1100` on both sides. ## Conservation and coverage (current build) -- 1,444 rows; **436 source districts → 436 current districts** (the full 119th +- 1,449 rows; **436 source districts → 436 current districts** (the full 119th Congress set, including one-district states and DC). - Every populated 2020 block in all 50 states + DC is covered: **331,449,281 people**, with **zero** unmatched or cross-state population. diff --git a/packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv b/packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv index 184678aaf5..8639096141 100644 --- a/packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv +++ b/packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv @@ -949,51 +949,56 @@ source_geography_id,target_geography_id,pair_population,weight 5001700US3627,5001900US3624,337457,0.468630397227021 5001700US3627,5001900US3625,43930,0.06100609366580937 5001700US3627,5001900US3626,34520,0.04793831899257317 -5001700US3701,5001900US3701,323902,0.43804103955327933 -5001700US3701,5001900US3703,78733,0.10647753075667438 -5001700US3701,5001900US3704,286624,0.38762673562040106 -5001700US3701,5001900US3713,50174,0.06785469406964525 -5001700US3702,5001900US3701,119881,0.13153875357427358 -5001700US3702,5001900US3702,162205,0.1779785247329856 -5001700US3702,5001900US3704,126509,0.1388112893279817 -5001700US3702,5001900US3713,502779,0.5516714323647591 -5001700US3703,5001900US3701,184554,0.24390933721007071 -5001700US3703,5001900US3703,572096,0.7560906627899293 -5001700US3704,5001900US3702,583465,0.6665406237183888 -5001700US3704,5001900US3704,291898,0.33345937628161115 -5001700US3705,5001900US3705,309843,0.4089197596445083 -5001700US3705,5001900US3706,108247,0.1428605365370174 -5001700US3705,5001900US3710,321815,0.4247199789893508 -5001700US3705,5001900US3711,17806,0.023499724829123506 -5001700US3706,5001900US3704,40640,0.05110541875885762 -5001700US3706,5001900US3705,216290,0.2719879680943237 -5001700US3706,5001900US3709,413171,0.5195688231795267 -5001700US3706,5001900US3713,125118,0.15733778996729203 -5001700US3707,5001900US3701,117333,0.15170592068516106 -5001700US3707,5001900US3703,94842,0.12262614038354124 -5001700US3707,5001900US3707,493650,0.6382656860919754 -5001700US3707,5001900US3713,67599,0.08740225283932229 -5001700US3708,5001900US3706,194142,0.2394999346174628 -5001700US3708,5001900US3707,108801,0.13422047978445967 -5001700US3708,5001900US3708,212801,0.26251828860592097 -5001700US3708,5001900US3709,294870,0.36376129699215654 -5001700US3709,5001900US3707,143220,0.1862619762601165 -5001700US3709,5001900US3708,526847,0.685180585160687 -5001700US3709,5001900US3712,77291,0.10051930182321368 -5001700US3709,5001900US3714,21559,0.028038136755982764 -5001700US3710,5001900US3710,243711,0.31378115621270236 -5001700US3710,5001900US3711,127525,0.16419013481551867 -5001700US3710,5001900US3714,405455,0.522028708971779 -5001700US3711,5001900US3705,80652,0.10493897036149376 -5001700US3711,5001900US3711,600339,0.7811208218996281 -5001700US3711,5001900US3714,87570,0.11394020773887824 -5001700US3712,5001900US3708,6023,0.006651654520375179 -5001700US3712,5001900US3712,668379,0.7381414903991104 -5001700US3712,5001900US3714,231087,0.2552068550805145 -5001700US3713,5001900US3705,138886,0.17362008745634058 -5001700US3713,5001900US3706,443282,0.5541426753439624 -5001700US3713,5001900US3709,37630,0.047040910466008785 -5001700US3713,5001900US3710,180144,0.2251963267336882 +5001700US3701,5001900US3701,569642,0.8285954916441084 +5001700US3701,5001900US3703,117837,0.1714045083558916 +5001700US3702,5001900US3702,583238,0.6389975666647311 +5001700US3702,5001900US3704,231906,0.25407701434911845 +5001700US3702,5001900US3713,97595,0.10692541898615048 +5001700US3703,5001900US3701,164131,0.22005869805095118 +5001700US3703,5001900US3703,581711,0.7799292351957696 +5001700US3703,5001900US3707,9,1.2066753279140203e-05 +5001700US3704,5001900US3701,11897,0.013558051068621037 +5001700US3704,5001900US3702,162432,0.18511064563993043 +5001700US3704,5001900US3704,513723,0.5854486567307057 +5001700US3704,5001900US3709,15887,0.018105132161652722 +5001700US3704,5001900US3713,173547,0.19777751439909014 +5001700US3705,5001900US3705,274695,0.36731050805303167 +5001700US3705,5001900US3710,16703,0.022334543460965025 +5001700US3705,5001900US3711,21,2.8080309685701107e-05 +5001700US3705,5001900US3714,456436,0.6103268681763176 +5001700US3706,5001900US3705,264080,0.3271187217496541 +5001700US3706,5001900US3706,256002,0.31711241671218926 +5001700US3706,5001900US3709,99558,0.12332355990590753 +5001700US3706,5001900US3710,187651,0.23244530163224908 +5001700US3707,5001900US3703,46123,0.05599177416339503 +5001700US3707,5001900US3707,515727,0.6260752707752147 +5001700US3707,5001900US3713,261896,0.3179329550613903 +5001700US3708,5001900US3706,101330,0.12341769453846557 +5001700US3708,5001900US3707,191522,0.23326955189377285 +5001700US3708,5001900US3708,212774,0.25915401695181556 +5001700US3708,5001900US3709,178189,0.21703025335157053 +5001700US3708,5001900US3713,137218,0.1671284832643755 +5001700US3709,5001900US3707,38413,0.04884453113675146 +5001700US3709,5001900US3708,494838,0.6292174549930446 +5001700US3709,5001900US3709,116681,0.14836718656619627 +5001700US3709,5001900US3712,77576,0.09864273416459614 +5001700US3709,5001900US3714,58926,0.07492809313941157 +5001700US3710,5001900US3705,206892,0.2667416167822936 +5001700US3710,5001900US3706,29960,0.03862681417743322 +5001700US3710,5001900US3709,27,3.48105468221194e-05 +5001700US3710,5001900US3710,538723,0.6945645265056528 +5001700US3710,5001900US3714,25,3.22319877982587e-05 +5001700US3711,5001900US3705,4,5.113670503197962e-06 +5001700US3711,5001900US3711,745649,0.9532508242597643 +5001700US3711,5001900US3714,36564,0.04674406206973257 +5001700US3712,5001900US3708,38059,0.04229374589525411 +5001700US3712,5001900US3712,668094,0.7424314319909587 +5001700US3712,5001900US3714,193720,0.21527482211378718 +5001700US3713,5001900US3704,42,5.442127509047537e-05 +5001700US3713,5001900US3706,358379,0.46436767013451125 +5001700US3713,5001900US3709,335329,0.43450075606700034 +5001700US3713,5001900US3710,2593,0.0033598658645143486 +5001700US3713,5001900US3713,75414,0.09771728665888356 5001700US3800,5001900US3800,779094,1.0 5001700US3901,5001900US3901,546669,0.7112991997918158 5001700US3901,5001900US3908,221881,0.2887008002081842 diff --git a/packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv.provenance.json b/packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv.provenance.json index 4fa9d278dd..4a79057b50 100644 --- a/packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv.provenance.json +++ b/packages/microcosm-build/src/microcosm/build/us_runtime/data/congressional_district_vintage_crosswalk.csv.provenance.json @@ -1,10 +1,10 @@ { "block_vintage": "2020_tabulation_blocks", - "crosswalk_sha256": "c7cb040b1f57ca2ea2adcbfe60cc2b250ca23acbc4b640cd421e766fa54c1aec", + "crosswalk_sha256": "a347303fff3fea145f758488c43cb94355df5a8cb5e512552b2ee3791d2aa233", "diagnostics": { "basis": "block_population", "cross_state_population": 0, - "row_count": 1444, + "row_count": 1449, "source_district_count": 436, "state_conservation": { "01": { @@ -268,524 +268,110 @@ "unmatched_population_no_old_district": 0 }, "kind": "us_congressional_district_vintage_crosswalk", - "method": "Population-weighted overlay of the old (117th, via the 2020 BAF CD layer) and current (119th BEF) congressional-district assignments on 2020 tabulation blocks, weighted by 2020 P.L. 94-171 block populations. Each old district's population is redistributed across current districts; totals conserve by construction.", + "known_deviations": { + "37": { + "agreement_with_the_116th_plan": { + "blocks": 174988, + "blocks_agreeing": 101110, + "population": 10439388, + "population_agreeing": 5776745, + "population_share_agreeing": 0.5533605035084432 + }, + "filled_blocks": [ + { + "block": "370710333061012", + "district": "05", + "population": 13, + "rule": "block_group" + }, + { + "block": "370899320003017", + "district": "11", + "population": 0, + "rule": "block_group" + }, + { + "block": "371499203042028", + "district": "11", + "population": 0, + "rule": "block_group" + }, + { + "block": "371690703012013", + "district": "10", + "population": 0, + "rule": "block_group" + } + ], + "method": "Each 2020 block takes the 2019-plan district whose 2010 blocks cover the most of its land area (then water area, then the lower district code). A 2020 block with no North Carolina 2010 counterpart takes the district holding most of its block group's population (else its tract's, else its county's).", + "method_check": { + "blocks": 174988, + "blocks_agreeing": 174919, + "description": "The same method run on the 2016 plan, compared with Census's own 2020-block version of that plan (the 2020 BAF CD layer), over populated blocks.", + "minimum_population_share": 0.999, + "population": 10439388, + "population_agreeing": 10438522, + "population_share_agreeing": 0.9999170449455466 + }, + "note": "North Carolina is the one state whose 117th districts differ from its 116th. Its 117th districts are the 2019 remedial plan (HB 1029 3rd edition, S.L. 2019-249, 'C-Goodwin-A-1-TC'), which Census never tabulated on 2020 blocks, so this state's rows are not from the 2020 BAF CD layer (the 2016 plan). The General Assembly's block file for the 2019 plan (2010 blocks) is carried onto 2020 blocks through the Census 2010-2020 tabulation block relationship file.", + "official_district_population_check": { + "description": "District populations of the carried 2019 plan minus the General Assembly's official 2020-census populations for that plan.", + "differences": { + "01": -28, + "02": 513, + "03": 41, + "04": 11, + "05": 267, + "06": 91, + "07": -170, + "08": -100, + "09": 158, + "10": -704, + "11": -29, + "12": 14, + "13": -64 + }, + "largest_share": 0.000907, + "maximum_share": 0.005 + }, + "source_files": { + "census_block_relationship_2010_2020_nc": { + "member": "tab2010_tab2020_st37_nc.txt", + "sha256": "b8749f2c00fa4339c107433cc8ff875fbfda1d920a32cfb2f5b731cff3abe8a6", + "url": "https://www2.census.gov/geo/docs/maps-data/data/rel2020/t10t20/TAB2010_TAB2020_ST37.zip" + }, + "ncga_2016_plan": { + "member": "2016 Contingent Congressional Plan - Corrected.csv", + "sha256": "6702777cbbd13323adb8ad5be77bda9be0e9b7fe6ecf215cf0aad4a03116a628", + "url": "https://www.ncleg.gov/Files/GIS/Plans_Main/Congress_2016/baf.zip" + }, + "ncga_2019_plan": { + "member": "C-Goodwin-A-1-TC.csv", + "sha256": "d669b2143e5cb44cef7a37774cc5cd2f1c5ad2ab5a59488d074fa419102dabfa", + "url": "https://www.ncleg.gov/Files/GIS/Plans_Main/Congress_2019/HB1029%203rd%20Edition%20-%20Blockfile.zip" + }, + "ncga_2020_district_populations_2019_plan": { + "sha256": "7737126c3bef52144495512b780fd66deafab8c45da97d293bd9eaa86f39d5f3", + "url": "https://www.ncleg.gov/Files/GIS/Maps_Reports/Decennial_ReCalc/2020/DeviationReports/2020_Deviation_Rpt_Cong.xlsx" + } + }, + "straddling_blocks": 289, + "straddling_populated_blocks": 183, + "straddling_population": 16671 + } + }, + "method": "Population-weighted overlay of the 117th and 119th plans of the US block -> congressional-district plan registry on 2020 tabulation blocks, weighted by 2020 P.L. 94-171 block populations. Each old district's population is redistributed across current districts; totals conserve by construction. The registry's 117th plan is the 2020 BAF CD layer except North Carolina, carried from its 2019 plan (see known_deviations).", "schema_version": 1, "source_geography_vintage": "117th_congress", "sources": { - "baf2020_cd_ak": { - "member": "BlockAssign_ST02_AK_CD.txt", - "sha256": "686f4283976326621be7173a21036ba2ea65e0b08346e6aac0952718d23b31db", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST02_AK.zip" - }, - "baf2020_cd_al": { - "member": "BlockAssign_ST01_AL_CD.txt", - "sha256": "4031d99d9acc6b0d40fb72123ed2029dc1e66525c6fd1d87ef01228ada18411d", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST01_AL.zip" - }, - "baf2020_cd_ar": { - "member": "BlockAssign_ST05_AR_CD.txt", - "sha256": "199b6bd6c96f2e7b868d307d13c393dffe76f55e15e0df7a67932412351de996", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST05_AR.zip" - }, - "baf2020_cd_az": { - "member": "BlockAssign_ST04_AZ_CD.txt", - "sha256": "0fe372e474a5aea77ebc421f926185a7318fc7c963cf74813ac7bd4c2af0e1cd", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST04_AZ.zip" - }, - "baf2020_cd_ca": { - "member": "BlockAssign_ST06_CA_CD.txt", - "sha256": "edc540d7e782507c080d6caea087b302f3f406c8572629763f0f7017ebe427a1", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST06_CA.zip" - }, - "baf2020_cd_co": { - "member": "BlockAssign_ST08_CO_CD.txt", - "sha256": "9199c97ed89fe42cfea45492095a26c02ae8eda42656abd220a907fdf39c917c", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST08_CO.zip" - }, - "baf2020_cd_ct": { - "member": "BlockAssign_ST09_CT_CD.txt", - "sha256": "1125c49564224cced90844ab2345df9a2c191a0084c5029ff53ddc9c320c54ba", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST09_CT.zip" - }, - "baf2020_cd_dc": { - "member": "BlockAssign_ST11_DC_CD.txt", - "sha256": "c789c1df0921dfdd9925d1c9747cec0b1c51895f3c1cfd5ee8938e15277eaa48", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST11_DC.zip" - }, - "baf2020_cd_de": { - "member": "BlockAssign_ST10_DE_CD.txt", - "sha256": "8f81f5239dca88f378f098325ae630d215bf0c5fd8986a088d2b2b2c4746f6d9", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST10_DE.zip" - }, - "baf2020_cd_fl": { - "member": "BlockAssign_ST12_FL_CD.txt", - "sha256": "a8eeda07f30d1010f984950dae030bb6276c6d11a99f64a0a4bbebccd9996451", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST12_FL.zip" - }, - "baf2020_cd_ga": { - "member": "BlockAssign_ST13_GA_CD.txt", - "sha256": "97fee9eee9eb0b338e9f8ea937c2d667c5293a4c1e38182bfe1c4786c4dbd111", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST13_GA.zip" - }, - "baf2020_cd_hi": { - "member": "BlockAssign_ST15_HI_CD.txt", - "sha256": "99ff6595ad6566804cc4682c9e73b9d854ae127154bffb9b0f2e6e97a7fa8147", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST15_HI.zip" - }, - "baf2020_cd_ia": { - "member": "BlockAssign_ST19_IA_CD.txt", - "sha256": "883baba3cc35a45b585293e58ce23c08d3644c5f4e59a92dc329c76d4fde1447", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST19_IA.zip" - }, - "baf2020_cd_id": { - "member": "BlockAssign_ST16_ID_CD.txt", - "sha256": "31c1e32901138ff732f9eded36bde73231af5e1529fd3a9317d106561a7a4f8a", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST16_ID.zip" - }, - "baf2020_cd_il": { - "member": "BlockAssign_ST17_IL_CD.txt", - "sha256": "97c913144c29b33ebaa17546e4976bb19e5cf65d33b26a1ea7609736aeb2c9b6", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST17_IL.zip" - }, - "baf2020_cd_in": { - "member": "BlockAssign_ST18_IN_CD.txt", - "sha256": "3158704fa33bc433087d21eff4f93702d0999b3f405085fb58fb64e72cc58eb0", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST18_IN.zip" - }, - "baf2020_cd_ks": { - "member": "BlockAssign_ST20_KS_CD.txt", - "sha256": "d331ad02ad4ba10a15bce5691c22834b3ca853357637bd3f6df02cc9c75fb816", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST20_KS.zip" - }, - "baf2020_cd_ky": { - "member": "BlockAssign_ST21_KY_CD.txt", - "sha256": "e3b7573bbad03ab78db313535b0e763ddb1268f0b0d59d760aa0e688793d055a", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST21_KY.zip" - }, - "baf2020_cd_la": { - "member": "BlockAssign_ST22_LA_CD.txt", - "sha256": "4b160562136f38e136b4384d591c5fcda351c6da2486bab07b39cc7576afe79e", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST22_LA.zip" - }, - "baf2020_cd_ma": { - "member": "BlockAssign_ST25_MA_CD.txt", - "sha256": "6d77b72047fef535efe396b675d4ec8cedfbc975e4cab9460431b861353e4f18", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST25_MA.zip" - }, - "baf2020_cd_md": { - "member": "BlockAssign_ST24_MD_CD.txt", - "sha256": "0cff392f34e07b00f2705bab056c902d67c63a32cfbbe30534777ea147d79b93", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST24_MD.zip" - }, - "baf2020_cd_me": { - "member": "BlockAssign_ST23_ME_CD.txt", - "sha256": "431631b3352f8bab61258626a824529434e3f51e421e62890f215e47d46cda64", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST23_ME.zip" - }, - "baf2020_cd_mi": { - "member": "BlockAssign_ST26_MI_CD.txt", - "sha256": "d819d7d0d2b5d83e0e5aab54d2674b5a625bde51add10f257a1511abd6014a88", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST26_MI.zip" - }, - "baf2020_cd_mn": { - "member": "BlockAssign_ST27_MN_CD.txt", - "sha256": "afc53ab51e43091e4454f54e0d57b61f60a73f0d521977a4165965fefabc3e61", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST27_MN.zip" - }, - "baf2020_cd_mo": { - "member": "BlockAssign_ST29_MO_CD.txt", - "sha256": "9d72554bbb23c0f859741641fcadd98bd2bd4858067ad010eddc881b9f14ccad", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST29_MO.zip" - }, - "baf2020_cd_ms": { - "member": "BlockAssign_ST28_MS_CD.txt", - "sha256": "d8e4d0ef5f467650394cc902883669143724cd31699a601e52ef185d52c60b8f", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST28_MS.zip" - }, - "baf2020_cd_mt": { - "member": "BlockAssign_ST30_MT_CD.txt", - "sha256": "7e5fde3990b536705f337bc7c3ffde2da3b2a49b7c9e58c22d6c198c7c0a93a8", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST30_MT.zip" - }, - "baf2020_cd_nc": { - "member": "BlockAssign_ST37_NC_CD.txt", - "sha256": "9619665fc781f4ab45891a4bea3160d7bc3877d9570ba64e7b1b42224c225bf0", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST37_NC.zip" - }, - "baf2020_cd_nd": { - "member": "BlockAssign_ST38_ND_CD.txt", - "sha256": "a89f5f58b9882aaf520c5c77c52b3b49dc198ff533b8436bf04ebfed252529fa", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST38_ND.zip" - }, - "baf2020_cd_ne": { - "member": "BlockAssign_ST31_NE_CD.txt", - "sha256": "9065e5d1e048af09777e815294c201ae548ebeb73f02195cfb2c895aa89ae39e", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST31_NE.zip" - }, - "baf2020_cd_nh": { - "member": "BlockAssign_ST33_NH_CD.txt", - "sha256": "c06b3a004317849b253ef32972f2a6a7e0abf9f71c496e4ddf5578c66c6b7c17", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST33_NH.zip" - }, - "baf2020_cd_nj": { - "member": "BlockAssign_ST34_NJ_CD.txt", - "sha256": "179a04d0e2f07f516298218002afe304999fbf272b59387ebb18d550eb34e430", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST34_NJ.zip" - }, - "baf2020_cd_nm": { - "member": "BlockAssign_ST35_NM_CD.txt", - "sha256": "e7507ee4cb1780233151c87aa2a84901dc86fede8680709e71fa14ad13b4df1e", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST35_NM.zip" - }, - "baf2020_cd_nv": { - "member": "BlockAssign_ST32_NV_CD.txt", - "sha256": "ea0e6834f8389127a74eb4be4e28b86659fe067af7d4fa0ab91f746060d4adb9", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST32_NV.zip" - }, - "baf2020_cd_ny": { - "member": "BlockAssign_ST36_NY_CD.txt", - "sha256": "7cf9a890beb47baca04bfa35c74db9300e1044c8a99b2cff559ade8fc1a89236", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST36_NY.zip" - }, - "baf2020_cd_oh": { - "member": "BlockAssign_ST39_OH_CD.txt", - "sha256": "c2b0b18a5a5230572e7ca9a73ca3a0bcbab6e2ffb83505230f614cc23641c1c5", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST39_OH.zip" - }, - "baf2020_cd_ok": { - "member": "BlockAssign_ST40_OK_CD.txt", - "sha256": "3859f1e17929d0ce91d09b750d31a3da39de398a3fc607044d2a6025452996f6", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST40_OK.zip" - }, - "baf2020_cd_or": { - "member": "BlockAssign_ST41_OR_CD.txt", - "sha256": "ad8fe9e57a32aa434b38b92cfff510e0fccbc367721f0c7043732ba9233cc674", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST41_OR.zip" - }, - "baf2020_cd_pa": { - "member": "BlockAssign_ST42_PA_CD.txt", - "sha256": "f61fb24709191cb1ee9f97b915fe37ef483e4b5f8bca79385790da8ad6d80bf3", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST42_PA.zip" - }, - "baf2020_cd_ri": { - "member": "BlockAssign_ST44_RI_CD.txt", - "sha256": "0833228c38a62f9c005b0ac9cb238e2c003e270380679657f7eb2f574966c896", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST44_RI.zip" - }, - "baf2020_cd_sc": { - "member": "BlockAssign_ST45_SC_CD.txt", - "sha256": "ee313741c4dff930cd8959855f6651cf823daf1f82f2f5e28bef17abb67d0d59", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST45_SC.zip" - }, - "baf2020_cd_sd": { - "member": "BlockAssign_ST46_SD_CD.txt", - "sha256": "b790819eb1f0ea1d0c4fe98c87c61989230817e66804c2ae1d79c379547d8eaa", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST46_SD.zip" - }, - "baf2020_cd_tn": { - "member": "BlockAssign_ST47_TN_CD.txt", - "sha256": "96dc990f409bbfbd02dedfd75caf6c18d65d70b8102e54100bc1b5a4e3812744", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST47_TN.zip" - }, - "baf2020_cd_tx": { - "member": "BlockAssign_ST48_TX_CD.txt", - "sha256": "db3f1a4e06aca3d61fb15f1d3581cd08d65120588f33cf00c8265c9f043aa725", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST48_TX.zip" - }, - "baf2020_cd_ut": { - "member": "BlockAssign_ST49_UT_CD.txt", - "sha256": "57ebe75d5353f004a466d4a7df6897207b1ef19d47849b61b188f9969c0bc2ce", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST49_UT.zip" - }, - "baf2020_cd_va": { - "member": "BlockAssign_ST51_VA_CD.txt", - "sha256": "87ebb3582232400286fb0ff4e6a51fdfb1449221c780c9d1a3a574f752010f36", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST51_VA.zip" - }, - "baf2020_cd_vt": { - "member": "BlockAssign_ST50_VT_CD.txt", - "sha256": "6869df8eecd778986f116f4138ce5d0f692468b944056eb58984dfa51ba3909d", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST50_VT.zip" - }, - "baf2020_cd_wa": { - "member": "BlockAssign_ST53_WA_CD.txt", - "sha256": "58114c250c830d81ac8f499f29ae71ecae545624f484a1f9cce448182cf09001", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST53_WA.zip" - }, - "baf2020_cd_wi": { - "member": "BlockAssign_ST55_WI_CD.txt", - "sha256": "75bed98fefe7454b6fc6c5e972744380bf99400d3c6a3b839e7a5e1fb13d093c", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST55_WI.zip" - }, - "baf2020_cd_wv": { - "member": "BlockAssign_ST54_WV_CD.txt", - "sha256": "949ed08edf0a082f6a9a0c4300fb3c850358115cf2cc0354437116d7da7c060e", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST54_WV.zip" - }, - "baf2020_cd_wy": { - "member": "BlockAssign_ST56_WY_CD.txt", - "sha256": "e4ce0356da93caff0870166d44e8491dc5b386580dd4e8504ad1957e76f37f09", - "url": "https://www2.census.gov/geo/docs/maps-data/data/baf2020/BlockAssign_ST56_WY.zip" - }, - "cd119_bef": { - "member": "NationalCD119.txt", - "sha256": "1433feb5178dc7b4188ee30f5f7f715851f4400740b8fe1ce606a876c6294bd6", - "url": "https://www2.census.gov/programs-surveys/decennial/rdo/mapping-files/2025/119-congressional-district-befs/cd119.zip" - }, - "pl94171_ak": { - "member": "akgeo2020.pl", - "sha256": "c90837c12ab2f5fe99132c558705e8d0b5174afdf2a0d28b71927f535fe63487", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Alaska/ak2020.pl.zip" - }, - "pl94171_al": { - "member": "algeo2020.pl", - "sha256": "bd3fbbf22615b166869f9119aa12017b9a7b82baf0f22a9170e92a88a397cd77", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Alabama/al2020.pl.zip" - }, - "pl94171_ar": { - "member": "argeo2020.pl", - "sha256": "940ad2916676e443dfdab98c3e145fcf62ef9dcd9f10c763ccdb195f4d5f132e", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Arkansas/ar2020.pl.zip" - }, - "pl94171_az": { - "member": "azgeo2020.pl", - "sha256": "56997e8b1b5175148fd3e1157ab57eee7c1722221cafebfd5d12fe2e687b2aff", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Arizona/az2020.pl.zip" - }, - "pl94171_ca": { - "member": "cageo2020.pl", - "sha256": "e02c83c85a9a5d69ded4bf0d4c8ebe3f4bcd2d8e5911db765e9e42915ad1f753", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/California/ca2020.pl.zip" - }, - "pl94171_co": { - "member": "cogeo2020.pl", - "sha256": "fce76008aedd10681bf0e57fa366ca788d280264edfe91e7d80a9b33ae86ab2c", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Colorado/co2020.pl.zip" - }, - "pl94171_ct": { - "member": "ctgeo2020.pl", - "sha256": "c4a2d42a79c52de10ea9397cca43db59c2df38e6d6e66b4396a0de8bd3f66780", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Connecticut/ct2020.pl.zip" - }, - "pl94171_dc": { - "member": "dcgeo2020.pl", - "sha256": "21b5189f45ce260c4607c9579f4ad52d11920133af827b14b183121b48d40ce4", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/District_of_Columbia/dc2020.pl.zip" - }, - "pl94171_de": { - "member": "degeo2020.pl", - "sha256": "2d7743cc442574dc785c728e9637c5d4648bef518beef41b5f03756eb56ea78a", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Delaware/de2020.pl.zip" - }, - "pl94171_fl": { - "member": "flgeo2020.pl", - "sha256": "fdcc98180f634b69c40ed1d71a63fe00fdda4cda3e35fbaa6382336501f4b0cf", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Florida/fl2020.pl.zip" - }, - "pl94171_ga": { - "member": "gageo2020.pl", - "sha256": "26434588ceab435fa33a138ff999e4187c923a74732fcc488c3a0597b4adebcb", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Georgia/ga2020.pl.zip" - }, - "pl94171_hi": { - "member": "higeo2020.pl", - "sha256": "b7e1ba6350c5d75267b688504052af001ad227763995f6bb26ccdd92fc45261f", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Hawaii/hi2020.pl.zip" - }, - "pl94171_ia": { - "member": "iageo2020.pl", - "sha256": "c55e8276080b684431b8629269d4225c31080ea17257a737f9d99e3cf2ac6822", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Iowa/ia2020.pl.zip" - }, - "pl94171_id": { - "member": "idgeo2020.pl", - "sha256": "89345b6d5d12ccdec8af4039b0a6f1f5ba4cd7ca86c8f73ad6250b5713e3df47", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Idaho/id2020.pl.zip" - }, - "pl94171_il": { - "member": "ilgeo2020.pl", - "sha256": "e73f82d6aea0b9b372e9f8572c6edbedfa72ae9fc2039da84faf0cdf0c478a32", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Illinois/il2020.pl.zip" - }, - "pl94171_in": { - "member": "ingeo2020.pl", - "sha256": "5f4999237a9bf3b3337865494d0d62c0a1dc620cad7e48caebaa83811f95901a", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Indiana/in2020.pl.zip" - }, - "pl94171_ks": { - "member": "ksgeo2020.pl", - "sha256": "ac6b4ae5e2af4c8d639b46748d55927ee7821756f6073cc4f8238c1fb00498ee", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Kansas/ks2020.pl.zip" - }, - "pl94171_ky": { - "member": "kygeo2020.pl", - "sha256": "252a04b760e4da69665d5654108be2b63d55d294c1c0c608bfc7e7fcf2360ac4", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Kentucky/ky2020.pl.zip" - }, - "pl94171_la": { - "member": "lageo2020.pl", - "sha256": "d8d9ea96195f2b848be70ef33dcb224d433d8d58025e11aaa0432c5211e5345c", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Louisiana/la2020.pl.zip" - }, - "pl94171_ma": { - "member": "mageo2020.pl", - "sha256": "9857362fbc1162d3943685134a77a91542a70eb667f3477559d07fd0a81e4889", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Massachusetts/ma2020.pl.zip" - }, - "pl94171_md": { - "member": "mdgeo2020.pl", - "sha256": "9af1e1f0185b6219a9432132ef78e47c2d74c474948d2ed6004c7061412393f3", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Maryland/md2020.pl.zip" - }, - "pl94171_me": { - "member": "megeo2020.pl", - "sha256": "f9bb6fe4cf13bb28b44e4d6a8d5d3e5209f9249ebc402db7ea5c24f8a673ac07", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Maine/me2020.pl.zip" - }, - "pl94171_mi": { - "member": "migeo2020.pl", - "sha256": "971bd53abeb1d905bb9b09bfe4dc1afe8514a916f24d285b289e0f66ec5cfb62", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Michigan/mi2020.pl.zip" - }, - "pl94171_mn": { - "member": "mngeo2020.pl", - "sha256": "7d30974597e73893d25ffef0975c8e673afcc1166f9919d5ff2a466a2e0e188f", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Minnesota/mn2020.pl.zip" - }, - "pl94171_mo": { - "member": "mogeo2020.pl", - "sha256": "900d8a3bcc59e2cc3c174730e2a8704bd653af1fc043c9fbd73196e1c0d7fba5", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Missouri/mo2020.pl.zip" - }, - "pl94171_ms": { - "member": "msgeo2020.pl", - "sha256": "cb64e3465cee9623dc574605f4dfcada33eaea6833062d31f769811fd20a5f26", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Mississippi/ms2020.pl.zip" - }, - "pl94171_mt": { - "member": "mtgeo2020.pl", - "sha256": "e01288ce3ae37ae732eb61d40268c6ac3ec8f9772c8609c6e9ca538bdd0bc130", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Montana/mt2020.pl.zip" - }, - "pl94171_nc": { - "member": "ncgeo2020.pl", - "sha256": "04f2f250dad877d237d94f553f9cb2209594c2a021d15f9fdeedd4404db538db", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/North_Carolina/nc2020.pl.zip" - }, - "pl94171_nd": { - "member": "ndgeo2020.pl", - "sha256": "a37d18e05a73b311a8fd7ade36dd5b17cac16ff125318c9ec6d2edd7f6968b71", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/North_Dakota/nd2020.pl.zip" - }, - "pl94171_ne": { - "member": "negeo2020.pl", - "sha256": "29298d68a5905cf9cb14cd64e35750d14727a330c5940ccf5238a8ae6e110300", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Nebraska/ne2020.pl.zip" - }, - "pl94171_nh": { - "member": "nhgeo2020.pl", - "sha256": "31852c0cfa871ea81f933bb1df904f0d45997097e38ade83db87ba58255d4468", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/New_Hampshire/nh2020.pl.zip" - }, - "pl94171_nj": { - "member": "njgeo2020.pl", - "sha256": "caf0da4a9e9d46ced3f8020a7a2bf5240cd8805ed0679716f0cb2b8fcc65e811", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/New_Jersey/nj2020.pl.zip" - }, - "pl94171_nm": { - "member": "nmgeo2020.pl", - "sha256": "77133ba4bb2fb2c28d39993bb4cff5f1c42823c9d13b75e51e8ecf986354c259", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/New_Mexico/nm2020.pl.zip" - }, - "pl94171_nv": { - "member": "nvgeo2020.pl", - "sha256": "ac7e792e0a1d6248ff1700159f4ec6a3a7b3330ebe10899bb4f47ab97c19952d", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Nevada/nv2020.pl.zip" - }, - "pl94171_ny": { - "member": "nygeo2020.pl", - "sha256": "9d046014bee6b61afa808c5dfe841a6d036c77bff70197472fbdd291a40b8ce2", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/New_York/ny2020.pl.zip" - }, - "pl94171_oh": { - "member": "ohgeo2020.pl", - "sha256": "ceef338247ad2dccc267885f2879e167ab6730c7a332d3ceb5aef9d6e2338aa0", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Ohio/oh2020.pl.zip" - }, - "pl94171_ok": { - "member": "okgeo2020.pl", - "sha256": "e95c99e6385f4ed3a5ea67962444a53ad7a731f26d1dd88df2efde504f101dff", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Oklahoma/ok2020.pl.zip" - }, - "pl94171_or": { - "member": "orgeo2020.pl", - "sha256": "f8e7fa8c331446850b1bf545e6c4aa63733373f582b11edd6b3cf31016bc4509", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Oregon/or2020.pl.zip" - }, - "pl94171_pa": { - "member": "pageo2020.pl", - "sha256": "2d33a7dab29c8dd5692bbde203d253e06eebbc44fcbaa96b1caa958d454026ae", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Pennsylvania/pa2020.pl.zip" - }, - "pl94171_ri": { - "member": "rigeo2020.pl", - "sha256": "a2a6de20d0bac0dfe91aef66d3c2dde0eb768863884fbc45d4f3df5c49f5dcd0", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Rhode_Island/ri2020.pl.zip" - }, - "pl94171_sc": { - "member": "scgeo2020.pl", - "sha256": "e8996f6873405d6c048c42e8878231fc7f152564295498ff586a041648a46bb5", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/South_Carolina/sc2020.pl.zip" - }, - "pl94171_sd": { - "member": "sdgeo2020.pl", - "sha256": "30d4edc3ccc86ad76a2a388c763ecdaebe23f43a5dcb92658587c04ca5cfa1a5", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/South_Dakota/sd2020.pl.zip" - }, - "pl94171_tn": { - "member": "tngeo2020.pl", - "sha256": "89f843dbc717409169fef44df2682548c308c06c0d825451bf2e78340234daf2", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Tennessee/tn2020.pl.zip" - }, - "pl94171_tx": { - "member": "txgeo2020.pl", - "sha256": "6fee8ca5759eb4942db25a59b8cbd3439ad4188279fccff87dac70a89ceca6ff", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Texas/tx2020.pl.zip" - }, - "pl94171_ut": { - "member": "utgeo2020.pl", - "sha256": "4e6de13cd7b5307bfdd327446dd7107e2fdb790903f59186bb8285881531d0f0", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Utah/ut2020.pl.zip" - }, - "pl94171_va": { - "member": "vageo2020.pl", - "sha256": "e33c7c1ac0aa84b5fe5ba20188d2e562c5c96b1c02f59e766e18d037b23df9e8", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Virginia/va2020.pl.zip" - }, - "pl94171_vt": { - "member": "vtgeo2020.pl", - "sha256": "6a18a3364dff5e0432b25f4b4972eaf22ac0699ea0273430509a9b6a763638e9", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Vermont/vt2020.pl.zip" - }, - "pl94171_wa": { - "member": "wageo2020.pl", - "sha256": "dde77f2db632a4af274a968c39a7ab58fac6cfd90718e4a72ec239b84980db5b", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Washington/wa2020.pl.zip" - }, - "pl94171_wi": { - "member": "wigeo2020.pl", - "sha256": "e70f80ef4a803cdeb1e1767fc42e13f7d01ce9f98566c82ca4c2379aa9809f7f", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Wisconsin/wi2020.pl.zip" - }, - "pl94171_wv": { - "member": "wvgeo2020.pl", - "sha256": "8356c49f300e9c18658de56c06e551f5e8cb591c395fc3d1748077d46322889b", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/West_Virginia/wv2020.pl.zip" - }, - "pl94171_wy": { - "member": "wygeo2020.pl", - "sha256": "98dd7d035942fea2758e0fcabe71efa0588a7ec7c9c33110bcde493fe8e8b942", - "url": "https://www2.census.gov/programs-surveys/decennial/2020/data/01-Redistricting_File--PL_94-171/Wyoming/wy2020.pl.zip" + "cd_plan_registry": { + "artifact": "us_cd_plan_registry_2020.npz", + "plans": [ + "117th_congress", + "119th_congress" + ], + "receipt": "microcosm.build.us_runtime/us_cd_plan_registry.provenance.json", + "sha256": "6b16fc2141060be312eae6ac5e5bd6b2985e45816bcfcab8024f7488be75168f" } }, "states": [ diff --git a/packages/microcosm-build/tests/engine/us/test_us_multispine_pool_tool.py b/packages/microcosm-build/tests/engine/us/test_us_multispine_pool_tool.py index da909c4b40..b30b5e0d85 100644 --- a/packages/microcosm-build/tests/engine/us/test_us_multispine_pool_tool.py +++ b/packages/microcosm-build/tests/engine/us/test_us_multispine_pool_tool.py @@ -338,7 +338,7 @@ def capture_equality(expected: object, actual: object) -> None: "country": "us", "schema_id": "country_spec", "schema_version": 1, - "spec_sha256": "1cf1f44d9a11458f59ea7d75dce68303816cab3e66668a6c6f6ecfe62d56f8dc", + "spec_sha256": "ffbb93ed5ae22a95536ae475d6912a42628568609b442360dc1cd3bcf26772cd", }, } diff --git a/packages/microcosm-build/tests/engine_free/us/test_us_bundle_core_contracts.py b/packages/microcosm-build/tests/engine_free/us/test_us_bundle_core_contracts.py index 6cb6bc781e..a780c4814b 100644 --- a/packages/microcosm-build/tests/engine_free/us/test_us_bundle_core_contracts.py +++ b/packages/microcosm-build/tests/engine_free/us/test_us_bundle_core_contracts.py @@ -81,8 +81,8 @@ def test_cd_vintage_crosswalk_source_and_geography_authority_are_pinned() -> Non assert crosswalk_source == { "id": "us_congressional_district_vintage_crosswalk_117_to_119", "role": "congressional_district_vintage_crosswalk", - "sha256": ("c7cb040b1f57ca2ea2adcbfe60cc2b250ca23acbc4b640cd421e766fa54c1aec"), - "byte_size": 77_935, + "sha256": ("a347303fff3fea145f758488c43cb94355df5a8cb5e512552b2ee3791d2aa233"), + "byte_size": 78_202, "loader": "kernel:load_congressional_district_vintage_crosswalk", "vintages": ["vintage:cd_117", "vintage:cd_119"], "vintage_authorities": [ diff --git a/packages/microcosm-build/tests/engine_free/us/test_us_congressional_district_vintage_crosswalk.py b/packages/microcosm-build/tests/engine_free/us/test_us_congressional_district_vintage_crosswalk.py index f91fe793da..4570f6a0d9 100644 --- a/packages/microcosm-build/tests/engine_free/us/test_us_congressional_district_vintage_crosswalk.py +++ b/packages/microcosm-build/tests/engine_free/us/test_us_congressional_district_vintage_crosswalk.py @@ -474,3 +474,158 @@ def test_packaged_validator_requires_population_provenance_and_full_coverage() - undersized = without_population.assign(pair_population=[600.0]) with pytest.raises(ValueError, match="exactly 436 source districts"): validate_packaged_congressional_district_vintage_crosswalk(undersized) + + +# --- the packaged crosswalk is derived from the CD plan registry ------------- + +#: The General Assembly's official 2020-census populations of the 2019 North +#: Carolina congressional plan, the 117th Congress's districts (NCGA, 2020 +#: deviation report, https://www.ncleg.gov/Files/GIS/Maps_Reports/ +#: Decennial_ReCalc/2020/DeviationReports/2020_Deviation_Rpt_Cong.xlsx). +NCGA_2020_POPULATION_2019_PLAN = { + "01": 687_507, + "02": 912_226, + "03": 745_810, + "04": 877_475, + "05": 747_588, + "06": 807_200, + "07": 823_916, + "08": 821_133, + "09": 786_276, + "10": 776_331, + "11": 782_246, + "12": 899_859, + "13": 771_821, +} + + +def _packaged_provenance() -> dict: + import json + + path = default_congressional_district_vintage_crosswalk_path() + return json.loads( + path.with_name(path.name + ".provenance.json").read_text(encoding="utf-8") + ) + + +def test_packaged_crosswalk_is_built_from_the_pinned_registry() -> None: + from microcosm.build.us_runtime.cd_plan_registry import ( + packaged_us_cd_plan_registry_provenance, + ) + + provenance = _packaged_provenance() + registry = packaged_us_cd_plan_registry_provenance() + assert ( + provenance["sources"]["cd_plan_registry"]["sha256"] + == (registry["output_sha256"]) + ) + # The crosswalk carries the registry's 117th deviations (North Carolina). + assert ( + provenance["known_deviations"] + == (registry["plan_sources"]["117th_congress"]["known_deviations"]) + ) + assert set(provenance["known_deviations"]) == {"37"} + + +def test_packaged_crosswalk_uses_north_carolinas_2019_plan_for_the_117th() -> None: + # North Carolina's 117th districts are its 2019 plan. Their 2020 + # populations in the crosswalk must match the General Assembly's official + # figures within 0.5% (the registry's build tolerance). The earlier + # crosswalk, which read NC's 2016 plan from the 2020 BAF layer, missed + # them by up to 7.55% (district 1), with 10 of 13 districts beyond 0.5%. + frame = load_default_congressional_district_vintage_crosswalk() + source = frame["source_geography_id"].astype(str) + north_carolina = frame[source.str.startswith("5001700US37")] + totals = ( + north_carolina.groupby(north_carolina["source_geography_id"].str[-2:])[ + "pair_population" + ] + .sum() + .to_dict() + ) + assert sorted(totals) == sorted(NCGA_2020_POPULATION_2019_PLAN) + for district, official in NCGA_2020_POPULATION_2019_PLAN.items(): + assert abs(totals[district] - official) <= 0.005 * official, district + + +def test_packaged_crosswalk_conserves_national_and_state_population() -> None: + # Runs without the registry NPZ: the packaged CSV alone must carry every + # 2020 resident exactly once, never pair districts across state lines, and + # agree with its build receipt and the registry's national total. + from microcosm.build.us_runtime.cd_plan_registry import ( + packaged_us_cd_plan_registry_provenance, + ) + + frame = load_default_congressional_district_vintage_crosswalk() + source_state = frame["source_geography_id"].astype(str).str[9:11] + target_state = frame["target_geography_id"].astype(str).str[9:11] + assert (source_state == target_state).all() + + by_state = { + state: int(total) + for state, total in frame.groupby(source_state)["pair_population"].sum().items() + } + conservation = _packaged_provenance()["diagnostics"]["state_conservation"] + assert by_state == { + state: int(row["state_population"]) for state, row in conservation.items() + } + assert all(int(row["unmatched_population"]) == 0 for row in conservation.values()) + assert len(by_state) == 51 + national = sum(by_state.values()) + assert national == packaged_us_cd_plan_registry_provenance()["population"] + assert national == 331_449_281 + # 2020 Census resident populations (P.L. 94-171) for a few states. + for state, population in { + "01": 5_024_279, + "02": 733_391, + "06": 39_538_223, + "36": 20_201_249, + "48": 29_145_505, + }.items(): + assert by_state[state] == population, state + + +@pytest.mark.skipif( + not __import__("os").environ.get("MICROCOSM_US_CD_PLAN_REGISTRY"), + reason="set MICROCOSM_US_CD_PLAN_REGISTRY to a built registry to rebuild", +) +def test_packaged_crosswalk_rebuilds_exactly_from_the_registry() -> None: + import os + + from microcosm.build.us_runtime.cd_plan_registry import ( + load_pinned_us_cd_plan_registry, + ) + + registry = load_pinned_us_cd_plan_registry( + os.environ["MICROCOSM_US_CD_PLAN_REGISTRY"] + ) + blocks = registry.block_geoid.tolist() + rows, _ = build_cd_vintage_crosswalk_rows( + old_cd_by_block={ + b: f"{d % 100:02d}" + for b, d in zip( + blocks, registry.plans["117th_congress"].tolist(), strict=True + ) + }, + current_cd_by_block={ + b: f"{d % 100:02d}" + for b, d in zip( + blocks, registry.plans["119th_congress"].tolist(), strict=True + ) + }, + block_population=dict(zip(blocks, registry.population.tolist(), strict=True)), + ) + packaged = load_default_congressional_district_vintage_crosswalk() + rebuilt = { + (row["source_geography_id"], row["target_geography_id"]): row["pair_population"] + for row in rows + } + assert rebuilt == { + (s, t): int(p) + for s, t, p in zip( + packaged["source_geography_id"], + packaged["target_geography_id"], + packaged["pair_population"], + strict=True, + ) + } diff --git a/packages/microcosm-build/tests/engine_free/us/test_us_multispine_pool_h5_io.py b/packages/microcosm-build/tests/engine_free/us/test_us_multispine_pool_h5_io.py index 900ee24d41..b329171528 100644 --- a/packages/microcosm-build/tests/engine_free/us/test_us_multispine_pool_h5_io.py +++ b/packages/microcosm-build/tests/engine_free/us/test_us_multispine_pool_h5_io.py @@ -557,7 +557,7 @@ def _fixture_geography_assignment( "source:us_congressional_district_vintage_crosswalk_117_to_119" ), "sha256": ( - "c7cb040b1f57ca2ea2adcbfe60cc2b250ca23acbc4b640cd421e766fa54c1aec" + "a347303fff3fea145f758488c43cb94355df5a8cb5e512552b2ee3791d2aa233" ), "source_vintage_ref": "vintage:cd_117", "source_vintage": "117th_congress", diff --git a/test_support/microcosm_build/us_multispine_pool_tool.py b/test_support/microcosm_build/us_multispine_pool_tool.py index 42f6767965..4117d5021a 100644 --- a/test_support/microcosm_build/us_multispine_pool_tool.py +++ b/test_support/microcosm_build/us_multispine_pool_tool.py @@ -1022,7 +1022,7 @@ def _stacked_main_argv( "--congressional-district-vintage-crosswalk", str(tmp_path / "congressional-district-vintage-crosswalk"), "--congressional-district-vintage-crosswalk-sha256", - "c7cb040b1f57ca2ea2adcbfe60cc2b250ca23acbc4b640cd421e766fa54c1aec", + "a347303fff3fea145f758488c43cb94355df5a8cb5e512552b2ee3791d2aa233", ] ) arguments.extend( diff --git a/tools/build_us_congressional_district_vintage_crosswalk.py b/tools/build_us_congressional_district_vintage_crosswalk.py index 982ff38d54..1442184283 100644 --- a/tools/build_us_congressional_district_vintage_crosswalk.py +++ b/tools/build_us_congressional_district_vintage_crosswalk.py @@ -1,13 +1,21 @@ -"""Build the 117th->119th CD vintage crosswalk artifact from Census sources. +"""Build the 117th->119th CD vintage crosswalk artifact from the CD plan registry. -Downloads (with a local cache) the 119th Congressional District block -equivalency file, the 2020 Block Assignment File ``CD`` layer for each state, -and the 2020 P.L. 94-171 geographic headers (block populations); joins them at -2020-tabulation-block grain weighted by block population; and writes the +Reads the pinned US block -> congressional-district plan registry (every +populated 2020 block's district under each plan, with its 2020 P.L. 94-171 +population), overlays its ``117th_congress`` and ``119th_congress`` plans on +the same blocks weighted by block population, and writes the ``source_geography_id,target_geography_id,weight`` crosswalk that ``congressional_district_vintage.translate_congressional_district_facts_to_current_vintage`` -consumes, plus a sidecar provenance JSON recording every source file's SHA-256, -the build metadata, and per-state population conservation. +consumes, plus a sidecar provenance JSON recording the registry's SHA-256, its +117th-plan known deviations, the build metadata, and per-state population +conservation. + +The registry carries the 117th plan correctly for North Carolina, whose 117th +districts (the 2019 remedial plan) differ from the 116th plan in the 2020 +Block Assignment Files; an earlier version of this tool read the BAF layer +directly and so mapped North Carolina's SOI districts through the wrong plan. +The registry's sources and checks are documented in +``packages/microcosm-build/src/microcosm/build/us_runtime/US_CD_PLAN_REGISTRY.md``. The method and rationale live in ``microcosm.build.us_runtime.congressional_district_vintage_crosswalk``. The @@ -18,13 +26,14 @@ Example: uv run --python 3.13 --package microcosm-build --group dev python \ tools/build_us_congressional_district_vintage_crosswalk.py \ + --cd-plan-registry build/us/us_cd_plan_registry_2020.npz \ --out packages/microcosm-build/src/microcosm/build/us_runtime/data/\ -congressional_district_vintage_crosswalk.csv \ - --cache-dir ~/.cache/populace-us-geography +congressional_district_vintage_crosswalk.csv # Smoke run over a few states: uv run python tools/build_us_congressional_district_vintage_crosswalk.py \ - --out /tmp/cd_xwalk_smoke.csv --states 08,30,11 + --cd-plan-registry build/us/us_cd_plan_registry_2020.npz \ + --out /tmp/cd_xwalk_smoke.csv --states 08,30,37 """ from __future__ import annotations @@ -32,38 +41,20 @@ import argparse import csv import hashlib -import io import json import sys -import urllib.request -import zipfile from pathlib import Path -from microcosm.build.us_runtime.block_ladder_sources import ( - US_STATES, - parse_pl_geo_blocks, +from microcosm.build.us_runtime.block_ladder_sources import US_STATES +from microcosm.build.us_runtime.cd_plan_registry import ( + US_CD_PLAN_REGISTRY_PROVENANCE_RESOURCE, + load_pinned_us_cd_plan_registry, ) from microcosm.build.us_runtime.congressional_district_vintage import ( CURRENT_CONGRESSIONAL_DISTRICT_VINTAGE, ) from microcosm.build.us_runtime.congressional_district_vintage_crosswalk import ( build_cd_vintage_crosswalk_rows, - parse_baf_cd_layer, - parse_national_cd_bef_districts, -) - -CD119_BEF_URL = ( - "https://www2.census.gov/programs-surveys/decennial/rdo/mapping-files/" - "2025/119-congressional-district-befs/cd119.zip" -) -CD119_NATIONAL_MEMBER = "NationalCD119.txt" -BAF2020_URL_TEMPLATE = ( - "https://www2.census.gov/geo/docs/maps-data/data/baf2020/" - "BlockAssign_ST{fips}_{usps}.zip" -) -PL94171_URL_TEMPLATE = ( - "https://www2.census.gov/programs-surveys/decennial/2020/data/" - "01-Redistricting_File--PL_94-171/{dirname}/{usps_lower}2020.pl.zip" ) SOURCE_CONGRESSIONAL_DISTRICT_VINTAGE = "117th_congress" @@ -75,10 +66,13 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: ) parser.add_argument("--out", required=True, type=Path) parser.add_argument( - "--cache-dir", + "--cd-plan-registry", + required=True, type=Path, - default=Path.home() / ".cache" / "populace-us-geography", - help="Download cache; re-runs reuse verified files.", + help=( + "The block -> CD-plan registry NPZ. It must be the artifact the " + "package pins (us_cd_plan_registry.provenance.json)." + ), ) parser.add_argument( "--states", @@ -108,31 +102,6 @@ def _sha256(path: Path) -> str: return digest.hexdigest() -def _download(url: str, cache_dir: Path) -> Path: - cache_dir.mkdir(parents=True, exist_ok=True) - destination = cache_dir / url.rsplit("/", 1)[-1] - if destination.exists() and destination.stat().st_size > 0: - return destination - _log(f" downloading {url}") - request = urllib.request.Request( - url, headers={"User-Agent": "microcosm-build (cd vintage crosswalk)"} - ) - with urllib.request.urlopen(request) as response: - payload = response.read() - if not payload: - raise RuntimeError(f"Empty download from {url}") - partial = destination.with_suffix(destination.suffix + ".partial") - partial.write_bytes(payload) - partial.replace(destination) - return destination - - -def _zip_member_lines(archive_path: Path, member: str) -> list[str]: - with zipfile.ZipFile(archive_path) as archive: - with archive.open(member) as stream: - return io.TextIOWrapper(stream, encoding="latin-1").readlines() - - def main(argv: list[str] | None = None) -> None: args = _parse_args(argv) selected = ( @@ -143,64 +112,45 @@ def main(argv: list[str] | None = None) -> None: unknown = selected - {entry[0] for entry in US_STATES} if unknown: raise SystemExit(f"Unknown state FIPS in --states: {sorted(unknown)}") + selected_fips = {int(fips) for fips, _, _ in states} _log(f"Building CD117->CD119 crosswalk for {len(states)} state(s)") - source_files: dict[str, dict[str, str]] = {} - - cd_zip = _download(CD119_BEF_URL, args.cache_dir) - source_files["cd119_bef"] = { - "url": CD119_BEF_URL, - "member": CD119_NATIONAL_MEMBER, - "sha256": _sha256(cd_zip), - } - _log(" parsing national CD119 BEF (current vintage)") - current_cd_by_block = parse_national_cd_bef_districts( - _zip_member_lines(cd_zip, CD119_NATIONAL_MEMBER) - ) - selected_fips = {fips for fips, _, _ in states} - if selected is not None: - current_cd_by_block = { - block: district - for block, district in current_cd_by_block.items() - if f"{block:015d}"[:2] in selected_fips - } - - old_cd_by_block: dict[int, str] = {} - block_population: dict[int, int] = {} - for fips, usps, dirname in states: - _log(f" state {fips} {usps}") - baf_zip = _download( - BAF2020_URL_TEMPLATE.format(fips=fips, usps=usps), args.cache_dir - ) - source_files[f"baf2020_cd_{usps.lower()}"] = { - "url": BAF2020_URL_TEMPLATE.format(fips=fips, usps=usps), - "member": f"BlockAssign_ST{fips}_{usps}_CD.txt", - "sha256": _sha256(baf_zip), - } - old_cd_by_block.update( - parse_baf_cd_layer( - _zip_member_lines(baf_zip, f"BlockAssign_ST{fips}_{usps}_CD.txt"), - label=f"BlockAssign_ST{fips}_{usps}_CD.txt", - ) + registry = load_pinned_us_cd_plan_registry(args.cd_plan_registry) + for plan in ( + SOURCE_CONGRESSIONAL_DISTRICT_VINTAGE, + CURRENT_CONGRESSIONAL_DISTRICT_VINTAGE, + ): + if plan not in registry.plans: + raise SystemExit(f"The CD plan registry has no {plan!r} plan.") + blocks = registry.block_geoid.tolist() + in_scope = [block // 10**13 in selected_fips for block in blocks] + old_cd_by_block = { + block: f"{district % 100:02d}" + for block, district, keep in zip( + blocks, + registry.plans[SOURCE_CONGRESSIONAL_DISTRICT_VINTAGE].tolist(), + in_scope, + strict=True, ) - - pl_zip = _download( - PL94171_URL_TEMPLATE.format(dirname=dirname, usps_lower=usps.lower()), - args.cache_dir, + if keep + } + current_cd_by_block = { + block: f"{district % 100:02d}" + for block, district, keep in zip( + blocks, + registry.plans[CURRENT_CONGRESSIONAL_DISTRICT_VINTAGE].tolist(), + in_scope, + strict=True, ) - source_files[f"pl94171_{usps.lower()}"] = { - "url": PL94171_URL_TEMPLATE.format( - dirname=dirname, usps_lower=usps.lower() - ), - "member": f"{usps.lower()}geo2020.pl", - "sha256": _sha256(pl_zip), - } - block_population.update( - parse_pl_geo_blocks( - _zip_member_lines(pl_zip, f"{usps.lower()}geo2020.pl"), - state_fips=fips, - ) + if keep + } + block_population = { + block: population + for block, population, keep in zip( + blocks, registry.population.tolist(), in_scope, strict=True ) + if keep + } rows, diagnostics = build_cd_vintage_crosswalk_rows( old_cd_by_block=old_cd_by_block, @@ -225,21 +175,38 @@ def main(argv: list[str] | None = None) -> None: writer.writerow(row) crosswalk_sha256 = _sha256(args.out) + source_spec = registry.plan_sources[SOURCE_CONGRESSIONAL_DISTRICT_VINTAGE] provenance = { "schema_version": 1, "kind": "us_congressional_district_vintage_crosswalk", "source_geography_vintage": SOURCE_CONGRESSIONAL_DISTRICT_VINTAGE, "target_geography_vintage": CURRENT_CONGRESSIONAL_DISTRICT_VINTAGE, - "block_vintage": "2020_tabulation_blocks", + "block_vintage": registry.metadata["block_vintage"], "crosswalk_sha256": crosswalk_sha256, "method": ( - "Population-weighted overlay of the old (117th, via the 2020 BAF CD " - "layer) and current (119th BEF) congressional-district assignments on " - "2020 tabulation blocks, weighted by 2020 P.L. 94-171 block " - "populations. Each old district's population is redistributed across " - "current districts; totals conserve by construction." + "Population-weighted overlay of the 117th and 119th plans of the US " + "block -> congressional-district plan registry on 2020 tabulation " + "blocks, weighted by 2020 P.L. 94-171 block populations. Each old " + "district's population is redistributed across current districts; " + "totals conserve by construction. The registry's 117th plan is the " + "2020 BAF CD layer except North Carolina, carried from its 2019 plan " + "(see known_deviations)." ), - "sources": source_files, + "sources": { + "cd_plan_registry": { + "artifact": args.cd_plan_registry.name, + "sha256": registry.sha256, + "receipt": ( + "microcosm.build.us_runtime/" + f"{US_CD_PLAN_REGISTRY_PROVENANCE_RESOURCE}" + ), + "plans": [ + SOURCE_CONGRESSIONAL_DISTRICT_VINTAGE, + CURRENT_CONGRESSIONAL_DISTRICT_VINTAGE, + ], + } + }, + "known_deviations": source_spec.get("known_deviations", {}), "diagnostics": diagnostics, "states": [fips for fips, _, _ in states], } @@ -263,9 +230,9 @@ def main(argv: list[str] | None = None) -> None: def _validate_conservation(diagnostics: dict[str, object]) -> None: """Fail loudly if any state loses population beyond a small block-coverage gap. - Populated blocks the BAF/BEF do not cover are a source-coverage gap, not a - silent redistribution. A published crosswalk should cover essentially every - populated block; a large gap means a source or field-layout drift. + The registry assigns every populated block a district under every plan, + so any uncovered population means the registry and this tool disagree on + the block universe: a defect, not a silent redistribution. """ state_conservation = diagnostics.get("state_conservation") diff --git a/tools/build_us_multispine_pool.py b/tools/build_us_multispine_pool.py index 828cceffdc..ebcb61067b 100644 --- a/tools/build_us_multispine_pool.py +++ b/tools/build_us_multispine_pool.py @@ -365,7 +365,7 @@ "source:us_congressional_district_vintage_crosswalk_117_to_119" ) _STACKED_CD_CROSSWALK_SHA256 = ( - "c7cb040b1f57ca2ea2adcbfe60cc2b250ca23acbc4b640cd421e766fa54c1aec" + "a347303fff3fea145f758488c43cb94355df5a8cb5e512552b2ee3791d2aa233" ) _STACKED_CD_CROSSWALK_SOURCE_VINTAGE_REF = "vintage:cd_117" _STACKED_CD_CROSSWALK_SOURCE_VINTAGE = "117th_congress"