Skip to contents

Assembles build_n_inputs()'s long-format nitrogen inputs into the aggregate N_input_* sums, adds the output side (crop production, residue use/burning, grazed weeds, soil organic-matter sequestration), runs the nitrogen-loss cascade (calculate_nh3(), calculate_soil_n2o(), calculate_n_leaching(), calculate_indirect_n2o_nh3()), closes the balance (N_input_full - N_output_full), applies the N-limitation cap on SOM sequestration, and derives nutrient-use-efficiency (NUE) indicators plus a GWP/CO2e indicator for the nitrous-oxide streams. Ported from Spain_Hist's Balance_parameters()/N_Figs.R equations.

Usage

build_nitrogen_balance(
  methods = list(nh3 = "manner", n2o = "ipcc2019", leaching = "meisinger_drainage"),
  resolution = c("grid", "polity"),
  polity_validity = c("keep", "flag", "drop"),
  data = list(),
  gwp = c("ar6", "ar5", "ar4"),
  example = FALSE
)

Arguments

methods

A named list of method choices: nh3 (forwarded to calculate_nh3(), default "manner"), n2o (forwarded to calculate_soil_n2o(), default "ipcc2019", the globally applicable IPCC 2019 Tier 1 method) and leaching (forwarded to calculate_n_leaching(), default "meisinger_drainage").

resolution

"grid" (default) or "polity", as in build_n_inputs(); "polity" sums every term (and re-derives every indicator) over cells.

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 upstream inputs. n_inputs (build_n_inputs()'s output) is used directly when supplied, else build_n_inputs() is called with this SAME data list (so its nested readers, e.g. bnf_input/npp_n_input/livestock_intake, propagate through). Also required:

  • npp_n_input: shared with build_n_inputs()'s "recycling" term (see calculate_npp_carbon_nitrogen()); used here for prod_n_t.

  • ag_land_support: the build_ag_land_support() table, supplying area_ha (the per-hectare boundary denominator) on a physical-land basis. Derived natively when absent and derivable, and shared with build_n_inputs() so both see one table; when neither supplied nor derivable, area_ha falls back to the harvested area the NPP input carries.

  • residue_destiny_input: calculate_residue_destinies()'s required input (item_prod_code, residue_dm_t, plus whatever the chosen residue_destiny_method needs), for used_residue_n_t/ burnt_residue_n_t. residue_destiny_method selects the method (default "krausmann_regional").

  • livestock_intake: shared with build_n_inputs()'s manure term; its "grass" feed_quality rows drive grazed_weeds_n_t.

  • carbon_balance: shared with build_n_inputs()'s "som_ mineralization" term; its son_change_kgn_ha drives som_ sequestration_n_t.

  • n_balance_drivers: additional per-(key, fert_type) driver columns calculate_nh3()/calculate_soil_n2o()/ calculate_indirect_n2o_nh3() need beyond n_input_t/fert_type (climate, irrig_type, plus every MANNER driver column when methods$nh3 == "manner"), joined by the balance key AND fert_type (the Title-case vocabulary from calculate_nh3()'s own documentation, e.g. "Synthetic", "Solid", "Excreta_other"; see the file-level "fert_type vocabulary bridge" note for the exact mapping from build_n_inputs()'s lowercase values). A second set of balance-key-only driver columns (fert_type, climate, irrig_cat, tillage, som_share, cn_input, land_use) for calculate_n_leaching() is read from n_balance_leaching_drivers, which must hold at most one row per balance key (a many-to-one join aborts on duplicate keys rather than fanning the rows out and misaligning drainage_mm). Missing required drivers abort inside the called calculate_*() function, naming the exact column.

  • drainage_mm: annual drainage (mm) for calculate_n_leaching(), as a numeric vector aligned to the balance-key rows, or already present as a drainage_mm column via n_balance_leaching_drivers.

gwp

100-year global warming potential standard for N2O, "ar6" (default), "ar5" or "ar4", matching build_crop_soil_n2o_extension().

example

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

Value

A tibble keyed by year/area_code/item_cbs_code (plus lon/lat at resolution = "grid") with area_ha (each crop's harvested hectares in the cell, summed over cells at resolution = "polity"; used downstream to convert tonnes N to a per-hectare rate), the input aggregates (n_input_full_t, n_input_full_nosom_t, n_input_std_t, n_input_som_t, n_input_for_n2o_t), the output aggregates (n_output_residues_t, n_output_som_t, n_output_useful_t, n_output_std_t, n_output_full_t), the loss terms (nh3_n_t, n2o_direct_n_t, no3_n_t, denitrification_n_t, n2o_indirect_no3_n_t, n2o_indirect_nh3_n_t), the (post-cap) som_sequestration_n_t, n_balance_t, surplus_t, surplus_share, the five NUE ratios (nue_std, nue_residues, nue_som, nue_useful, nue_full), total_gwp_co2e_kg, and the method_nh3/ method_soil_n2o/method_leaching provenance columns, plus the polity columns below. When the supplied n_inputs carry them, the method_recycling_n, method_synthetic and method_deposition_scope stamps from build_n_inputs() are carried through as well, so a balance names the input conventions that produced it. Gains reporting_polity_out_of_span when polity_validity = "flag".

Details

polity_validity is forwarded to build_n_inputs() – which forwards it in turn to build_ag_land_support(), build_n_deposition(), build_urban_n() and spatialize_country_n_to_crops() – and then applied to the balance rows themselves, so one choice governs the whole build (whep#727). A data$n_inputs table supplied directly is left alone: it was built by its own call with its own choice, and the balance rows derived from it still get this argument's fate at the tail.

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_nitrogen_balance(example = TRUE)
#> # A tibble: 1 × 41
#>    year area_code polity_area_code reporting_polity_code reporting_polity_name
#>   <int>     <int>            <int> <chr>                 <chr>                
#> 1  2020        10               10 AUS-1901-2025         Australia            
#> # ℹ 36 more variables: reporting_polity_has_geometry <lgl>,
#> #   item_cbs_code <int>, area_ha <dbl>, n_input_full_t <dbl>,
#> #   n_input_full_nosom_t <dbl>, n_input_std_t <dbl>, n_input_som_t <dbl>,
#> #   n_input_for_n2o_t <dbl>, prod_n_t <dbl>, used_residue_n_t <dbl>,
#> #   burnt_residue_n_t <dbl>, grazed_weeds_n_t <dbl>,
#> #   som_sequestration_n_t <dbl>, n_output_residues_t <dbl>,
#> #   n_output_som_t <dbl>, n_output_useful_t <dbl>, n_output_std_t <dbl>, …