Estimates the nitrogen the human population returns to agricultural land
through municipal solid waste, sewage sludge and human excreta, per WHEP
0.5-degree grid cell. Each polycell's population is converted to a
nitrogen load via a per-capita rate interpolated from a national
historical benchmark series, the Spanish series taken as reference
(human_n_reference; see Details), then spilled from cells with no local
cropland room to same-polity neighbouring cells with spare capacity via
allocate_manure_transport(), the same buffering used by the manure
engine.
The population and the rate are chosen together by population_basis, so
a per-urban-inhabitant rate is never applied to a total population:
"total"(default): UN WPP total population downscaled by HYDE's total-population pattern (build_total_population_grid()) times human_kgn_cap_total_reference, kg N per inhabitant."urban": HYDE's urban population count (read_hyde_population(variable = "urban")) times human_kgn_cap_reference, kg N per urban inhabitant.
Both rates are the calibration nitrogen divided by the calibration population on the same basis, so either regenerates its calibration total. Elsewhere they differ by how far a population's urban share departs from the calibration one: in 2010 the global WPP total population is 2.0 times HYDE's global urban count.
build_urban_n() is the deprecated former name of this function. It
forwards every argument to build_human_n() and warns (class
whep_build_urban_n_deprecated, also lifecycle_warning_deprecated); it
will be removed in a future release. The output columns were renamed with
it: urban_n_t is now human_n_t, and method_urban,
method_urban_population and method_urban_kgn_cap are now
method_human, method_human_population and method_human_kgn_cap.
Arguments
- years
Optional integer vector of calendar years to keep.
NULLkeeps every year the supplied population covers; it is required when the population is read rather than supplied.- population_basis
Which population, and the per-capita rate on the same basis, generates the load:
"total"(default) or"urban". See Description. Recorded in themethod_human_populationandmethod_human_kgn_capoutput columns.- polity_validity
What to do with a row whose
(area_code, year)resolves to a polity that did not exist in that year (the cell-polity crosswalk has no year dimension, so an early-20th-century cell is labelled with its present-day territory)."keep"(default) keeps every row, which is the historical behaviour, and warns naming the rows, years and area codes involved."flag"keeps them and adds the per-row logicalreporting_polity_out_of_span, marking exactly which rows are stand-ins."drop"removes them. All three warn; only"drop"changes the numbers. Seepolity_coverage_gaps(), which reports the same rows for an already-built table.- data
Optional named list of pre-loaded inputs:
total_population(lon,lat,area_code,year,population, one row per polycell asbuild_total_population_grid()returns it; read underpopulation_basis = "total", falling back to that builder when absent; taken per polycell, not re-split bypolity_frac, and every polycell must exist incell_polity),urban_population(lon,lat,year,urban_pop; read underpopulation_basis = "urban", falling back toread_hyde_population(variable = "urban")when absent),cell_polity(lon,lat,area_code, plus optionalpolity_frac; a missingpolity_fracis treated as 1 for backwards compatibility) andcropland_ha(lon,lat,area_code,year,cropland_ha, required: the gridded cropland area used as the simple room proxy,cropland_ha * 0.170t N/ha, the same EU-Nitrates fixed ceiling used byallocate_manure_to_land()'sfixed_ceiling_kg_hadefault). Supplying only the other basis's population aborts with classwhep_human_n_population_basis_mismatchrather than silently reading a default. Both frames'area_codemust be the numeric WHEP area code, whole-numbered, asbuild_cell_polity()emits it. Anything else – an ISO3 literal, an area name, a fractional value – aborts with classwhep_human_n_area_code_unresolved(alsowhep_urban_area_code_unresolved, its former name), naming the frame that carries it. It is not bridged: the two frames key the same transport partition, so one written in a different vocabulary from the other would silently strand a cell's load on a cell with no room instead of placing it, and an ISO3 resolves to apolity_area_codeaggregation bucket that is not every territory's own code ("SSD"would become 206, Sudan (former)). Map to the code first, viaadd_area_code()or regions_full.- example
If
TRUE, return a small fixture instead of reading data. Defaults toFALSE.- method_residual
What happens to human N that the transport step cannot deliver: the residual on a source cell with no cropland, plus, under the default
method_local_residual = "room_cap", the part of a cropland source cell's residual that exceeds its own room. On the 2010 global grid, under the default"total"basis, the first is 13,198 cells and 47,365 t N, 0.700% of the 6.77 Mt of human N, and the second adds 24,346 t on 1,424 cells; seemethod_local_residualfor what"nearest"leaves stranded."nearest"(default): run the transport step's own rule again with a growing radius. Each such cell offers its nitrogen to the same-polity cropland cells in its nearest ring (Chebyshev distance in 0.5-degree steps, wrapped at the antimeridian) that still has room, in proportion to that room; a cell's room is its 170 kg N/ha ceiling minus the human N already on it, and an over-subscribed cell is filled only to its room. The radius grows one ring at a time until the nitrogen is placed or the polity has no room left. Conserves mass and keeps the nitrogen as close to the people who produced it as the room allows. No distance cap or transport coefficient is applied."polity": pool it per polity-year and spread it over all of that polity-year's cropland in proportion to cropland room (area), the rulebuild_n_inputs()applies undermethod_unsupported = "reallocate". Conserves mass, but places the nitrogen anywhere in the polity."keep": leave it on its source cell, as before this argument existed, flagged inhuman_n_stranded_t. On a cell with no cropland,build_n_inputs()'smethod_unsupportedthen decides its fate (by default, it aborts); an over-room excess is applied on its own cell's cropland, as"uncapped"would."drop": discard it. Loses the mass, biased towards dense, cropland-free cells.
"nearest"and"polity"never cross a polity, like the transport step itself, so a polity-year with population and no cropland anywhere keeps its nitrogen on the source cell under either, flagged inhuman_n_stranded_t;"nearest"does the same with whatever its polity has no room left for. Whenever any cell is undelivered, the count, the tonnes and the share of human N are reported (a warning, classwhep_human_n_undelivered, when any nitrogen is dropped or left stranded; otherwise a message of the same class), and the per-year figures are attached asattr(x, "human_n_undelivered"). Recorded in themethod_human_residualoutput column.- method_local_residual
What happens to the residual the transport step hands back to a source cell that has cropland. The transport step offers a cell's load to its ring-1 neighbours only, so a dense cell with little cropland gets back most of its own load, on that little cropland.
"room_cap"(default): the cell keeps only what fits in its own room, 170 kg N/ha times its cropland minus the human N the transport step already landed on it – the room the transport step and"nearest"respect everywhere else. The excess is undelivered N, placed bymethod_residuallike the residual of a cell with no cropland. On the 2010 global grid ("total"basis) that is 24,346 t on 1,424 of the 2,180 cropland source cells left with a residual. Under"nearest", 17,083 t of it is moved and 7,263 t stays stranded on its own cell, in the three polities with no room left anywhere (Hong Kong 6,701 t, Kuwait 363 t, the Bahamas 200 t), alongside the 2,145 t on cells with no cropland (Qatar, Iceland, Samoa)."uncapped": the cell keeps its whole residual, as before this argument existed, whatever its cropland area. On the same grid 1,417 cropland cells then end above 170 kg N/ha, holding 24,346 t above it, and the largest load is booked on 3.6e-7 ha.
No minimum-cropland threshold is offered: the room cap already moves a sliver's whole residual, and a threshold would be a new, unsourced number that misses the excess on larger cells (15,202 t of the 24,346 t sits on cells with at least 1 ha). Recorded in the
method_human_local_residualoutput column; the part ofundelivered_tit adds is the summary'sover_room_t.- ...
For
build_urban_n(), arguments passed on tobuild_human_n().population_basisdefaults to"urban"here if omitted, matching this function's historical behaviour, unlikebuild_human_n()'s own"total"default.
Value
A tibble with lon, lat, area_code, year, human_n_t,
human_n_relocated_t (the part of human_n_t placed on the cell by
method_residual), human_n_stranded_t (the undelivered part no rule
could place: on a cell with no cropland, which no downstream cropland
allocation can place, or above the room of the cell's own cropland, which
build_n_inputs() then applies there, over the ceiling),
method_human, method_human_population ("total_population" or
"urban_population"), method_human_kgn_cap
("kg_n_per_total_inhabitant" or "kg_n_per_urban_inhabitant") and
method_human_residual and method_human_local_residual, plus the
polity columns below, plus
reporting_polity_out_of_span when polity_validity = "flag". The
attribute "human_n_undelivered" is a tibble with one row per year:
year, human_n_t, n_cells (undelivered source cells),
undelivered_t, over_room_t (the part of undelivered_t that exceeded
the room of its own cell's cropland), relocated_t, stranded_t,
dropped_t, undelivered_share (of human_n_t),
method_human_residual and method_human_local_residual.
Details
The current per-capita rate is a documented placeholder (one national historical series applied as a global default). For a future refinement, human N should instead be derived from two distinct, more mechanistic streams: (1) sewage/human-excreta N estimated from actual historical per-capita dietary protein/N intake (already reconstructable in WHEP via its FAOSTAT/commodity-balance food-supply data, rather than a fixed external per-capita constant), and (2) food-waste/municipal-solid-waste N from actual historical food-loss and waste estimates. This is out of scope for the current task and is not implemented here.
Polity columns
Every area-keyed output carries the polity its area_code resolves to in
that row's year:
polity_area_code: The numeric key rows are AGGREGATED on, for the matrix workflows. It is a bucket, not an identity: usereporting_polity_codeto say which territory a row belongs to.reporting_polity_code: The polity itself, e.g.ESP-1846-1914. It is year-aware, so the samearea_coderesolves to different polities in different years, which is the point of the crosswalk.reporting_polity_name: Its name. It can differ from the area's own name where the area folds into an aggregate.reporting_polity_has_geometry: Whether the polity has a polygon in the WHEP polity database, for callers that need to map or intersect it.FALSEis a documented gap upstream, not an error.
Rows whose area_code resolves to no polity keep the columns with NA
rather than being dropped, so a gap is visible instead of silent.
Rows before the back-cast anchor year resolve to the polity live in that
anchor year rather than to the polity live in the row's own year, because
WHEP's pre-anchor series are back-cast onto the anchor-year territory. See
add_polity_code() for the reasoning. Where that polity is not live in the
row's own year – 41.5% of the pre-1961 (area, year) cells –
add_polity_code() says so as mapping_status == "backcast_anchor", and
polity_coverage_gaps() reports it as gap_kind == "backcast_anchor".
These columns do not say so either way.
A row whose year no mapped period covers is resolved to the NEAREST period of
the same area instead, so reporting_polity_code can name a polity that did
not exist in that row's year – FAOSTAT bucket 206 "Sudan (former)" keeps
reporting after SUD-1956-2011 ends, and its post-2011 rows carry that code.
These columns do not say so: add_polity_code() reports such a row as
mapping_status == "out_of_span", and that column is dropped here so that
adding it does not change the schema of every area-keyed output at once.
polity_coverage_gaps() reports the stand-in rows of a built table, and
options(whep.polity_mapping_status = "flag") (or "status") carries the
signal on the outputs themselves. Both are opt-in; the default is no extra
column.
Examples
build_human_n(example = TRUE)
#> # A tibble: 1 × 16
#> year area_code polity_area_code reporting_polity_code reporting_polity_name
#> <int> <int> <int> <chr> <chr>
#> 1 2020 203 203 ESP-1800-2025 Spain
#> # ℹ 11 more variables: reporting_polity_has_geometry <lgl>, lon <dbl>,
#> # lat <dbl>, human_n_t <dbl>, human_n_relocated_t <dbl>,
#> # human_n_stranded_t <dbl>, method_human <chr>,
#> # method_human_population <chr>, method_human_kgn_cap <chr>,
#> # method_human_residual <chr>, method_human_local_residual <chr>
