Skip to content
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

Remove inconsistency regarding REMIND/EDGE-T communication of active modes energy service demand #24

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .buildlibrary
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ValidationKey: '199700'
ValidationKey: '799040'
AutocreateReadme: yes
AcceptedWarnings:
- 'Warning: package ''.*'' was built under R version'
Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ cff-version: 1.2.0
message: If you use this software, please cite it using the metadata from this file.
type: software
title: 'reporttransport: Reporting package for edgeTransport'
version: 0.1.0
date-released: '2024-09-04'
version: 0.4.0
date-released: '2024-09-10'
abstract: This package contains edgeTransport-specific routines to report model results.
The main functionality is to generate transport reporting variables in MIF format
from a given edgeTransport model run folder or REMIND input data.
Expand Down
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Type: Package
Package: reporttransport
Title: Reporting package for edgeTransport
Version: 0.1.0
Date: 2024-09-04
Version: 0.4.0
Date: 2024-09-10
Authors@R:
person("Johanna", "Hoppe", , "[email protected]", role = c("aut", "cre"))
Description: This package contains edgeTransport-specific routines to
Expand Down
2 changes: 1 addition & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export(reportLiquidsAndGasesComposition)
export(reportREMINDinputVarSet)
export(reportToREMINDcapitalCosts)
export(reportToREMINDenergyEfficiency)
export(reportToREMINDesDemand)
export(reportToREMINDfinalEnergyDemand)
export(reportToREMINDfinalEnergyShares)
export(reportToREMINDtrpdemand)
export(reportTransportVarSet)
export(reportUE)
export(storeData)
Expand Down
11 changes: 8 additions & 3 deletions R/reportREMINDinputVarSet.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ reportREMINDinputVarSet <- function(fleetESdemand,

# See needed inputs in REMIND/modules/35_transport/edge_esm/datainput.gms
# and REMIND/modules/29_CES_parameters/calibratedatainput.gms
f35_esCapCost <- reportToREMINDcapitalCosts(fleetCapCosts, fleetESdemand, timeResReporting, demScen, SSPscen, transportPolScen, helpers)
f35_fe2es <- reportToREMINDenergyEfficiency(fleetEnergyIntensity, scenSpecLoadFactor, fleetESdemand, hybridElecShare, timeResReporting,
f35_esCapCost <- reportToREMINDcapitalCosts(fleetCapCosts, fleetESdemand, hybridElecShare, timeResReporting, demScen, SSPscen, transportPolScen, helpers)
f35_fe2es <- reportToREMINDenergyEfficiency(fleetFEdemand, fleetESdemand, hybridElecShare, timeResReporting,
demScen, SSPscen, transportPolScen, helpers)
f35_demByTech <- reportToREMINDfinalEnergyDemand(fleetFEdemand, timeResReporting, demScen, SSPscen, transportPolScen, helpers)

Expand All @@ -72,7 +72,12 @@ reportREMINDinputVarSet <- function(fleetESdemand,
f29_trpdemand <- merge(fe2es, inputREMIND$f35_demByTech, by = intersect(names(fe2es), names(f35_demByTech)))
weightESdemand <- f29_trpdemand[, .(value = sum(fe2es * value)), by = c("tall", "all_regi", "GDP_scenario", "DEM_scenario", "EDGE_scenario", "all_teEs")]
f29_trpdemand <- f29_trpdemand[, .(value = sum(fe2es * value)), by = c("tall", "all_regi", "GDP_scenario", "DEM_scenario", "EDGE_scenario", "all_in")]

#Check for data consistency
test <- reportToREMINDesDemand(fleetESdemand, hybridElecShare, timeResReporting, demScen, SSPscen, transportPolScen, helpers)
setcolorder(test, names(weightESdemand))
setkey(test, all_regi,tall, all_teEs)
setkey(weightESdemand, all_regi, tall, all_teEs)
if (!all.equal(test, weightESdemand[tall %in% unique(test$tall)])) stop("The data set that is reported to REMIND is inconsistent. Please check reportREMINDinputVarSet()")
inputREMIND[["f29_trpdemand"]] <- f29_trpdemand
inputREMIND[["weightESdemand"]] <- weightESdemand

Expand Down
50 changes: 34 additions & 16 deletions R/reportToREMINDcapitalCosts.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#'
#' @param fleetCapCosts capital costs on fleet level
#' @param fleetESdemand energy service demand on fleet level
#' @param hybridElecShare share of electric driving for hybrid electric vehicles
#' @param timeResReporting time resolution reporting
#' @param demScen demand scenario
#' @param SSPscen SSP scenario
Expand All @@ -12,26 +13,43 @@
#' @export
#' @author Johanna Hoppe
#' @import data.table
#' @importFrom rmndt approx_dt

reportToREMINDcapitalCosts <- function(fleetCapCosts, fleetESdemand, timeResReporting, demScen, SSPscen, transportPolScen, helpers) {
reportToREMINDcapitalCosts <- function(fleetCapCosts, fleetESdemand, hybridElecShare, timeResReporting, demScen, SSPscen, transportPolScen, helpers) {

# f35_esCapCost(tall, all_regi, all_GDPscen, all_demScen, all_EDGE_scenario, all_teEs) # nolint: commented_code_linter
f35_esCapCost <- copy(fleetCapCosts) # nolint: object_name_linter
f35_esCapCost <- f35_esCapCost[period %in% timeResReporting]
# Note: In order to account for the influence of active modes, the capital costs per p|tkm are related to the adjusted
# energy service demand per sector technology that is reported to REMIND and includes the energy service demand of active modes (split and distributed equally to all technologies)
# f35_esCapCost(tall, all_regi, all_GDPscen, all_demScen, all_EDGE_scenario, all_teEs) # nolint: commented_code_linter
esCapCost <- fleetCapCosts[period %in% timeResReporting] # nolint: object_name_linter
EDGETesDemand <- fleetESdemand[period %in% timeResReporting][, c("unit", "variable") := NULL]
setnames(EDGETesDemand, "value", "esDem")
esCapCost <- merge(esCapCost, EDGETesDemand, by = intersect(names(esCapCost), names(EDGETesDemand)))
esCapCost[, value := value * esDem * 1e6] #[2005US$]
# The absolute costs of hybrids should not get lost so they are attributed acording to the hybridElecShare
esCapCostWoHybrid <- copy(esCapCost)
hybrids <- esCapCostWoHybrid[technology == "Hybrid electric"]
hybrids[, value := hybridElecShare * value][, technology := "BEV"]
esCapCostWoHybrid[technology == "Hybrid electric", value := (1 - hybridElecShare) * value]
esCapCostWoHybrid[technology == "Hybrid electric", technology := "Liquids"]
esCapCostWoHybrid <- rbind(esCapCostWoHybrid, hybrids)
byCols <- names(esCapCostWoHybrid)
byCols <- byCols[!byCols %in% c("value")]
esCapCost <- esCapCostWoHybrid[, .(value = sum(value)), by = eval(byCols)]
# Map on REMIND energy service demand technologies
capCostMap <- unique(helpers$mapEdgeToREMIND[, c("all_teEs", "univocalName", "technology")])
# Walk and Cycle are not mapped on all_teEs
# Walk and Cycle don't have capital costs and are not mapped directly on all_teEs
capCostMap <- capCostMap[!is.na(all_teEs)]
f35_esCapCost <- merge(f35_esCapCost, capCostMap, by = c("univocalName", "technology")) # nolint: object_name_linter
# aggregate with fleet ES demand as weight
weightESdemand <- copy(fleetESdemand)
setnames(weightESdemand, "value", "ESdemand")
weightESdemand[, c("unit", "variable") := NULL]
f35_esCapCost <- merge(f35_esCapCost, weightESdemand, by = intersect(names(f35_esCapCost), names(weightESdemand)))
f35_esCapCost[, sumES := sum(ESdemand), by = c("region", "period", "all_teEs")]
# Remove weight if whole branch has zero demand to keep data
f35_esCapCost[sumES == 0, ESdemand := 1]
f35_esCapCost[, sumES := sum(ESdemand), by = c("region", "period", "all_teEs")]
f35_esCapCost <- f35_esCapCost[, .(value = sum(value * ESdemand / sumES)), by = c("region", "period", "all_teEs")] # nolint: object_name_linter
esCapCost <- merge(esCapCost, capCostMap, by = c("univocalName", "technology"))[, c("univocalName", "technology") := NULL] # nolint: object_name_linter
esCapCost <- esCapCost[, .(value = sum(value)), by = c("region", "period", "all_teEs")]
REMINDesDemand <- reportToREMINDesDemand(fleetESdemand, hybridElecShare, timeResReporting, demScen, SSPscen, transportPolScen, helpers)
REMINDesDemand[, c("GDP_scenario", "DEM_scenario", "EDGE_scenario") := NULL]
setnames(REMINDesDemand, c("value", "tall", "all_regi"), c("REMINDesDem", "period", "region"))
f35_esCapCost <- merge(esCapCost, REMINDesDemand, by = intersect(names(esCapCost), names(REMINDesDemand)))
# Filter out zero demand values and interpolate them from other timesteps as they do not mean zero costs (they are not relevant, as the demand is zero)
f35_esCapCost <- f35_esCapCost[!REMINDesDem < 1e-7]
f35_esCapCost[, value := value/REMINDesDem * 1e-9][, REMINDesDem := NULL]#[2005US$/p|tkm]
f35_esCapCost <- approx_dt(f35_esCapCost, unique(f35_esCapCost$period), "period", "value", extrapolate = TRUE)

checkForNAsAndDups(f35_esCapCost, "f35_esCapCost", "reportToREMINDcapitalCosts()")
f35_esCapCost <- prepareForREMIND(f35_esCapCost, demScen, SSPscen, transportPolScen)
setnames(f35_esCapCost, c("period", "region"), c("tall", "all_regi"))
Expand Down
64 changes: 12 additions & 52 deletions R/reportToREMINDenergyEfficiency.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#'Report to REMIND f35_fe2es
#'
#' @param fleetEnergyIntensity energy intensity on fleet level
#' @param scenSpecLoadFactor scenario specific load factor data
#' @param fleetFEdemand final energy demand on fleet level
#' @param fleetESdemand energy service demand on fleet level
#' @param hybridElecShare share of electric driving for hybrid electric vehicles
#' @param timeResReporting time resolution reporting
Expand All @@ -14,65 +13,26 @@
#' @author Johanna Hoppe
#' @import data.table

reportToREMINDenergyEfficiency <- function(fleetEnergyIntensity,
scenSpecLoadFactor,
reportToREMINDenergyEfficiency <- function(fleetFEdemand,
fleetESdemand,
hybridElecShare,
timeResReporting,
demScen,
SSPscen,
transportPolScen,
helpers) {

MJtoTwa <- 3.169e-14
# f35_fe2es(tall, all_regi, all_GDPscen, all_demScen, EDGE_scenario, all_teEs) # nolint: commented_code_linter
fleetEnergyIntensity <- copy(fleetEnergyIntensity) # nolint: object_name_linter
loadFactor <- copy(scenSpecLoadFactor)[, c("variable", "unit") := NULL]
setnames(loadFactor, "value", "loadFactor")
f35_fe2es <- merge(fleetEnergyIntensity[period %in% timeResReporting], loadFactor[period %in% timeResReporting],
by = intersect(names(fleetEnergyIntensity), names(loadFactor)), all = TRUE)
# REMIND does not know about active modes, so they are filtered out (they cannot be assigned to a technology in the CES tree)
f35_fe2es <- f35_fe2es[!univocalName %in% c("Cycle", "Walk")]
f35_fe2es[, value := value / loadFactor][, loadFactor := NULL][, unit := "MJ/(p|t)km"]
# In order to deal with hybrids, a fuel column is needed
f35_fe2es[, techWithoutHybrid := technology]
f35_fe2es[technology == "Hybrid electric", techWithoutHybrid := "Liquids"]
hybrids <- f35_fe2es[technology == "Hybrid electric"]
hybrids[, techWithoutHybrid := "BEV"]
f35_fe2es <- rbind(f35_fe2es, hybrids)
#prepare weight
#use FE demand as weight but keep hybrid electric in order to aggregate accurately
weight <- copy(f35_fe2es)[, c("unit", "variable") := NULL]
fleetESdemand <- copy(fleetESdemand)[, c("unit", "variable") := NULL]
setnames(fleetESdemand, "value", "ESdemand")
fleetESdemand[, techWithoutHybrid := technology]
fleetESdemand[technology == "Hybrid electric", techWithoutHybrid := "Liquids"]
hybrids <- fleetESdemand[technology == "Hybrid electric"]
hybrids[, techWithoutHybrid := "BEV"]
fleetESdemand <- rbind(fleetESdemand, hybrids)
fleetESdemand[technology == "Hybrid electric" & techWithoutHybrid == "Liquids", ESdemand := ESdemand * (1 - hybridElecShare)]
fleetESdemand[technology == "Hybrid electric" & techWithoutHybrid == "BEV", ESdemand := ESdemand * (hybridElecShare)]
weight <- merge(weight, fleetESdemand, by = intersect(names(weight), names(fleetESdemand)))
weight[, FEdemand := value * ESdemand][, c("value", "ESdemand") := NULL]
byCols <- names(weight)
byCols <- byCols[!byCols %in% c("value")]

f35_fe2es[, value := 1 / value][, unit := "(p|t)km/MJ"]
# convert to trn (pkm|tkm)/TWa
f35_fe2es[, value := value * 10^-12/MJtoTwa]
f35_fe2es <- merge(f35_fe2es, weight, by = intersect(names(f35_fe2es), names(weight))) # nolint: object_name_linter
fe2esMap <- unique(helpers$mapEdgeToREMIND[, c("all_teEs", "univocalName", "technology")])
fe2esMap <- fe2esMap[!is.na(all_teEs)]
setnames(fe2esMap, "technology", "techWithoutHybrid")
f35_fe2es <- merge(f35_fe2es, fe2esMap, by = c("univocalName", "techWithoutHybrid"), all.x = TRUE) # nolint: object_name_linter
f35_fe2es[, sumFE := sum(FEdemand), by = c("region", "period", "all_teEs")]
# Remove weight if whole branch has zero demand to keep data
f35_fe2es[sumFE == 0, FEdemand := 1]
f35_fe2es[, sumFE := sum(FEdemand), by = c("region", "period", "all_teEs")]
f35_fe2es <- f35_fe2es[, .(value = sum(value * FEdemand / sumFE)), by = c("region", "period", "all_teEs")] # nolint: object_name_linter
checkForNAsAndDups(f35_fe2es, "f35_fe2es", "reportToREMINDenergyEfficiency()")
f35_fe2es <- prepareForREMIND(f35_fe2es, demScen, SSPscen, transportPolScen)
setnames(f35_fe2es, c("period", "region"), c("tall", "all_regi"))
ES <- reportToREMINDesDemand(fleetESdemand, hybridElecShare, timeResReporting, demScen, SSPscen, transportPolScen, helpers)
FE <- reportToREMINDfinalEnergyDemand(fleetFEdemand, timeResReporting, demScen, SSPscen, transportPolScen, helpers)[, c("all_enty", "all_in") := NULL]
setnames(FE, "value", "FE")
setnames(ES, "value", "ES")
f35_fe2es <- merge(ES, FE, by = intersect(names(FE), (names(ES))))
# Insert small number instead of zero
f35_fe2es <- f35_fe2es[!ES < 1e-7]
Copy link
Collaborator Author

@johannah-pik johannah-pik Sep 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robertpietzcker The differences in the energy efficiency values for freight are coming from a change in how the energy efficiency is caluclated. They appear solely, where the energy service/final energy demand is anyway zero.
Previously I used the reciprocal energy intensity and aggregated it to all_teEs level by using the final energy demand as weight (energy efficiency is reported in (p|t)km/MJ to REMIND).
Where the whole branch has zero final energy demand, a weight of 1 was used, so that the energy intensity value does not disappear (the CES tree cannot deal with an energy efficiency of zero). Now I used FE and ES on te_Es level including the active modes to calculate the energy efficiency. To get rid of the zeros I used another method: I removed them beforehand and interpolated the missing values afterwards. Filtering out the zero demand values in advance ensures that we do not also interpolate NAs, which would indicate errors in the model if they occur.

So in summary: The changes come from a different way of dealing with zero demand values and don't affect the model results.

f35_fe2es[, value := ES/FE][, c("ES", "FE") := NULL]
f35_fe2es <- approx_dt(f35_fe2es, unique(f35_fe2es$tall), "tall", "value", extrapolate = TRUE)
setcolorder(f35_fe2es, c("all_regi", "tall", "GDP_scenario", "DEM_scenario", "EDGE_scenario", "all_teEs", "value"))

return(f35_fe2es)
}
69 changes: 69 additions & 0 deletions R/reportToREMINDesDemand.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#'Report energy service demand to REMIND
#'
#'Note that this is a helpers function that accounts for structural differences in REMIND and EDGE-T.
#'The REMIND CES tree cannot represent active modes, as they deliver valuable energy service demand without capital cost and final energy production.
#'Just filtering out the active modes energy service demand allocated by EDGE-Transport would lead to inconsistencies between both models:
#'E.g. REMIND would split the whole trn_pass energy service demand according to the FE shares and energy efficiencies reported by EDGE-T (without considering the active modes)
#' and would calculate a different FE value compared to EDGE-T that reserves a part of the trn_pass energy service demand
#'to the active modes.
#'
#'Solution: split and distribute the energy service demand of active modes across all technologies to calculate energy efficiency and capital cost per pkm.
#'Consequence: This leads to the fact that in REMIND energy service demand, energy efficiencies, an capital costs on technology level are rather abstract values that are not technically correct.
#'Hence, they should not be reported on technology level by REMIND (only by EDGE-T). The FE values on technology level are represented correctly as well as the energy service demand on CES node level.
#'The decision of the technology share is anyway made by EDGE-T and not by REMIND.
#'
#'
#' @param fleetESdemand energy service demand on fleet level
#' @param hybridElecShare share of electric driving for hybrid electric vehicles
#' @param timeResReporting time resolution reporting
#' @param demScen demand scenario
#' @param SSPscen SSP scenario
#' @param transportPolScen transport policy scenario
#' @param helpers list of helpers
#' @returns Energy service demand per technology in [trillion pkm/trillion tkm]
#' @export
#' @author Johanna Hoppe
#' @import data.table

reportToREMINDesDemand <- function(fleetESdemand, hybridElecShare, timeResReporting, demScen, SSPscen, transportPolScen, helpers) {

fleetESdemand <- copy(fleetESdemand)
fleetESdemand <- fleetESdemand[period %in% timeResReporting]
# convert billion pkm|tkm to trillion pkm|tkm
fleetESdemand[, value := value * 1e-3]
test <- fleetESdemand[, .(value = sum(value)), by = c("region", "period", "sector")]
test <- test[sector == "trn_pass"][, sector := NULL]
#split hybrid energy service demand
fleetESdemandWoHybrid <- copy(fleetESdemand)
hybrids <- fleetESdemandWoHybrid[technology == "Hybrid electric"]
hybrids[, value := hybridElecShare * value][, technology := "BEV"]
fleetESdemandWoHybrid[technology == "Hybrid electric", value := (1 - hybridElecShare) * value]
fleetESdemandWoHybrid[technology == "Hybrid electric", technology := "Liquids"]
fleetESdemandWoHybrid <- rbind(fleetESdemandWoHybrid, hybrids)
byCols <- names(fleetESdemandWoHybrid)
byCols <- byCols[!byCols %in% c("value")]
fleetESdemand <- fleetESdemandWoHybrid[, .(value = sum(value)), by = eval(byCols)]

# Split and distribute the active modes energy service demand to the technologies as
# they are not represented in REMIND (see explanation in function header)
demandMap <- unique(helpers$mapEdgeToREMIND[, c("all_teEs", "univocalName", "technology")])
demandMap <- demandMap[!is.na(all_teEs)]
activeModes <- fleetESdemand[grepl(".*Cycle|Walk.*", subsectorL1)]
REMINDesDemand <- fleetESdemand[!grepl(".*Cycle|Walk.*", subsectorL1)]
activeModes <- activeModes[, .(activeESdemand = sum(value)), by = c("region", "period", "sector")]
REMINDesDemand <- merge(REMINDesDemand, demandMap, by = c("univocalName", "technology"), all.x = TRUE) # nolint: object_name_linter
REMINDesDemand <- REMINDesDemand[, .(value = sum(value)), by = c("region", "period", "sector", "all_teEs")]
REMINDesDemand[, share := value / sum(value), by = c("region", "period", "sector")]
REMINDesDemand <- merge(REMINDesDemand, activeModes[, c("region", "period", "sector", "activeESdemand")], by = c("region", "period", "sector"), all.x = TRUE)
REMINDesDemand[sector == "trn_pass", value := value + share * activeESdemand][, c("activeESdemand", "sector", "share") := NULL]

test2 <- REMINDesDemand[grepl(".*pass_sm", all_teEs), .(value = sum(value)), by = c("region", "period")]
setkey(test, region, period)
setkey(test2, region, period)
if (!all.equal(test, test2)) stop("Something went wrong with the hybrids or active modes split in reportToREMINDesDemand()")
checkForNAsAndDups(REMINDesDemand, "REMINDesDemand", "reportToREMINDesDemand()")
REMINDesDemand <- prepareForREMIND(REMINDesDemand, demScen, SSPscen, transportPolScen)
setnames(REMINDesDemand, c("period", "region"), c("tall", "all_regi"))

return(REMINDesDemand)
}
Loading