Skip to content

IPF drops 2.35 M people: LAUs on the NUTS3 fallback get zero broad-age shares, so A_i collapses to 0 #20

Description

@MaartenHilferink

EU 2011, 100 m, GeoDms 20.19.1.m on main @ 735cad7.

/Analysis is handed 445 356 960 persons and writes 443 008 640. 2 348 320 people are in the input map and in no output map. It is not rounding and it is not spread thinly: it is fully accounted for by grid cells whose balancing factor A_i ends at exactly 0.

measured over the EU run value
sum(Checks/P_r), the ARDECO input 445 356 960
sum(Result/X_asr/Total), the output 443 008 640
difference 2 348 320
sum(P_i) over the cells where Result/A_i == 0 2 348 314
LAUs with Checks/Qr_0_and_Pr_gt0 930
of those, LAUs where Share_LT15_LAU + Share_15_64_LAU + Share_GE65_LAU == 0 928

The 6-person gap between rows 3 and 4 is float32 rounding over 97 428 sums; per country the two agree to within a person (FR -2 318 976 vs -2 318 974, DE -26 632 vs -26 635, EL -1 980 vs -1 980, LT -667 vs -667, everything else nets to zero).

Mechanism

Checks/Share_<broad>_LAU is the per-LAU age structure that E_ib falls back on for 1 km cells where ESTAT has no age data:

attribute<Share> Share_LT15_LAU (r) := (sum(<raw census pop, LT15>, LAU_rel) / Q_asr/Total)[Share];

The numerator is the raw census from Population/AGEGROUP5_SEX_perLAU; the denominator is Q_asr/Total, which already carries the NUTS3 fallback of #12. For a LAU with no census the numerator is 0 while the denominator is P_r > 0, so all three broad shares come out 0. That is the case for 928 of the 930 LAUs on the fallback.

E_ib then hands 0 to every cell in those LAUs where ESTAT_Age_0_and_with_ARDECO_pop holds (158 728 cells EU-wide), and:

A_i_raw = P_i / sum_as(E_is * E_ib * B_asr)   ->  division by zero
A_i     = MakeDefined(A_i_raw, 0f)            ->  0
X_asi   = E_is * E_ib * A_i * B_asr           ->  0

so the cell's population disappears. The NUTS3 fallback repairs the target Q_asr but not the shares E_ib uses, and the two disagree in exactly the LAUs where the fallback was needed.

Where it lands

47 LAUs lose more than 100 persons, and 43 of them have POP_2021 = 0 in the LAU gpkg:

GISCO_ID lost input P_r POP_2021 LAU_NAME
FR_75056 2 318 836 2 318 836 2 187 526 Paris (#19)
DE_03153504 2 111 3 757 0 Harz (Landkreis Goslar), gemfr. Gebiet
DE_09188451 1 800 3 532 0 Starnberger See
EL_99010000 1 798 1 798 1 811 Ψευδοκοινοτητα Αγιο Ορος (Αυτοδιοίκητο)
DE_09184452 1 532 2 535 0 Forstenrieder Park
DE_09184454 1 442 2 231 0 Grünwalder Forst
DE_03159501 1 205 3 610 0 Harz (Landkreis Göttingen), gemfr. Geb.
DE_01053105 1 072 2 066 0 Sachsenwald (Forstgutsbez.),gemfr.Geb.

The German entries are almost all gemeindefreie Gebiete - forests and lakes such as Starnberger See, Chiemsee, Ammersee and Perlacher Forst - that genuinely have no inhabitants. There the census is right and it is the ARDECO 100 m disaggregation that puts some 27 000 people in them. Dropping those is defensible; doing it silently is not, and the same code path is what loses Paris.

Suggestion

  • Derive Share_<broad>_LAU from Q_asr rather than from the raw census, so a LAU on the NUTS3 fallback gets the fallback's age structure instead of zeros.
  • Add A_i == 0 && P_i > 0 next to Checks/Qr_0_and_Pr_gt0, so a run can state how many people it dropped.
  • Decide separately what should happen to ARDECO population in LAUs the census says are empty: reallocate over the neighbours, or accept the loss explicitly.

Reproducing

Measured with a Diag container appended to a scratch copy of the config, which writes per_country.csv, lau_lost.csv and unmatched_census_ids.csv. The two items that carry the finding:

attribute<Person> P_in_A0_cells := sum(/Analysis/Result/A_i == 0f ? /Analysis/P_i : 0f[Person], /Analysis/i/r_rel);
attribute<Share>  share_sum_LAU := /Analysis/Checks/Share_LT15_LAU + /Analysis/Checks/Share_15_64_LAU + /Analysis/Checks/Share_GE65_LAU;

Happy to add the whole container to the repo as cfg/main/Diagnostics.dms if that is wanted.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions