Skip to contents

Combines biological nitrogen fixation (calculate_bnf()), residue/root N recycling (calculate_npp_carbon_nitrogen()), livestock manure (build_livestock_nutrient_flows()), atmospheric deposition (build_n_deposition()), urban/human-excreta N (build_urban_n()), soil organic-matter mineralization (build_carbon_balance()'s son_change_kgn_ha) and synthetic fertiliser (a country total spatialized to crops and cells via spatialize_country_n_to_crops()) into one long-format tibble of nitrogen inputs to agricultural land.

fert_type values: "bnf", "recycling", "manure_solid", "manure_liquid", "excreta", "deposition", "urban", "som_mineralization", "synthetic" and "accum_loss". The last is a documented gap (perennial-crop standing-biomass N accumulation from Spain_Hist's N_balance.R): its source computation was not available for this task, so it is never emitted, only reserved in the vocabulary.

Terms that are fundamentally per-cell or per-land-use rather than per-crop are allocated over the agricultural land support, either supplied as data$ag_land_support or derived by build_ag_land_support() from the gridded inputs already present. Deposition uses both cropland and grassland support. "urban", "som_mineralization", and manure already assigned upstream to Cropland but lacking a crop use only local cropland support, so manure is not reassigned to grassland after the manure engine's capacity allocation. Forest and natural land are outside that support and therefore outside the agricultural balance. Grassland is represented by CBS 3000; no intensive/extensive class is inferred.

Usage

build_n_inputs(
  years = NULL,
  resolution = c("grid", "polity"),
  synthetic_method = NULL,
  polity_validity = c("keep", "flag", "drop"),
  data = list(),
  example = FALSE
)

Arguments

years

Optional integer vector of calendar years to keep. NULL keeps every year the assembled inputs cover.

resolution

"grid" (default, per cell/crop/year/fert_type) or "polity" (summed to area_code/item_cbs_code/year/fert_type).

synthetic_method

Synthetic-N crop allocation method, "coello" or "area_share". When NULL (default), uses data$synthetic_method %||% "coello" for backwards compatibility.

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 logical reporting_polity_out_of_span, marking exactly which rows are stand-ins. "drop" removes them. All three warn; only "drop" changes the numbers. See polity_coverage_gaps(), which reports the same rows for an already-built table.

data

Named list of pre-loaded, caller-supplied upstream inputs. Each of the following is required for its corresponding fert_type to be emitted (a missing one silently skips that source rather than erroring, since callers may only want a subset):

  • bnf_input: calculate_bnf()'s required input tibble (lon, lat, area_code, year, item_prod_code, crop_npp_n_t, product_n_t, weed_npp_n_t, land_use, legumes_seeded, seeded_cover_crop_share, area_ha).

  • npp_n_input: calculate_npp_carbon_nitrogen()'s required input tibble (lon, lat, area_code, year, item_prod_code, item_cbs_code, product_dm_t, residue_dm_t, root_dm_t, optionally residue_soil_dm_t).

  • livestock_intake: build_livestock_nutrient_flows()'s intake argument (the redistribute_feed() realised-intake contract), plus gridded (its land-surface layer) and resolution/methods (forwarded as-is).

  • nhx, noy, cell_polity: build_n_deposition()'s inputs.

  • deposition_scope: which of a polycell's territory the "deposition" term is credited with. "territory" (default) is land plus inland water plus ice: nitrogen deposited on a lake or a glacier still drives indirect N2O and still reaches the eutrophication pathway, so the impact terms have to account for it. "land" is the terrestrial surface alone, for purposes that want it, and it needs a cell_polity carrying land_area_ha, inland_water_ha and ice_area_ha; asking for it without them aborts rather than silently returning the whole territory. Recorded in method_deposition_scope.

  • ag_land_support: agricultural physical land support keyed by lon, lat, area_code, year, item_cbs_code, with land_use ("cropland" or "grassland") and positive area_ha. Optional: when absent it is derived natively by build_ag_land_support() from cell_polity, type_cropland and crop_patterns (plus states or grassland_ha for the grassland side). Supply it to override that derivation with a better land surface. Cropland rows identify crop CBS items; all pasture/rangeland rows use CBS 3000.

  • grassland_source, gridded_pasture, grassland_ha, states: forwarded to build_ag_land_support() when the support is derived. grassland_source selects its grassland argument ("gridded_pasture" default, "luh2", or "none" for cropland-only support).

  • urban_population, cropland_ha, cell_polity: build_urban_n()'s inputs.

  • carbon_balance: build_carbon_balance()'s "grid"-resolution output (lon, lat, area_code, land_use, year, area_ha, son_change_kgn_ha); this driver requires it supplied directly, it is never computed here.

  • primary_prod, fertilizer, crop_patterns, type_cropland, cell_polity: the synthetic-fertiliser assembly (country total from fertilizer, the faostat-fertilizer-nutrients pin, split to crops by the chosen crop-share method, then to cells by crop_patterns/type_cropland).

  • synthetic_method: how the synthetic-N country total is split across crops, "coello" (default; Coello 2025 rate-weighted, FAOSTAT- conserving) or "area_share" (harvested-area shares only).

  • coello_rates: crop-specific synthetic-N rate table shaped like coello_synthetic_n (year, area_code, item_cbs_code, kg_n_ha); defaults to whep::coello_synthetic_n. Used only when synthetic_method = "coello".

  • gridded, resolution, methods: forwarded to build_livestock_nutrient_flows(). resolution is the manure engine's own axis, not this function's: it defaults to "subnational" at resolution = "grid" (cell-level nitrogen needs cell-level manure) and to "national" otherwise. A value supplied here is always honoured.

example

If TRUE, return a small fixture instead of assembling real data. Defaults to FALSE.

Value

A tibble. At resolution = "grid": lon, lat, area_code, item_cbs_code, year, fert_type, n_input_t, method_recycling_n, method_synthetic, method_deposition_scope. At resolution = "polity": area_code, item_cbs_code, year, fert_type, method_recycling_n, method_synthetic, method_deposition_scope, n_input_t (summed over cells). method_recycling_n records which residue basis the "recycling" term used: "residue_soil_returned" when the upstream NPP input supplied residue_soil_dm_t (residue N net of removal for feed/fuel/burning) or "total_residue" when only gross residue N was available; it is NA for every other fert_type. method_synthetic records the synthetic crop-split basis ("coello" or "area_share") on "synthetic" rows and is NA for every other fert_type. method_deposition_scope records which of the polycell's territory the "deposition" term was credited with ("territory" or "land") and is NA for every other fert_type. Both grains also carry the polity columns below, plus reporting_polity_out_of_span when polity_validity = "flag".

Details

polity_validity is forwarded to every builder this function calls that offers it – build_ag_land_support(), build_n_deposition(), build_urban_n() and spatialize_country_n_to_crops() – and then applied to the assembled output, so one choice governs the whole assembly instead of each builder deciding on its own key space (whep#727). Under "drop" the support table loses those rows too, so a non-item input (deposition, urban, SOM mineralization) whose own rows were supplied directly and therefore not dropped can find no cropland support left to allocate over; that aborts in the mass check rather than silently losing nitrogen.

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: use reporting_polity_code to say which territory a row belongs to.

  • reporting_polity_code: The polity itself, e.g. ESP-1846-1914. It is year-aware, so the same area_code resolves 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. FALSE is 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_n_inputs(example = TRUE)
#> # A tibble: 9 × 14
#>    year area_code polity_area_code reporting_polity_code reporting_polity_name
#>   <int>     <int>            <int> <chr>                 <chr>                
#> 1  2020         1                1 ARM-1991-2025         Armenia              
#> 2  2020         1                1 ARM-1991-2025         Armenia              
#> 3  2020         1                1 ARM-1991-2025         Armenia              
#> 4  2020         1                1 ARM-1991-2025         Armenia              
#> 5  2020         1                1 ARM-1991-2025         Armenia              
#> 6  2020         1                1 ARM-1991-2025         Armenia              
#> 7  2020         1                1 ARM-1991-2025         Armenia              
#> 8  2020         1                1 ARM-1991-2025         Armenia              
#> 9  2020         1                1 ARM-1991-2025         Armenia              
#> # ℹ 9 more variables: reporting_polity_has_geometry <lgl>, lon <dbl>,
#> #   lat <dbl>, item_cbs_code <int>, fert_type <chr>, n_input_t <dbl>,
#> #   method_recycling_n <chr>, method_synthetic <chr>,
#> #   method_deposition_scope <chr>