
Assemble the per-land-use-class soil carbon inputs.
Source:R/carbon_inputs.R
build_carbon_inputs.RdBuild the carbon-input layer build_carbon_balance() consumes, keyed by
(lon, lat, area_code, year, land_use). The cropland class aggregates the
per-crop cropland inputs from build_soil_carbon_inputs() to the class
grain: the class carbon density is the harvested-area-weighted mean of the
per-crop densities, and the humification fraction is the carbon-mass-weighted
mean of the per-crop fractions (mass = density times area). The grassland and
natural classes come from build_grass_natural_carbon_inputs() unchanged.
Usage
build_carbon_inputs(
resolution = c("grid", "polity"),
data = list(),
years = NULL,
crop_groups = list(),
density_basis = c("renormalised", "static"),
method_grazing = c("whep", "lpjml"),
method_unspatialized = c("fodder_pattern", "reallocate", "drop"),
method_input_cn = c("known_crops", "require_all"),
method_crop_weights = c("spatialized", "static"),
method_manure_placement = c("livestock", "crop_area"),
example = FALSE
)Source
Cropland inputs from build_soil_carbon_inputs(); grassland and
natural inputs from build_grass_natural_carbon_inputs(); assembled per
the WHEP historical carbon-balance design.
Arguments
- resolution
"grid"(default, per cell and class) or"polity"(aggregated toarea_code, area-weighting the cropland density by the polity crop area).- data
Named list of pre-loaded inputs, each falling back to its builder when absent:
cropland(thebuild_soil_carbon_inputs()output, per cell, crop and year, withtotal_c_input_mgc_ha_yrandhumified_fraction);crop_area(per cell, crop and year harvested area with columnslon,lat,area_code,item_prod_code,year,crop_area_ha, used to area-weight the crop densities);grass_natural(thebuild_grass_natural_carbon_inputs()output at the class grain); and optionalland_use(per-cell classarea_ha, used to area-weight grassland/natural polity output); andcountry_grid, the polycell support resolved to one row per cell andarea_code, from whichcrop_areais derived when absent. It is the same supportbuild_soil_carbon_inputs()reads, so the weights and the carbon they weight can never come from two different crosswalks. Whencroplandorgrass_naturalare absent the respective builder is called with the remaining members ofdata. The yield split ofcrop_groups$regimereads three more, each built when absent:regime_ratio(build_regime_yield_ratio()output; built on the crop rows that carry a share and a national yield, so pass the tablebuild_nitrogen_balance()used to make both chains read one ratio);regime_production(get_primary_production()output, for each crop's national yield and the yield bounds; read foryearsand for 1961-2023 when absent); andresidue_humification(defaults to residue_humification), whose residue and root coefficients re-weight each part's humified fraction.- years
Optional integer vector of calendar years to keep.
NULL(default) keeps every year the inputs cover. Threaded into the defaultbuild_soil_carbon_inputs()andbuild_grass_natural_carbon_inputs()builders so their readers slice to the requested years; ignored for inputs supplied viadata.- crop_groups
How cropland is resolved into land-use classes, a named list validated element-wise.
method:"rotation_groups"(default) resolves cropland into crop GROUPS – herbaceous crops pooled per irrigation regime (they rotate, so nothing inside the pool is a land-use change), woody crops per species, rainfed and irrigated separate – labelled bysoc_crop_group();"none"keeps the singlecroplandclass the package used before, for comparison and for a caller that wants one cropland number. The former name of"rotation_groups"is still accepted as a deprecated alias: it resolves to"rotation_groups", with identical results, and warns once per session.irrigation: where each crop's irrigated share of its cell area comes from."spatialized"(default) usesbuild_gridded_landuse()on the pinned spatialization inputs, crop-specific and yearly;"none"puts every crop in its rainfed group. Recorded inmethod_c_input. A pre-built share layer can be supplied asdata$crop_regime_share(lon,lat,area_code,item_prod_code,year,irrigated_share).regime: how a crop's carbon per hectare differs between its rainfed and irrigated parts."yield_split"(default) re-evaluates each part's residue and root at the part's own yield. The parts' yields keep the crop's production, with the irrigated:rainfed yield ratioRof the cell-crop-year thatbuild_nitrogen_balance()splits synthetic nitrogen with (the boundedratioofsplit_regime_yield()onbuild_regime_yield_ratio()): with irrigated sharesand mean yieldY,Y_r = Y / (1 + s (R - 1))andY_i = R Y_r. Each part carries the crop's residue and root carbon per hectare times the response ofcalculate_crop_residues()andcalculate_crop_roots()from the crop's mean yield to the part's yield, evaluated in the part's water regime, so the model's irrigation terms (a lower residue ratio for the crop groups that respond, a lower root:shoot ratio) apply to the irrigated part. Residue and root are affine in yield, so a high-yield hectare carries less than its yield share, and with the irrigation terms the crop's residue and root carbon fall on irrigated land rather than being kept. A crop wholly in one regime is re-evaluated at its mean yield in that regime. Weed and manure carbon keep the crop's density on both parts."area_split"keeps the crop's densities on both parts, the rule before the yield split. Recorded per class inmethod_regime.Inputs the default needs. Unless
data$regime_ratioanddata$regime_productionare supplied,"yield_split"builds the ratio withbuild_regime_yield_ratio()and the national yields withget_primary_production(), which read: SPAM2010 (read_spam_yields(), downloaded from Harvard Dataverse into the user cache on first use, so the first run needs network, unlessWHEP_SPAM_DIRpoints at a copy); the LPJmL crop regime yields (thelpjml-crop-regime-yieldpin, or the run inWHEP_LPJML_RUN_DIRwhen set); and the FAOSTAT pins for production, fertiliser nutrients and land use, plus theluh2-areaspin."area_split"builds no ratio and reads none of them.- density_basis
Which crop area weights the per-crop densities when they collapse to a class.
"renormalised"(default) uses the yearly cell crop area the densities were computed on – the FAOSTAT-renormalised areabuild_soil_carbon_inputs()returns ascrop_area_ha– so the class carbon mass equals the sum of the crop masses that were spatialized."static"uses the time-invariant crop-pattern area split by the polycell's share of the cell, which the package used before. The two differ wherever the spatialized cell areas of a polity-crop-year do not sum to its FAOSTAT harvested area: measured on the 2010 pins over 40,065 cropland cells the per-cell class density ratio renormalised/static has an area-weighted median of 0.988 (p5-p95 0.922-1.043). Recorded inmethod_area_basison cropland rows.- method_grazing
Whose grazing removes carbon from grassland and returns it as excreta; see
build_grass_natural_carbon_inputs()."whep"(default) charges the class WHEP's own grass intake and applied excreta, and so needsdata$livestock_intakeanddata$excreta;"lpjml"uses the model's livestock module instead and needs neither.- method_unspatialized
What happens to a polity-crop whose crop has no hectares in the (time-invariant)
crop_patternslayer."fodder_pattern"(default) first places the fodder crops (FAOSTAT items 636-649, 651 and 655) on the pooled 16 forage layers of Monfreda, Ramankutty and Foley (2008, doi:10.1029/2007GB002947 ), which the crop-pattern pin omits, gridded exactly as a crop-pattern crop is. The layers are pooled because the per-item ones follow the circa-2000 reporting vocabulary, not where each item is grown later. Whatever the layer still cannot place is reallocated as under"reallocate". The layer is read from thespatialize-fodder-patternspin (built byinst/scripts/prepare_fodder_patterns.R); settingWHEP_MONFREDA_DIRto theHarvestedAreaYield175Crops_Geotifffolder of the Monfreda archive (inst/scripts/download/download_monfreda.R) rebuilds it from the rasters instead, anddata$fodder_patternsreplaces it. It aborts rather than falling back to uniform reallocation when the layer is empty or a setWHEP_MONFREDA_DIRis wrong. The layer codes are matched on the layer names, as the archive's metadata gives no FAO name for them (assumed, unverified against a Monfreda table). At 2010 it moves 83.6 Mt C between cells relative to"reallocate", with polity totals unchanged."reallocate"spreads that carbon over the polity's crop-pattern cropland cells in proportion to each cell's total cropland area, and puts it on the crop's FAOSTAT national harvested area, so the polity's gridded carbon mass equals its national mass; this is the rule the sibling nitrogen spatialization already applies to the same gap (spatialize_country_n_to_crops()), and was the default before whep#1118."drop"discards it, which is what the package did before and what a caller who prefers a hole to a smear should ask for. Either way the mass and the affected crops are reported, and the choice is recorded inmethod_unspatialized. Reallocation needs a national area to put the carbon on, so a polity-crop with noharvested_arearow – and a polity with no cell at all in the support, which no rule here can reach (see whep#1002) – is dropped under all three methods, reported separately. Under eithermethod_crop_weights, the fodder layer only fills the polity-crops that year's crop weights do not place, and the 83.6 Mt C figure above was measured on the static weights.- method_input_cn
What a crop of unknown input C:N does to the C:N of the class it sits in.
"known_crops"(default) forms the class ratio from the crops that have one, weighted by the carbon that came with them – the rulebuild_soil_carbon_inputs()already applies one level down, where the input nitrogen totals only the components that carry a nitrogen and the carbon paired with them."require_all"leaves the class ratioNAas soon as any one crop's is unknown, so the class takes the land-use default C:N instead of the ratio its other crops measured. Recorded inmethod_input_cnon cropland rows.- method_crop_weights
Where each crop's within-polity cell weights come from.
"spatialized"(default) takes them from the spatialization engine's crop-level output,build_gridded_landuse()run on the pinned spatialization inputs and the same cell support, year by year: the crop's placement follows that year's cropland, its irrigated share and the engine's per-cell capacity ceiling, so the carbon lands where the gridded land use puts the crop.data$gridded_cropssupplies that output pre-built."static"uses the time-invariantcrop_patternslayer (harvest_fractiontimes each cell's cropland averaged over every year of the gridded-cropland pin), one map for every year, which is what the package did before (whep#1002). Measured on the pins, the L1 distance between the two cell-share vectors of a polity-crop (0 = same placement, 2 = disjoint) has an area-weighted mean of 0.30 in 1961, 0.44 in 2010 and 0.47 in 2020. Polity totals are the same under both; only where the carbon sits moves. Recorded inmethod_crop_weights.- method_manure_placement
Where the cropland manure carbon is put.
"livestock"(default) places it where the animals are, the grain the gridded nitrogen balance uses for the same manure (whep#1300): the feed intake is spread to cells by gridded livestock heads oncountry_grid(the local grain ofbuild_feed_intake_local()), andbuild_livestock_nutrient_flows()runs at"subnational", so each cell's collected manure goes to that cell's crops up to the 170 kg N/ha ceiling and the excess is trucked to neighbouring cells with room. Its crop layer is that year's cell-crop area, the hectares the carbon is divided by. Manure with no crop – trucked in, or over-applied above the cap – is spread over its cell's crops by area, as the nitrogen balance spreads it, and manure on a cell with no crop over its polity's cropland; only a polity with no cropland cell loses it, with a warning. Measured at 2010, it places 322.1 Tg C of cropland manure where"crop_area"places 346.9 Tg C, because the cell-grain intake caps grazing at each cell's grass; 41.5% of the manure carbon sits on a different cell."crop_area"allocates the polity's manure to its crops by harvested area and fans each polity-crop out to cells by crop area, as residue and roots are, which is what the package did before (whep#1307); it drops the over-cap manure the national allocation leaves with no crop (whep#805). A supplieddata$manuremust be keyed on cells (sub_territorya"lon_lat"cell id) under"livestock"; a national stream aborts. Recorded inmethod_manure_placement.- example
If
TRUE, return a small fixture instead of reading remote data. Defaults toFALSE.
Value
A tibble keyed by (lon, lat, area_code, year, land_use) at "grid"
resolution (or (area_code, year, land_use) at "polity"), with
c_input_mgc_ha_yr, humified_fraction, method_c_input, and
method_unspatialized, method_input_cn, method_crop_weights and
method_manure_placement (all NA on the grassland and natural
classes, which are neither spatialized from polity-crop totals nor
collapsed from crops), for land_use in
"cropland", "grassland" and "natural", plus the polity columns
below. Cropland groups also carry the regime split, NA on the single
"cropland" class and on grassland and natural:
method_regime: thecrop_groups$regimerule,"yield_split"or"area_split".method_regime_split: the split rules of the class's crop parts, sorted and joined by"|":"yield_ratio"(part yields from the regime yield ratio),"trivial_rainfed_only"and"trivial_irrigated_only"(the crop has one regime in the cell),"no_ratio"(no ratio for the cell-crop-year),"area_no_production"(no national yield),"area_no_components"(the per-crop layer has no residue and root densities),"area_split"(asked for) and"no_regime_share"(no irrigated share; booked rainfed). The last five keep the crop's own densities on both parts.method_residue_regime: how the parts' residue and root were set, joined the same way:"part_yield"(re-evaluated at a rainfed part's yield),"part_yield_irrigation_terms"(re-evaluated at an irrigated part's yield, with the model's irrigation terms) or"crop_density"(the crop's own density).yield_split_c_share: the share of the class's carbon input from crop parts re-evaluated at the part yields of the regime yield ratio (rule"yield_ratio").method_regime_cn: how the parts' input C:N was formed, joined the same way:"part_input_cn"(from the part's own components' carbon and nitrogen) or"crop_input_cn"(the crop's, where the per-crop layer carries no component 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: 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_carbon_inputs(example = TRUE)
#> # A tibble: 3 × 13
#> year area_code polity_area_code reporting_polity_code reporting_polity_name
#> <int> <int> <int> <chr> <chr>
#> 1 2000 1 1 ARM-1991-2025 Armenia
#> 2 2000 1 1 ARM-1991-2025 Armenia
#> 3 2000 1 1 ARM-1991-2025 Armenia
#> # ℹ 8 more variables: reporting_polity_has_geometry <lgl>, lon <dbl>,
#> # lat <dbl>, land_use <chr>, c_input_mgc_ha_yr <dbl>,
#> # humified_fraction <dbl>, method_c_input <chr>, method_unspatialized <chr>