Repository navigation
Forest loss drivers: fix the Curtis table, add GFW/Sims et al. (2025) as default source #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 16 commits
59d7d5f
5b69ab0
2524e25
0b54a9b
95ceecb
9dd6a59
2ea680f
e179f2c
f5ca996
3fd4047
9179650
def0792
149662e
795523b
2fe6a09
9f6f91e
c073b5f
9323673
c2903c3
80e0d28
ec20f16
7b85b43
4a6df67
6f8b408
1563aa9
af2e7b7
ce477f3
410607a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,71 @@ | ||||||
| # Driver classes that reach f35_forest_lost_share. Module 35 resets the age structure of the | ||||||
| # "lost" area (the land stays forest) and module 52 books the flux as land-use-change CO2, so a | ||||||
| # class qualifies only if it leaves the land as forest, is not decided endogenously by MAgPIE, | ||||||
| # and is a land-use rather than a natural process. Of the eight GFW classes only shifting | ||||||
| # cultivation passes. The others are published by mrvalidation::calcValidTreeCoverLoss: | ||||||
| # permanent agriculture, hard commodities and settlements remove the forest, logging is | ||||||
| # MAgPIE's own timber harvest, wildfire and other natural disturbances are natural. | ||||||
| gfwModelDrivers <- c("shifting_cultivation") | ||||||
|
|
||||||
| #' @title calcForestLossByDriver | ||||||
| #' | ||||||
| #' @description Annual forest area loss by driver, Mha per year, from the Sims et al. (2025) | ||||||
| #' driver product (`source = "GFW"`, default) or the Curtis et al. (2018) table | ||||||
| #' (`source = "Curtis"`), with the FRA 2020 fire series bound on as `overall`. | ||||||
| #' | ||||||
| #' @param source `"GFW"` (default) or `"Curtis"`. Curtis computes as before, from the | ||||||
| #' corrected Curtis et al. (2018) table. | ||||||
| #' @param period Years averaged into the annual rate, GFW only (default 2015:2024). Curtis | ||||||
| #' carries a single 2001-2015 mean. | ||||||
| #' | ||||||
| #' @details For GFW only the classes in `gfwModelDrivers` are returned. `overall` is the FRA | ||||||
| #' fire series averaged over its own years (2000-2017), as before; GAMS loads it but never | ||||||
| #' reads it. | ||||||
| #' | ||||||
| #' @return MAgPIE object with forest area lost per year by driver, Mha | ||||||
| #' @author Abhijeet Mishra, Michael Crawford | ||||||
| #' @importFrom magclass mbind setNames dimSums getYears getItems | ||||||
| #' @importFrom madrat readSource | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| #' @seealso [readGFWLossByDriver()], [readForestLossDrivers()] | ||||||
| #' @examples | ||||||
| #' \dontrun{ | ||||||
| #' calcOutput("ForestLossByDriver", aggregate = FALSE) | ||||||
| #' calcOutput("ForestLossByDriver", source = "Curtis", aggregate = FALSE) | ||||||
| #' } | ||||||
|
|
||||||
| calcForestLossByDriver <- function(source = "GFW", period = 2015:2024) { | ||||||
|
|
||||||
| fao <- setNames(readSource("FRA2020", subtype = "forest_fire", convert = TRUE), "overall") | ||||||
| fao <- dimSums(fao, dim = 2) / length(getYears(fao)) # mean annual area lost to fire | ||||||
|
|
||||||
| drivers <- switch( | ||||||
| source, # nolint: undesirable_function_linter. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of disabling the linter
Suggested change
|
||||||
| "Curtis" = readSource("ForestLossDrivers"), | ||||||
| "GFW" = { | ||||||
| x <- readSource("GFWLossByDriver", convert = TRUE) | ||||||
| wanted <- paste0("y", period) | ||||||
| absent <- setdiff(wanted, getYears(x)) | ||||||
| if (length(absent) > 0) { | ||||||
| stop("calcForestLossByDriver: the GFW record does not cover ", toString(absent), | ||||||
| ". It runs ", min(getYears(x, TRUE)), "-", max(getYears(x, TRUE)), ".") | ||||||
| } | ||||||
| absent <- setdiff(gfwModelDrivers, getItems(x, 3)) | ||||||
| if (length(absent) > 0) { | ||||||
| stop("calcForestLossByDriver: the GFW source does not carry driver class(es) ", | ||||||
| toString(absent), ".") | ||||||
| } | ||||||
| dimSums(x[, wanted, gfwModelDrivers], dim = 2) / length(wanted) | ||||||
| }, | ||||||
| stop("calcForestLossByDriver: unknown source '", source, # nolint: undesirable_function_linter. | ||||||
| "'. Use \"GFW\" or \"Curtis\".") | ||||||
| ) | ||||||
|
|
||||||
| out <- mbind(fao, drivers) | ||||||
|
|
||||||
| return(list(x = out, | ||||||
| weight = NULL, | ||||||
| unit = "Mha", | ||||||
| description = paste0("Forest area lost per year by driver (", | ||||||
| source, ")"), # nolint: undesirable_function_linter. | ||||||
| isocountries = FALSE)) | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,36 +1,65 @@ | ||||||
| #' @title calcForestLossShare | ||||||
| #' | ||||||
| #' @description Calculates which share of forest land is lost due to different drivers | ||||||
| #' @description Calculates which share of forest area is lost per year to each driver of | ||||||
| #' forest loss. | ||||||
| #' | ||||||
| #' @return MAgPIE object with share of area lost in forests due to different drivers | ||||||
| #' @author Abhijeet Mishra | ||||||
| #' @seealso | ||||||
| #' \code{\link{readForestLossDrivers}} | ||||||
| #' | ||||||
| #' @param source Driver data to use, `"GFW"` (default) or `"Curtis"`. See | ||||||
| #' [calcForestLossByDriver()]. | ||||||
| #' @param period Years averaged to give the annual rate, for `source = "GFW"` only. | ||||||
| #' | ||||||
| #' @details For `source = "GFW"` numerator and denominator come from the same product at the | ||||||
| #' same canopy threshold: UMD tree cover loss by driver over UMD tree cover extent in 2000, | ||||||
| #' the base the loss is measured on. For `source = "Curtis"` the denominator stays FRA 2020 | ||||||
| #' naturally regenerating forest area in 2010, reproducing the previous parameterisation | ||||||
| #' exactly. The `overall` column (FRA fire area) is divided by the same denominator as the | ||||||
| #' drivers of its source and exceeds 1 in a few savanna countries; GAMS loads it but never reads | ||||||
| #' it. Shares above 1 are clamped to 1; for a driver column this also warns with the country names. | ||||||
| #' | ||||||
| #' @return MAgPIE object with the share of forest area lost per year by driver | ||||||
| #' @author Abhijeet Mishra, Michael Crawford | ||||||
| #' @importFrom magclass setYears setNames getItems | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| #' @importFrom utils head | ||||||
| #' @importFrom madrat calcOutput readSource | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| #' @seealso [calcForestLossByDriver()] | ||||||
| #' @examples | ||||||
| #' | ||||||
| #' \dontrun{ | ||||||
| #' calcOutput("ForestLossShare",aggregate=FALSE) | ||||||
| #' \dontrun{ | ||||||
| #' calcOutput("ForestLossShare", aggregate = FALSE) | ||||||
| #' calcOutput("ForestLossShare", source = "Curtis", aggregate = FALSE) | ||||||
| #' } | ||||||
| #' | ||||||
| #' | ||||||
|
|
||||||
| calcForestLossShare <- function(){ | ||||||
|
|
||||||
| lost_area <- calcOutput("ForestFireLoss",aggregate=FALSE) # Convert=T returns Mha area lost to fire | ||||||
| forest_area <- setYears(setNames(readSource("FRA2020",subtype = "forest_area",convert = TRUE)[,"y2010","naturallyRegeneratingForest"],NULL),NULL) # Convert=T returns Mha area of forests | ||||||
| calcForestLossShare <- function(source = "GFW", period = 2015:2024) { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Renaming gets around the linter issues |
||||||
|
|
||||||
| lostArea <- calcOutput("ForestLossByDriver", source = source, # nolint: undesirable_function_linter. | ||||||
| period = period, aggregate = FALSE) | ||||||
|
|
||||||
| forestArea <- switch( | ||||||
| source, # nolint: undesirable_function_linter. | ||||||
| "GFW" = readSource("GFWLossByDriver", subtype = "extent", convert = TRUE), | ||||||
| "Curtis" = readSource("FRA2020", subtype = "forest_area", | ||||||
| convert = TRUE)[, "y2010", "naturallyRegeneratingForest"], | ||||||
| stop("calcForestLossShare: unknown source '", source, # nolint: undesirable_function_linter. | ||||||
| "'. Use \"GFW\" or \"Curtis\".") | ||||||
| ) | ||||||
| forestArea <- setYears(setNames(forestArea, NULL), NULL) | ||||||
|
|
||||||
| lostShare <- lostArea / forestArea | ||||||
| lostShare[is.infinite(lostShare)] <- 0 | ||||||
| lostShare[is.na(lostShare)] <- 0 | ||||||
| # overall (FRA fire area over the driver denominator) exceeds 1 in savanna countries; GAMS | ||||||
| # never reads it. A driver share above 1 would mean the numerator and denominator disagree. | ||||||
| drivers <- setdiff(getItems(lostShare, 3), "overall") | ||||||
| over <- lostShare[, , drivers] > 1 | ||||||
| if (any(over)) { | ||||||
| warning("calcForestLossShare: ", sum(over), " driver share(s) above 1 clamped to 1 (", | ||||||
| source, "): ", # nolint: undesirable_function_linter. | ||||||
| toString(utils::head(getItems(lostShare, 1)[apply(over, 1, any)], 5)), ".") | ||||||
| } | ||||||
| lostShare[lostShare > 1] <- 1 | ||||||
|
|
||||||
| lost_share <- lost_area/forest_area | ||||||
| lost_share[is.infinite(lost_share)] <- 0 | ||||||
| lost_share[is.na(lost_share)] <- 0 | ||||||
| lost_share[lost_share>1] <- 1 | ||||||
|
|
||||||
| out <- lost_share | ||||||
| weight <- forest_area | ||||||
|
|
||||||
| return(list(x=out, | ||||||
| weight=weight, | ||||||
| unit="1", | ||||||
| description="Area lost in forests by drivers", | ||||||
| isocountries=FALSE)) | ||||||
| return(list(x = lostShare, | ||||||
| weight = forestArea, | ||||||
| unit = "1", | ||||||
| description = paste0("Share of forest area lost per year by driver (", | ||||||
| source, ")"), # nolint: undesirable_function_linter. | ||||||
| isocountries = FALSE)) | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,62 @@ | ||||||
| # GADM codes that are not madrat ISO countries but sit inside one, so their loss is added | ||||||
|
mscrawford marked this conversation as resolved.
Outdated
|
||||||
| # to the host rather than discarded. Measured over 2001-2025 at a 30 per cent threshold: | ||||||
| # XKO 19 126 ha, ZNC 1740 ha, XAD 80 ha. | ||||||
| gfwIsoHosts <- c(XKO = "SRB", # Kosovo, inside Serbia in madrat's country set | ||||||
| ZNC = "CYP", # Northern Cyprus | ||||||
| XAD = "CYP") # Akrotiri and Dhekelia, the sovereign base areas on Cyprus | ||||||
|
|
||||||
| # Undocumented GADM placeholders for disputed territories, dropped rather than guessed at. Loss | ||||||
| # 2001-2025: Z01, Z06 and Z07 carry 169 451 ha together, 0.031 per cent of the global total. | ||||||
| # Extent 2000: 6.5 Mha over all nine, 0.16 per cent, mostly Z07 and Z01. | ||||||
| gfwIsoDropped <- sprintf("Z%02d", 1:9) | ||||||
|
|
||||||
| #' @title convertGFWLossByDriver | ||||||
| #' | ||||||
| #' @description Reconciles the GADM country set used by GFW with madrat's ISO set. | ||||||
| #' | ||||||
| #' GADM and madrat disagree on 6 codes carrying loss and on 39 madrat countries GFW does | ||||||
| #' not list; both are handled explicitly rather than by a silent inner join. | ||||||
| #' | ||||||
| #' @param x magpie object as returned by [readGFWLossByDriver()] | ||||||
| #' @param subtype `"loss"` or `"extent"`, as passed to the read function; the reconciliation is | ||||||
| #' the same for both | ||||||
| #' @return magpie object on madrat's ISO country set, unit Mha | ||||||
| #' @author Michael Crawford | ||||||
| #' @importFrom madrat toolCountryFill | ||||||
| #' @importFrom magclass getItems | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| #' @seealso [readGFWLossByDriver()] | ||||||
| #' @examples | ||||||
| #' \dontrun{ | ||||||
| #' a <- readSource("GFWLossByDriver", convert = TRUE) | ||||||
| #' } | ||||||
|
|
||||||
| convertGFWLossByDriver <- function(x, subtype = "loss") { | ||||||
|
|
||||||
| before <- sum(x) | ||||||
|
|
||||||
| for (code in names(gfwIsoHosts)) { | ||||||
| host <- gfwIsoHosts[[code]] | ||||||
| if (code %in% getItems(x, 1)) { | ||||||
| if (host %in% getItems(x, 1)) { | ||||||
| x[host, , ] <- x[host, , ] + x[code, , ] | ||||||
| } else { | ||||||
| getItems(x, 1)[getItems(x, 1) == code] <- host | ||||||
| next | ||||||
| } | ||||||
| x <- x[setdiff(getItems(x, 1), code), , ] | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| out <- toolCountryFill(x, fill = 0, no_remove_warning = gfwIsoDropped) | ||||||
|
|
||||||
| # A future version moving real loss onto a code madrat does not know must fail here. | ||||||
| lost <- (before - sum(out)) / before | ||||||
| if (lost > 0.005) { | ||||||
| stop("GFWLossByDriver: reconciling GADM with madrat's ISO set dropped ", | ||||||
| round(100 * lost, 3), " per cent of global loss, above the 0.5 per cent tolerance. ", | ||||||
| "Codes not in madrat: ", | ||||||
| toString(setdiff(getItems(x, 1), madrat::getISOlist())), ".") | ||||||
| } | ||||||
|
|
||||||
| return(out) | ||||||
| } | ||||||
Uh oh!
There was an error while loading. Please reload this page.