From c727ea0245266fc6f8692e20e64f5b0f3db1a622 Mon Sep 17 00:00:00 2001 From: David Klein Date: Thu, 30 Apr 2026 18:22:45 +0200 Subject: [PATCH 1/6] Add air pollutant mapping mag to rem --- scripts/input/magpie.R | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts/input/magpie.R b/scripts/input/magpie.R index ba6a9f02d..b0cd18c99 100644 --- a/scripts/input/magpie.R +++ b/scripts/input/magpie.R @@ -40,6 +40,32 @@ mag2rem <- tibble::tribble( 'Emissions|CH4|Land|+|Peatland' , 'ch4peatland' , 1 , 'f_macBaseMagpie_coupling') +# Mapping of air pollutant emissions from MAgPIE names to REMIND names +# Unlike the variables above, the air pollutants are not imported into REMIND GAMS but directly to the reporting. +# Therefore, they wont be written to the gdx but to an extra file the reporting uses. +species <- c("BC","CO","NH3","NO2","OC","SO2","VOC") + +magAP <- c(paste0("Emissions|", species, "|AFOLU|Agriculture (Mt ", species, "/yr)"), + paste0("Emissions|", species, "|Land|Biomass Burning|+|Burning of Crop Residues (Mt ", species, "/yr)"), + paste0("Emissions|", species, "|Land|+|Peatland (Mt ", species, "/yr)"), + paste0("Emissions|", species, "|AFOLU|Land|Fires (Mt ", species, "/yr)"), + paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Forest Burning (Mt ", species, "/yr)"), + paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Grassland Burning (Mt ", species, "/yr)"), + paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Peat Burning (Mt ", species, "/yr)") + ) + +remAP <- c(paste0("Emi|", species, "|AFOLU|+|Agriculture (Mt ", species, "/yr)"), + paste0("Emi|", species, "|AFOLU|+|Agricultural Waste Burning (Mt ", species, "/yr)"), + paste0("Emi|", species, "|AFOLU|Land|+|Peatland (Mt ", species, "/yr)"), + paste0("Emi|", species, "|AFOLU|Land|+|Fires (Mt ", species, "/yr)"), + paste0("Emi|", species, "|AFOLU|Land|Fires|+|Forest Burning (Mt ", species, "/yr)"), + paste0("Emi|", species, "|AFOLU|Land|Fires|+|Grassland Burning (Mt ", species, "/yr)"), + paste0("Emi|", species, "|AFOLU|Land|Fires|+|Peat Burning (Mt ", species, "/yr)") + ) + +mappingAP <- tibble::tibble(mag = magAP, rem = remAP) + + # Delete entries in stack that contain needle and append new .setgdxcopy <- function(needle,stack,new){ matches <- grepl(needle,stack) From dbc839500f3afa34709c33c56a6156c7ca4ee50f Mon Sep 17 00:00:00 2001 From: David Klein Date: Fri, 22 May 2026 13:04:36 +0200 Subject: [PATCH 2/6] Write cm_LU_emi_scen to gdx because it is needed by the remind2::reportAirPollutantEmissions --- core/sets.gms | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/sets.gms b/core/sets.gms index 06c56998a..1c507bb35 100755 --- a/core/sets.gms +++ b/core/sets.gms @@ -22,6 +22,9 @@ c_model_version "model version" /%c_model_version%/ cm_GDPpopScen "cm_GDPpopScen as set for use in GDX" /%cm_GDPpopScen%/ cm_APssp "cm_APssp as set for use in GDX" /%cm_APssp%/ cm_APscen "cm_APscen as set for use in GDX" /%cm_APscen%/ +cm_LU_emi_scen "cm_LU_emi_scen as set for use in GDX" /%cm_LU_emi_scen%/ + + all_GDPpopScen "all possible GDP scenarios" / From 754c71e93c19a0899dd1bc9ed9eba74a016a95bf Mon Sep 17 00:00:00 2001 From: David Klein Date: Fri, 22 May 2026 13:10:05 +0200 Subject: [PATCH 3/6] Set cm_LU_emi_scen for SSP2_lowEn scenarios from SSP2_lowEn to SSP2. That means: for SSP2_lowEn scenarios use the landuse data from SSP2 (emissions, supply curves, costs). --- config/scenario_config.csv | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/scenario_config.csv b/config/scenario_config.csv index 0db8ed12d..f290d7a5d 100755 --- a/config/scenario_config.csv +++ b/config/scenario_config.csv @@ -35,11 +35,11 @@ SSP1-NPi2025;1,AMT;;;;;;;;;0;;;;;;;;;;;;2;;;;;;5;;;;;;;;;SSP1;SSP1;lowOil;lowGas SSP1-PkBudg750;1,AMT;;;;;rcp20;globallyOptimal;;;9;750;;functionalForm;;;100;2055;6;;EUR_regi 0.12, USA_regi 0.1, CHA_regi 0.1, CAZ_regi 0.1, JPN_regi 0.1, GLO 0.08;transformative;2;;;;;;5;;feelhpb 1.4, fehob 0.8, feheb 0.15;2050.GLO 0.9;;;;;;SSP1;SSP1;lowOil;lowGas;lowCoal;SSP1;;4;2;SSP1;;90;;forcing_SSP1;;Elec_Push;Mix4ICEban;1;2030;;SSP2-NPi2025;;;SSP1-PkBudg750: This climate policy scenario follows the Shared Socioeconomic Pathways 1 called Sustainability. The stylized climate policy scenario assumes a peak budget of 750 Gt CO2 on total CO2 emissions from 2015 to 2100. This is a 1.5C scenario, peak warming is allowed to be at or slightly above 1.5C at median climate sensitivity but returns to values below 1.5C in at least 67 % of scenarios by the end of the century. SSP1-PkBudg1000;1,AMT;;;;;rcp26;globallyOptimal;;;9;1000;;functionalForm;;;75;2080;7;;EUR_regi 0.12, USA_regi 0.1, CHA_regi 0.1, CAZ_regi 0.1, JPN_regi 0.1, GLO 0.08;transformative;2;;;;;;5;;;2050.GLO 0.5;;;;;;SSP1;SSP1;lowOil;lowGas;lowCoal;SSP1;;4;2;SSP1;;90;;forcing_SSP1;;;Mix3ICEban;1;2030;;SSP2-NPi2025;;;SSP1-PkBudg1050: This climate policy scenario follows the Shared Socioeconomic Pathways 1 called Sustainability. The stylized climate policy scenario assumes a peak budget of 1050 Gt CO2 on total CO2 emissions from 2015 to 2100. This is a well below 2C scenario at median climate sensitivity but returns to values below 2C in at least 67 % of scenarios during the whole century. # H12 SSP2 lowEnergy;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -SSP2_lowEn-NPi2025-calibrate;calibrate;calibrate;14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SSP2_lowEn;;;;;;;;SSP2_lowEn;;;;;;;;;2005;;;;;SSP2_lowEn-calibration: This baseline scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. This scenario also assumes low energy demand trajectories. -SSP2_lowEn-NDC;0;;;;;;globallyOptimal;;;3;;;NDC;;;;;;;EUR_regi 0.08, USA_regi 0.07, CHA_regi 0.07, CAZ_regi 0.07, JPN_regi 0.07, GLO 0.06;transformative;;;;;;;;;;;;;;;;;SSP2_lowEn;;;;;;;;SSP2_lowEn;;;;;;;;1;2030;;SSP2-NPi2025;SSP2-NPi2025;;SSP2_lowEn-NDC: This Nationally Determined Contribution (NDC) scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. It assumes NPi2025 until 2025 and has start year 2030. This scenario also assumes low energy demand trajectories. The NDC includes all pledged policies even if not yet implemented. It assumes that the moderate and heterogeneous climate ambition reflected in the NDCs at the begining of 2021 continues over the 21st century. -SSP2_lowEn-NPi2025;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;SSP2_lowEn;;;;;;;;SSP2_lowEn;;;;;;;;1;2030;;SSP2-NPi2025;;;SSP2_lowEn-NPi2025: This National Policies Implemented (NPi) scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. This scenario also assumes low energy demand trajectories. The NPi reflects currently implemented policies, while fulfilling near-term feasibility and long-term plausibility assessments. Emissions trajectories are aligned with bottom-up studies on the effect of currently implemented policies. Carbon prices are constant in the mid- and long-term. -SSP2_lowEn-PkBudg650;0;;;;;rcp20;globallyOptimal;;;9;650;;functionalForm;;;100;2055;6;;EUR_regi 0.08, USA_regi 0.07, CHA_regi 0.07, CAZ_regi 0.07, JPN_regi 0.07, GLO 0.06;transformative;;;;;;;;;;2050.GLO 0.9;;;;;;;SSP2_lowEn;;;;;;;2;SSP2_lowEn;;;;;;Elec_Push;Mix4ICEban;1;2030;;SSP2-NPi2025;;;SSP2_lowEn-PkBudg650: This climate policy scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. This scenario also assumes low energy demand trajectories. The stylized climate policy scenario assumes a peak budget of 650 Gt CO2 on total CO2 emissions from 2015 to 2100. This is a 1.5C scenario, peak warming is allowed to be at or slightly above 1.5C at median climate sensitivity but returns to values below 1.5C in at least 67 % of scenarios by the end of the century. -SSP2_lowEn-PkBudg1000;0;;;;;rcp26;globallyOptimal;;;9;1000;;functionalForm;;;75;2080;7;;EUR_regi 0.08, USA_regi 0.07, CHA_regi 0.07, CAZ_regi 0.07, JPN_regi 0.07, GLO 0.06;transformative;;;;;;;;;;2050.GLO 0.5;;;;;;;SSP2_lowEn;;;;;;;2;SSP2_lowEn;;;;;;;Mix3ICEban;1;2030;;SSP2-NPi2025;;;SSP2_lowEn-PkBudg1050: This climate policy scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. This scenario also assumes low energy demand trajectories. The stylized climate policy scenario assumes a peak budget of 1050 Gt CO2 on total CO2 emissions from 2015 to 2100. This is a well below 2C scenario at median climate sensitivity but returns to values below 2C in at least 67 % of scenarios during the whole century. +SSP2_lowEn-NPi2025-calibrate;calibrate;calibrate;14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SSP2_lowEn;;;;;;;;;;;;;;;;;2005;;;;;SSP2_lowEn-calibration: This baseline scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. This scenario also assumes low energy demand trajectories. +SSP2_lowEn-NDC;0;;;;;;globallyOptimal;;;3;;;NDC;;;;;;;EUR_regi 0.08, USA_regi 0.07, CHA_regi 0.07, CAZ_regi 0.07, JPN_regi 0.07, GLO 0.06;transformative;;;;;;;;;;;;;;;;;SSP2_lowEn;;;;;;;;;;;;;;;;1;2030;;SSP2-NPi2025;SSP2-NPi2025;;SSP2_lowEn-NDC: This Nationally Determined Contribution (NDC) scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. It assumes NPi2025 until 2025 and has start year 2030. This scenario also assumes low energy demand trajectories. The NDC includes all pledged policies even if not yet implemented. It assumes that the moderate and heterogeneous climate ambition reflected in the NDCs at the begining of 2021 continues over the 21st century. +SSP2_lowEn-NPi2025;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;SSP2_lowEn;;;;;;;;;;;;;;;;1;2030;;SSP2-NPi2025;;;SSP2_lowEn-NPi2025: This National Policies Implemented (NPi) scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. This scenario also assumes low energy demand trajectories. The NPi reflects currently implemented policies, while fulfilling near-term feasibility and long-term plausibility assessments. Emissions trajectories are aligned with bottom-up studies on the effect of currently implemented policies. Carbon prices are constant in the mid- and long-term. +SSP2_lowEn-PkBudg650;0;;;;;rcp20;globallyOptimal;;;9;650;;functionalForm;;;100;2055;6;;EUR_regi 0.08, USA_regi 0.07, CHA_regi 0.07, CAZ_regi 0.07, JPN_regi 0.07, GLO 0.06;transformative;;;;;;;;;;2050.GLO 0.9;;;;;;;SSP2_lowEn;;;;;;;2;;;;;;;Elec_Push;Mix4ICEban;1;2030;;SSP2-NPi2025;;;SSP2_lowEn-PkBudg650: This climate policy scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. This scenario also assumes low energy demand trajectories. The stylized climate policy scenario assumes a peak budget of 650 Gt CO2 on total CO2 emissions from 2015 to 2100. This is a 1.5C scenario, peak warming is allowed to be at or slightly above 1.5C at median climate sensitivity but returns to values below 1.5C in at least 67 % of scenarios by the end of the century. +SSP2_lowEn-PkBudg1000;0;;;;;rcp26;globallyOptimal;;;9;1000;;functionalForm;;;75;2080;7;;EUR_regi 0.08, USA_regi 0.07, CHA_regi 0.07, CAZ_regi 0.07, JPN_regi 0.07, GLO 0.06;transformative;;;;;;;;;;2050.GLO 0.5;;;;;;;SSP2_lowEn;;;;;;;2;;;;;;;;Mix3ICEban;1;2030;;SSP2-NPi2025;;;SSP2_lowEn-PkBudg1050: This climate policy scenario follows the Shared Socioeconomic Pathways 2 called Middle of the Road. This scenario also assumes low energy demand trajectories. The stylized climate policy scenario assumes a peak budget of 1050 Gt CO2 on total CO2 emissions from 2015 to 2100. This is a well below 2C scenario at median climate sensitivity but returns to values below 2C in at least 67 % of scenarios during the whole century. # H12 SSP5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SSP5-NPi2025-calibrate;calibrate;calibrate;14;;;;;;;;;;;;;;;;;;;1;1.75;;;;;3;2;;;;;;;;SSP5;SSP5;highOil;highGas;highCoal;SSP5;6;4;;SSP5;0.5;;;forcing_SSP5;;;;;2005;;;;;SSP5-calibration: This baseline calibration scenario follows the Shared Socioeconomic Pathways 5 called Fossil-Fueled Development. SSP5-NDC;0;;;;;;globallyOptimal;;;3;;;NDC;;;;;;;;;1;1.75;;;;;3;2;;;;;;;;SSP5;SSP5;highOil;highGas;highCoal;SSP5;6;4;;SSP5;0.5;;;forcing_SSP5;;;;1;2030;;SSP2-NPi2025;SSP2-NPi2025;;SSP5-NDC: This Nationally Determined Contribution (NDC) scenario follows the Shared Socioeconomic Pathways 5 called Fossil-Fueled Development. It assumes NPi2025 until 2025 and has start year 2030. The NDC includes all pledged policies even if not yet implemented. It assumes that the moderate and heterogeneous climate ambition reflected in the NDCs at the begining of 2021 continues over the 21st century. From 6e98cce92a757000d03d54862eb2772dc1332135 Mon Sep 17 00:00:00 2001 From: David Klein Date: Thu, 28 May 2026 15:54:40 +0200 Subject: [PATCH 4/6] Transfer MAgPIE air pollutants to REMIND in coupled runs --- scripts/input/magpie.R | 161 ++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 67 deletions(-) diff --git a/scripts/input/magpie.R b/scripts/input/magpie.R index b0cd18c99..dc0476195 100644 --- a/scripts/input/magpie.R +++ b/scripts/input/magpie.R @@ -1,6 +1,6 @@ # Mapping of MAgPIE variables to REMIND variables -# If you change the mapping, check whether the structure of the gdx object in “getMagpieData” (see below) needs to be adjusted. +# If you change the mapping, check whether the structure of the gdx object in ?getMagpieData? (see below) needs to be adjusted. mag2rem <- tibble::tribble( ~mag , ~enty , ~factorMag2Rem , ~parameter , 'Demand|Bioenergy|2nd generation|++|Bioenergy crops' , NA , 1/31.536 , 'pm_pebiolc_demandmag' , @@ -45,25 +45,29 @@ mag2rem <- tibble::tribble( # Therefore, they wont be written to the gdx but to an extra file the reporting uses. species <- c("BC","CO","NH3","NO2","OC","SO2","VOC") -magAP <- c(paste0("Emissions|", species, "|AFOLU|Agriculture (Mt ", species, "/yr)"), - paste0("Emissions|", species, "|Land|Biomass Burning|+|Burning of Crop Residues (Mt ", species, "/yr)"), - paste0("Emissions|", species, "|Land|+|Peatland (Mt ", species, "/yr)"), - paste0("Emissions|", species, "|AFOLU|Land|Fires (Mt ", species, "/yr)"), - paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Forest Burning (Mt ", species, "/yr)"), - paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Grassland Burning (Mt ", species, "/yr)"), - paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Peat Burning (Mt ", species, "/yr)") +magAP <- c(#paste0("Emissions|", species, "|AFOLU|Agriculture"), # exists for BC, CO, OC, SO2 and VOC only (exogenous in MAgPIE) + paste0("Emissions|", c("NH3", "NO2"), "|Land|+|Agriculture"), # exists for NH3, NO2 only (endogeous in MAgPIE) + paste0("Emissions|", species, "|Land|Biomass Burning|+|Burning of Crop Residues"), + #paste0("Emissions|", species, "|Land|+|Peatland"), + paste0("Emissions|", species, "|AFOLU|Land|Fires"), + paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Forest Burning"), + paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Grassland Burning"), + paste0("Emissions|", species, "|AFOLU|Land|Fires|+|Peat Burning") ) -remAP <- c(paste0("Emi|", species, "|AFOLU|+|Agriculture (Mt ", species, "/yr)"), - paste0("Emi|", species, "|AFOLU|+|Agricultural Waste Burning (Mt ", species, "/yr)"), - paste0("Emi|", species, "|AFOLU|Land|+|Peatland (Mt ", species, "/yr)"), - paste0("Emi|", species, "|AFOLU|Land|+|Fires (Mt ", species, "/yr)"), - paste0("Emi|", species, "|AFOLU|Land|Fires|+|Forest Burning (Mt ", species, "/yr)"), - paste0("Emi|", species, "|AFOLU|Land|Fires|+|Grassland Burning (Mt ", species, "/yr)"), - paste0("Emi|", species, "|AFOLU|Land|Fires|+|Peat Burning (Mt ", species, "/yr)") +remAP <- c(#paste0("Emi|", species, "|AFOLU|+|Agriculture"), + paste0("Emi|", c("NH3", "NO2"), "|AFOLU|+|Agriculture"), + paste0("Emi|", species, "|AFOLU|+|Agricultural Waste Burning"), + #paste0("Emi|", species, "|AFOLU|Land|+|Peatland"), + paste0("Emi|", species, "|AFOLU|Land|+|Fires"), + paste0("Emi|", species, "|AFOLU|Land|Fires|+|Forest Burning"), + paste0("Emi|", species, "|AFOLU|Land|Fires|+|Grassland Burning"), + paste0("Emi|", species, "|AFOLU|Land|Fires|+|Peat Burning") ) -mappingAP <- tibble::tibble(mag = magAP, rem = remAP) +mappingAP <- tibble::tibble(mag = magAP, enty = remAP, factorMag2Rem = 1, parameter = "AirPollutantsMAgPIE") + +mag2rem <- bind_rows(mag2rem, mappingAP) # Delete entries in stack that contain needle and append new @@ -172,44 +176,9 @@ runMAgPIE <- function(pathToRemindReport) { }) } -# Transfer coupling variables from MAgPIE report to magpieData.gdx read by REMIND between the Nash iterations -getMagpieData <- function(path_to_report = "report.mif", mapping) { - +# Write data to gdx +writeToGdx <- function(rem, mapping) { require(gamstransfer, quietly = TRUE, warn.conflicts = FALSE) - require(quitte, quietly = TRUE, warn.conflicts = FALSE) - require(dplyr, quietly = TRUE, warn.conflicts = FALSE) - require(readr, quietly = TRUE, warn.conflicts = FALSE) - - # ---- Record runtime when the data transfer from MAgPIE to REMIND starts in runtime.log ---- - - write(paste(format(Sys.time(), "%Y-%m-%d %H:%M:%S"), "getMagpieData", NashIteration, sep = ","), file = paste0("runtime.log"), append = TRUE) - message(round(Sys.time()), " Transferring MAgPIE data") - message(" from ", path_to_report) - message(" to ./magpieData.gdx") - - # ---- Read and prepare MAgPIE data ---- - - mag <- quitte::read.quitte(path_to_report, check.duplicates = FALSE) - - # Stop if variables are missing - variablesMissing <- ! mapping$mag %in% mag$variable - if (any(variablesMissing)) { - stop("The following variables defined in the coupling interface could not be found in the MAgPIE report: ", - mapping$mag[variablesMissing]) - } - - rem <- mag |> - inner_join(mapping, by = c("variable" = "mag"), # combine tables keeping relevant variables only - relationship = "many-to-one", # each row in x (mag) matches at most 1 row in y (mapping) - unmatched = c("drop", "error")) |> # drop rows from x that are not in y, error: all rows in y must be in x - mutate(value = value * factorMag2Rem) |> # apply unit conversion - group_by(period, region, enty, parameter) |> # define groups for summation - summarise(value = sum(value), .groups = "drop") |> # sum MAgPIE emissions (variable) that have the same enty in remind - rename(ttot = period, regi = region) |> # use REMIND set names - filter(, regi != "World", between(ttot, 2005, 2150)) |> # keep REMIND time horizon and remove World region - select(regi, ttot, enty, parameter, value) # keep only columns required for import to REMIND - - # ---- Start creating objects that will be written to gdx ---- # ---- Define SETS ---- @@ -231,9 +200,9 @@ getMagpieData <- function(path_to_report = "report.mif", mapping) { emiMacMagpie <- m$addSet( "emiMacMagpie", records = mapping |> - filter(parameter == "f_macBaseMagpie_coupling") |> - select(enty) |> - unique(), + filter(parameter == "f_macBaseMagpie_coupling") |> + select(enty) |> + unique(), description = "emission types coming from MAgPIE" ) @@ -243,9 +212,9 @@ getMagpieData <- function(path_to_report = "report.mif", mapping) { "f_macBaseMagpie_coupling", domain = c(ttot, regi, emiMacMagpie), records = rem |> - filter(parameter == "f_macBaseMagpie_coupling") |> - select(ttot, regi, enty, value) |> - rename(emiMacMagpie = enty), + filter(parameter == "f_macBaseMagpie_coupling") |> + select(ttot, regi, enty, value) |> + rename(emiMacMagpie = enty), description = "emissions from MAgPIE" ) @@ -253,17 +222,17 @@ getMagpieData <- function(path_to_report = "report.mif", mapping) { "p30_pebiolc_pricemag", domain = c(ttot, regi), records = rem |> - filter(parameter == "p30_pebiolc_pricemag") |> - select(ttot, regi, value), + filter(parameter == "p30_pebiolc_pricemag") |> + select(ttot, regi, value), description = "bioenergy price from MAgPIE" ) - + pm_pebiolc_demandmag <- m$addParameter( "pm_pebiolc_demandmag", domain = c(ttot, regi), records = rem |> - filter(parameter == "pm_pebiolc_demandmag") |> - select(ttot, regi, value), + filter(parameter == "pm_pebiolc_demandmag") |> + select(ttot, regi, value), description = "demand for bioenergy in MAgPIE from which the prices result" ) @@ -271,8 +240,8 @@ getMagpieData <- function(path_to_report = "report.mif", mapping) { "p26_totLUcost_coupling", domain = c(ttot, regi), records = rem |> - filter(parameter == "p26_totLUcost_coupling") %>% - select(ttot, regi, value), + filter(parameter == "p26_totLUcost_coupling") %>% + select(ttot, regi, value), description = "total production costs from MAgPIE without costs for GHG" ) @@ -281,6 +250,64 @@ getMagpieData <- function(path_to_report = "report.mif", mapping) { m$write("magpieData.gdx") } +# Write air pollutant emissions to file for reporting +writeAPToReporting <- function(rem, mapping) { + + # select only air pollutant emissions and write to file + rem <- rem |> + filter(parameter == "AirPollutantsMAgPIE") |> + mutate(enty = paste0(enty, " (", unit, ")")) |> + select(ttot, regi, enty, value) |> + write.table(file = "reporting/AirPollutantsMAgPIE.cs4r", + quote = FALSE, sep = ",", row.names = FALSE, col.names = FALSE) +} + +# Transfer coupling variables from MAgPIE report to magpieData.gdx read by REMIND between the Nash iterations +getMagpieData <- function(path_to_report = "report.mif", mapping) { + + require(quitte, quietly = TRUE, warn.conflicts = FALSE) + require(dplyr, quietly = TRUE, warn.conflicts = FALSE) + + # ---- Record runtime when the data transfer from MAgPIE to REMIND starts in runtime.log ---- + + write(paste(format(Sys.time(), "%Y-%m-%d %H:%M:%S"), "getMagpieData", NashIteration, sep = ","), file = paste0("runtime.log"), append = TRUE) + message(round(Sys.time()), " Transferring MAgPIE data") + message(" from ", path_to_report) + message(" to ./magpieData.gdx and ./reporting/AirPollutantsMAgPIE.cs4r") + + # ---- Read and prepare MAgPIE data ---- + + mag <- quitte::read.quitte(path_to_report, check.duplicates = FALSE) + + # Stop if variables are missing + variablesMissing <- ! mapping$mag %in% mag$variable + if (any(variablesMissing)) { + stop("The following variables defined in the coupling interface could not be found in the MAgPIE report: \n", + paste(mapping$mag[variablesMissing], collapse = "\n")) + } + + rem <- mag |> + inner_join(mapping, by = c("variable" = "mag"), # combine tables keeping relevant variables only + relationship = "many-to-one", # each row in x (mag) matches at most 1 row in y (mapping) + unmatched = c("drop", "error")) |> # drop rows from x that are not in y, error: all rows in y must be in x + mutate(value = value * factorMag2Rem) |> # apply unit conversion + group_by(period, region, enty, parameter, unit) |> # define groups for summation, include unit to keep it (needed for export of air pollutants to REMIND reporting) + summarise(value = sum(value), .groups = "drop") |> # sum MAgPIE emissions (variable) that have the same enty in remind + rename(ttot = period, regi = region) |> # use REMIND set names + filter(, regi != "World", between(ttot, 2005, 2150)) # keep REMIND time horizon and remove World region + + + # write data to gdx, that will be read by REMIND in the next Nash iteration. + # If you change the structure of rem, check whether writeToGdx needs to be adjusted. + # keep only columns required for import to REMIND + writeToGdx(rem |> select(regi, ttot, enty, parameter, value) , mapping) + + # write data to file, that will be read by remind2::reportAirPollutantEmissions in the reporting step. + # If you change the structure of rem, check whether reportAirPollutantEmissions needs to be adjusted. + writeAPToReporting(rem, mapping) + +} + # Obtain number of MAgPIE iteration and Nash iteration passed to this script by GAMS args <- commandArgs(trailingOnly = TRUE) i <- as.numeric(args[1]) @@ -325,7 +352,7 @@ if (is.null(cfg$continueFromHere) || NashIteration > 1) { # Write pathToMagpieReport to cfg, so reporting.R can find the MAgPIE report and append it to the REMIND reporting cfg$pathToMagpieReport <- pathToMagpieReport -# In any case transfer MAgPIE data from report to magpieData.gdx +# In any case transfer MAgPIE data from report to magpieData.gdx and AirPollutantsMAgPIE.cs4r getMagpieData(path_to_report = pathToMagpieReport, mapping = mag2rem) # Save the same elements that were loaded (they may have been updated in the meantime) From 863d332da8bb0e390c4fd1c529a1055a67b97193 Mon Sep 17 00:00:00 2001 From: David Klein Date: Thu, 28 May 2026 16:32:24 +0200 Subject: [PATCH 5/6] Rename MAgPIE's NO2 emissions to NOx when transferring it in the coupling --- scripts/input/magpie.R | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/input/magpie.R b/scripts/input/magpie.R index dc0476195..155c138c0 100644 --- a/scripts/input/magpie.R +++ b/scripts/input/magpie.R @@ -257,6 +257,7 @@ writeAPToReporting <- function(rem, mapping) { rem <- rem |> filter(parameter == "AirPollutantsMAgPIE") |> mutate(enty = paste0(enty, " (", unit, ")")) |> + mutate(enty = gsub("NO2", "NOx", enty)) |> select(ttot, regi, enty, value) |> write.table(file = "reporting/AirPollutantsMAgPIE.cs4r", quote = FALSE, sep = ",", row.names = FALSE, col.names = FALSE) From 3536a7f78560fb4997515d8bf88b0b88d1cd41f0 Mon Sep 17 00:00:00 2001 From: David Klein Date: Tue, 2 Jun 2026 18:27:38 +0200 Subject: [PATCH 6/6] Write cm_rcp_scen to gdx for remind2::reportExtraEmissions. Bugfix to magpie.R. Increase verbosity about appending the MAgPIE report to the REMIND report. --- core/sets.gms | 1 + scripts/input/magpie.R | 2 +- scripts/output/single/reporting.R | 37 ++++++++++++++++++------------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/core/sets.gms b/core/sets.gms index 1c507bb35..0190fc192 100755 --- a/core/sets.gms +++ b/core/sets.gms @@ -23,6 +23,7 @@ cm_GDPpopScen "cm_GDPpopScen as set for use in GDX" /%cm_GDPpopScen%/ cm_APssp "cm_APssp as set for use in GDX" /%cm_APssp%/ cm_APscen "cm_APscen as set for use in GDX" /%cm_APscen%/ cm_LU_emi_scen "cm_LU_emi_scen as set for use in GDX" /%cm_LU_emi_scen%/ +cm_rcp_scen "cm_rcp_scen as set for use in GDX" /%cm_rcp_scen%/ diff --git a/scripts/input/magpie.R b/scripts/input/magpie.R index 155c138c0..0309425bd 100644 --- a/scripts/input/magpie.R +++ b/scripts/input/magpie.R @@ -67,7 +67,7 @@ remAP <- c(#paste0("Emi|", species, "|AFOLU|+|Agriculture"), mappingAP <- tibble::tibble(mag = magAP, enty = remAP, factorMag2Rem = 1, parameter = "AirPollutantsMAgPIE") -mag2rem <- bind_rows(mag2rem, mappingAP) +mag2rem <- dplyr::bind_rows(mag2rem, mappingAP) # Delete entries in stack that contain needle and append new diff --git a/scripts/output/single/reporting.R b/scripts/output/single/reporting.R index 5b092c8ef..6b266d29c 100644 --- a/scripts/output/single/reporting.R +++ b/scripts/output/single/reporting.R @@ -128,22 +128,29 @@ envir <- new.env() load(file.path(outputdir, "config.Rdata"), envir = envir) magpie_reporting_file <- envir$cfg$pathToMagpieReport -if (!is.null(magpie_reporting_file) && file.exists(magpie_reporting_file)) { - message("### add MAgPIE reporting from ", magpie_reporting_file) - tmp_rem <- quitte::as.quitte(remind_reporting_file) - tmp_mag <- dplyr::filter(quitte::as.quitte(magpie_reporting_file), .data$period %in% unique(tmp_rem$period)) - # remove common variables from magpie reporting to avoid duplication - sharedvariables <- intersect(tmp_mag$variable, tmp_rem$variable) - if (length(sharedvariables) > 0) { - message("The following variables will be dropped from MAgPIE reporting because they are in REMIND reporting: ", - paste(sharedvariables, collapse = ", ")) - tmp_mag <- dplyr::filter(tmp_mag, !.data$variable %in% sharedvariables) + +if (!is.null(magpie_reporting_file)) { + if (file.exists(magpie_reporting_file)) { + message("### add MAgPIE reporting from ", magpie_reporting_file) + tmp_rem <- quitte::as.quitte(remind_reporting_file) + tmp_mag <- dplyr::filter(quitte::as.quitte(magpie_reporting_file), .data$period %in% unique(tmp_rem$period)) + # remove common variables from magpie reporting to avoid duplication + sharedvariables <- intersect(tmp_mag$variable, tmp_rem$variable) + if (length(sharedvariables) > 0) { + message("The following variables will be dropped from MAgPIE reporting because they are in REMIND reporting: ", + paste(sharedvariables, collapse = ", ")) + tmp_mag <- dplyr::filter(tmp_mag, !.data$variable %in% sharedvariables) + } + # Harmonize scenario names: use the REMIND scenario name also for MAgPIE + tmp_mag$scenario <- paste0(scenario) + tmp_rem_mag <- rbind(tmp_rem, tmp_mag) + quitte::write.mif(tmp_rem_mag, path = remind_reporting_file) + piamutils::deletePlus(remind_reporting_file, writemif = TRUE) + } else { + message("A path to a MAgPIE report was specified but the files cannot be found:", magpie_reporting_file) } - # Harmonize scenario names: use the REMIND scenario name also for MAgPIE - tmp_mag$scenario <- paste0(scenario) - tmp_rem_mag <- rbind(tmp_rem, tmp_mag) - quitte::write.mif(tmp_rem_mag, path = remind_reporting_file) - piamutils::deletePlus(remind_reporting_file, writemif = TRUE) +} else { + message("Since no path to a MAgPIE report was specified (which is normal for standalone runs), no MAgPIE report will be appended to the REMIND report.") } # warn if duplicates in mif and incorrect spelling of variables ----