From 33c2619b9cc9727aa144704d8c781f231f75586b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:40:34 +0000 Subject: [PATCH 01/11] Initial plan From b9ee05ee1dfac4222ccdd901a8ae34cde753e941 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:46:43 +0000 Subject: [PATCH 02/11] Automate calibration_results setup on CES calibration runs Co-authored-by: lecfab <1883023+lecfab@users.noreply.github.com> --- scripts/start/run.R | 21 ++++++++++ tests/testthat/test_01-run.R | 48 ++++++++++++++++++++++ tutorials/12_Calibrating_CES_Parameters.md | 6 ++- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/test_01-run.R diff --git a/scripts/start/run.R b/scripts/start/run.R index 147314bc8b..5485d6e847 100644 --- a/scripts/start/run.R +++ b/scripts/start/run.R @@ -5,6 +5,24 @@ # | REMIND License Exception, version 1.0 (see LICENSE file). # | Contact: remind@pik-potsdam.de +# Thin wrapper around the shell script; extracted for testability. +callSetLocalCalibration <- function(remind_folder) { + withr::with_dir(remind_folder, system("./scripts/utils/set-local-calibration.sh")) +} + +# Ensures calibration_results/ exists in remind_folder, running the setup +# script when it is absent. Only called for CES calibration runs. +ensureLocalCalibrationSetup <- function(remind_folder) { + caldir <- file.path(remind_folder, "calibration_results") + if (!dir.exists(caldir)) { + ret <- callSetLocalCalibration(remind_folder) + if (ret != 0) { + stop("Failed to set up calibration_results/ directory (exit code ", ret, "). ", + "Please run 'make set-local-calibration' manually.") + } + } +} + run <- function() { load("config.Rdata") @@ -36,6 +54,9 @@ run <- function() { } else if (cfg$gms$CES_parameters == "calibrate") { + # Set up calibration_results/ directory if it does not yet exist + ensureLocalCalibrationSetup(cfg$remind_folder) + # Remember file modification time of fulldata.gdx to see if it changed fulldata_m_time <- Sys.time(); diff --git a/tests/testthat/test_01-run.R b/tests/testthat/test_01-run.R new file mode 100644 index 0000000000..ad4502b6aa --- /dev/null +++ b/tests/testthat/test_01-run.R @@ -0,0 +1,48 @@ +# | (C) 2006-2024 Potsdam Institute for Climate Impact Research (PIK) +# | authors, and contributors see CITATION.cff file. This file is part +# | of REMIND and licensed under AGPL-3.0-or-later. Under Section 7 of +# | AGPL-3.0, you are granted additional permissions described in the +# | REMIND License Exception, version 1.0 (see LICENSE file). +# | Contact: remind@pik-potsdam.de + +test_that("ensureLocalCalibrationSetup calls setup script when calibration_results/ is absent", { + withr::with_tempdir({ + remind_folder <- getwd() + called <- FALSE + # Mock callSetLocalCalibration so no shell script is run + local_mocked_bindings( + callSetLocalCalibration = function(folder) { called <<- TRUE; 0L }, + .env = globalenv() + ) + ensureLocalCalibrationSetup(remind_folder) + expect_true(called, label = "callSetLocalCalibration should be invoked when calibration_results/ is absent") + }) +}) + +test_that("ensureLocalCalibrationSetup skips setup script when calibration_results/ already exists", { + withr::with_tempdir({ + remind_folder <- getwd() + dir.create(file.path(remind_folder, "calibration_results")) + called <- FALSE + local_mocked_bindings( + callSetLocalCalibration = function(folder) { called <<- TRUE; 0L }, + .env = globalenv() + ) + ensureLocalCalibrationSetup(remind_folder) + expect_false(called, label = "callSetLocalCalibration should NOT be invoked when calibration_results/ exists") + }) +}) + +test_that("ensureLocalCalibrationSetup stops on non-zero exit code from setup script", { + withr::with_tempdir({ + remind_folder <- getwd() + local_mocked_bindings( + callSetLocalCalibration = function(folder) 1L, + .env = globalenv() + ) + expect_error( + ensureLocalCalibrationSetup(remind_folder), + regexp = "make set-local-calibration" + ) + }) +}) diff --git a/tutorials/12_Calibrating_CES_Parameters.md b/tutorials/12_Calibrating_CES_Parameters.md index 124462ea1f..6f48daff19 100644 --- a/tutorials/12_Calibrating_CES_Parameters.md +++ b/tutorials/12_Calibrating_CES_Parameters.md @@ -140,8 +140,10 @@ all REMIND users), or used in a local directory (e.g. for project work). 1. Prepare the calibration directory: - To include calibration results in the PIK calibration repository, navigate to `/p/projects/remind/inputdata/CESparametersAndGDX/`. - - For use in a local directory, go to your local REMIND folder and type - `make set-local-calibration`. Navigate to the newly created folder `calibration_results/`. + - For use in a local directory, the `calibration_results/` directory is created + automatically when you start a CES calibration run. If you prefer to set it up + in advance, you can also run `make set-local-calibration` manually. + Navigate to the newly created folder `calibration_results/`. 2. Use the `collect_calibration` script with one ore more paths to the completed calibration run directories as a parameter, for instance: ```sh From 37ee7174cec76b3fe134eac4debfe630d9ce0089 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:57:05 +0000 Subject: [PATCH 03/11] Simplify: inline system call, remove test file, update tutorial wording Co-authored-by: lecfab <1883023+lecfab@users.noreply.github.com> --- scripts/start/run.R | 7 +--- tests/testthat/test_01-run.R | 48 ---------------------- tutorials/12_Calibrating_CES_Parameters.md | 7 ++-- 3 files changed, 4 insertions(+), 58 deletions(-) delete mode 100644 tests/testthat/test_01-run.R diff --git a/scripts/start/run.R b/scripts/start/run.R index 5485d6e847..19c93278b3 100644 --- a/scripts/start/run.R +++ b/scripts/start/run.R @@ -5,17 +5,12 @@ # | REMIND License Exception, version 1.0 (see LICENSE file). # | Contact: remind@pik-potsdam.de -# Thin wrapper around the shell script; extracted for testability. -callSetLocalCalibration <- function(remind_folder) { - withr::with_dir(remind_folder, system("./scripts/utils/set-local-calibration.sh")) -} - # Ensures calibration_results/ exists in remind_folder, running the setup # script when it is absent. Only called for CES calibration runs. ensureLocalCalibrationSetup <- function(remind_folder) { caldir <- file.path(remind_folder, "calibration_results") if (!dir.exists(caldir)) { - ret <- callSetLocalCalibration(remind_folder) + ret <- withr::with_dir(remind_folder, system("./scripts/utils/set-local-calibration.sh")) if (ret != 0) { stop("Failed to set up calibration_results/ directory (exit code ", ret, "). ", "Please run 'make set-local-calibration' manually.") diff --git a/tests/testthat/test_01-run.R b/tests/testthat/test_01-run.R deleted file mode 100644 index ad4502b6aa..0000000000 --- a/tests/testthat/test_01-run.R +++ /dev/null @@ -1,48 +0,0 @@ -# | (C) 2006-2024 Potsdam Institute for Climate Impact Research (PIK) -# | authors, and contributors see CITATION.cff file. This file is part -# | of REMIND and licensed under AGPL-3.0-or-later. Under Section 7 of -# | AGPL-3.0, you are granted additional permissions described in the -# | REMIND License Exception, version 1.0 (see LICENSE file). -# | Contact: remind@pik-potsdam.de - -test_that("ensureLocalCalibrationSetup calls setup script when calibration_results/ is absent", { - withr::with_tempdir({ - remind_folder <- getwd() - called <- FALSE - # Mock callSetLocalCalibration so no shell script is run - local_mocked_bindings( - callSetLocalCalibration = function(folder) { called <<- TRUE; 0L }, - .env = globalenv() - ) - ensureLocalCalibrationSetup(remind_folder) - expect_true(called, label = "callSetLocalCalibration should be invoked when calibration_results/ is absent") - }) -}) - -test_that("ensureLocalCalibrationSetup skips setup script when calibration_results/ already exists", { - withr::with_tempdir({ - remind_folder <- getwd() - dir.create(file.path(remind_folder, "calibration_results")) - called <- FALSE - local_mocked_bindings( - callSetLocalCalibration = function(folder) { called <<- TRUE; 0L }, - .env = globalenv() - ) - ensureLocalCalibrationSetup(remind_folder) - expect_false(called, label = "callSetLocalCalibration should NOT be invoked when calibration_results/ exists") - }) -}) - -test_that("ensureLocalCalibrationSetup stops on non-zero exit code from setup script", { - withr::with_tempdir({ - remind_folder <- getwd() - local_mocked_bindings( - callSetLocalCalibration = function(folder) 1L, - .env = globalenv() - ) - expect_error( - ensureLocalCalibrationSetup(remind_folder), - regexp = "make set-local-calibration" - ) - }) -}) diff --git a/tutorials/12_Calibrating_CES_Parameters.md b/tutorials/12_Calibrating_CES_Parameters.md index 6f48daff19..34639488df 100644 --- a/tutorials/12_Calibrating_CES_Parameters.md +++ b/tutorials/12_Calibrating_CES_Parameters.md @@ -140,10 +140,9 @@ all REMIND users), or used in a local directory (e.g. for project work). 1. Prepare the calibration directory: - To include calibration results in the PIK calibration repository, navigate to `/p/projects/remind/inputdata/CESparametersAndGDX/`. - - For use in a local directory, the `calibration_results/` directory is created - automatically when you start a CES calibration run. If you prefer to set it up - in advance, you can also run `make set-local-calibration` manually. - Navigate to the newly created folder `calibration_results/`. + - For use in a local directory, navigate to folder `calibration_results/`. It is + automatically created during CES calibration runs, or you can set it up manually + with `make set-local-calibration`. 2. Use the `collect_calibration` script with one ore more paths to the completed calibration run directories as a parameter, for instance: ```sh From bb393435aeeeb15d4d2cc4271dbf7c4194fd598e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:03:08 +0000 Subject: [PATCH 04/11] Inline calibration setup check; warn instead of stop on failure Co-authored-by: lecfab <1883023+lecfab@users.noreply.github.com> --- scripts/start/run.R | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/scripts/start/run.R b/scripts/start/run.R index 19c93278b3..3cc451a9f5 100644 --- a/scripts/start/run.R +++ b/scripts/start/run.R @@ -5,19 +5,6 @@ # | REMIND License Exception, version 1.0 (see LICENSE file). # | Contact: remind@pik-potsdam.de -# Ensures calibration_results/ exists in remind_folder, running the setup -# script when it is absent. Only called for CES calibration runs. -ensureLocalCalibrationSetup <- function(remind_folder) { - caldir <- file.path(remind_folder, "calibration_results") - if (!dir.exists(caldir)) { - ret <- withr::with_dir(remind_folder, system("./scripts/utils/set-local-calibration.sh")) - if (ret != 0) { - stop("Failed to set up calibration_results/ directory (exit code ", ret, "). ", - "Please run 'make set-local-calibration' manually.") - } - } -} - run <- function() { load("config.Rdata") @@ -50,7 +37,10 @@ run <- function() { } else if (cfg$gms$CES_parameters == "calibrate") { # Set up calibration_results/ directory if it does not yet exist - ensureLocalCalibrationSetup(cfg$remind_folder) + if (!dir.exists(file.path(cfg$remind_folder, "calibration_results"))) { + if (0 != withr::with_dir(cfg$remind_folder, system("./scripts/utils/set-local-calibration.sh"))) + warning("Could not set up calibration_results/ automatically. Please run 'make set-local-calibration' manually.") + } # Remember file modification time of fulldata.gdx to see if it changed fulldata_m_time <- Sys.time(); From 4e163bdf3b0e4b6d366cecc3037dc1d9de9ec181 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:16:44 +0000 Subject: [PATCH 05/11] Move calibration_results setup to start.R, before SLURM launch Co-authored-by: lecfab <1883023+lecfab@users.noreply.github.com> --- scripts/start/run.R | 6 ------ start.R | 4 ++++ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/start/run.R b/scripts/start/run.R index 3cc451a9f5..147314bc8b 100644 --- a/scripts/start/run.R +++ b/scripts/start/run.R @@ -36,12 +36,6 @@ run <- function() { } else if (cfg$gms$CES_parameters == "calibrate") { - # Set up calibration_results/ directory if it does not yet exist - if (!dir.exists(file.path(cfg$remind_folder, "calibration_results"))) { - if (0 != withr::with_dir(cfg$remind_folder, system("./scripts/utils/set-local-calibration.sh"))) - warning("Could not set up calibration_results/ automatically. Please run 'make set-local-calibration' manually.") - } - # Remember file modification time of fulldata.gdx to see if it changed fulldata_m_time <- Sys.time(); diff --git a/start.R b/start.R index 24a7c42517..f4febe2e3a 100755 --- a/start.R +++ b/start.R @@ -530,6 +530,10 @@ if (any(c("--reprepare", "--restart") %in% flags)) { errorsfound <- errorsfound + ! gcresult } else if (start_now) { if (errorsfound == 0) { + if (cfg$gms$CES_parameters == "calibrate" && !dir.exists("calibration_results")) { + if (0 != system("./scripts/utils/set-local-calibration.sh")) + warning("Could not set up calibration_results/ automatically. Please run 'make set-local-calibration' manually.") + } submit(cfg) } else { message(" Not started, as errors were found.") From c92ba5eea8f0cbeabe832f2277380a62704b925e Mon Sep 17 00:00:00 2001 From: lecfab Date: Mon, 23 Feb 2026 18:44:24 +0100 Subject: [PATCH 06/11] Automatise gdx creation for new calibration names --- scripts/start/prepare.R | 18 +++++------- start.R | 61 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/scripts/start/prepare.R b/scripts/start/prepare.R index 3ef6df060a..11cbff803b 100644 --- a/scripts/start/prepare.R +++ b/scripts/start/prepare.R @@ -182,17 +182,13 @@ prepare <- function() { replace_in_file('core/sets.gms',content,"MODULES",comment="***") ### ADD MODULE INFO IN SETS ############# END ######### - # copy right gdx file to the output folder - gdx_name <- paste0("config/gdx-files/", cfg$gms$cm_CES_configuration, ".gdx") - if (!file.copy(gdx_name, file.path(cfg$results_folder, 'input.gdx'))) { - errmsg <- 'Could not copy gdx file:\n ' - if (cfg$gms$CES_parameters == 'calibrate') { - errmsg <- paste0(errmsg, - 'Calibration requires a start point, so copy the gdx file with the closest configuration and paste it to:\n ') - } - stop(errmsg, gdx_name, '\n\n') - } else { - message('Copied ', gdx_name, ' to input.gdx') + # Retrieve appropriate gdx file + gdxConfig <- paste0("config/gdx-files/", cfg$gms$cm_CES_configuration, ".gdx") + gdxInput <- file.path(cfg$results_folder, "input.gdx") + if (file.copy(gdxConfig, gdxInput)) { + message("Copied: ", gdxConfig, "\n to: ", gdxInput) + } else { + stop("Could not copy gdx file:\n ", gdxConfig, "\n\n") } # choose which conopt files to copy diff --git a/start.R b/start.R index 24a7c42517..5b92780a2e 100755 --- a/start.R +++ b/start.R @@ -123,6 +123,11 @@ startedRuns <- 0 waitingRuns <- 0 modeltestRunsUsed <- 0 +nonStoppingError <- function(...) { # error display that still allows starting the other runs + message(red, "Error", NC, ": ", ...) + errorsfound <<- errorsfound + 1 # operator <<- ensures that the global variable errorsfound is modified +} + # Returns TRUE if 'fullname' ends with 'extension' (eg. if "C_SSP2-Base/fulldata.gdx" ends with "fulldata.gdx") # AND if the file given in 'fullname' exists. .isFileAndAvailable <- function(fullname, extension) { @@ -309,7 +314,7 @@ if (any(c("--reprepare", "--restart") %in% flags)) { } ###################### Loop over scenarios ############################### - + # Modify and save cfg for all runs for (scen in rownames(scenarios)) { @@ -346,6 +351,7 @@ if (any(c("--reprepare", "--restart") %in% flags)) { verboseGamsCompile = ! "--gamscompile" %in% flags || "--interactive" %in% flags) errorsfound <- sum(errorsfound, cfg$errorsfoundInConfigureCfg) cfg$errorsfoundInConfigureCfg <- NULL + # set optimization mode to testOneRegi, if specified as command line argument if (any(c("--quick", "--testOneRegi") %in% flags)) { cfg$description <- paste("testOneRegi:", cfg$description) @@ -378,6 +384,50 @@ if (any(c("--reprepare", "--restart") %in% flags)) { # abort on too long paths ---- cfg$gms$cm_CES_configuration <- calculate_CES_configuration(cfg, check = TRUE) + # offer to copy existing gdx when missing required calibration gdx (interactive mode only) ---- + if ("--interactive" %in% flags && cfg$gms$CES_parameters == "calibrate") { + gdxFolder <- "./config/gdx-files" + gdxConfig <- file.path(gdxFolder, paste0(cfg$gms$cm_CES_configuration, ".gdx")) + if (!file.exists(gdxConfig)) { + message("\nCalibration requires a starting gdx that does not exist:\n ", gdxConfig) + abortText <- paste0("Please copy the gdx file with the closest configuration and paste it to:\n ", gdxConfig, "\n") + + # List available gdx files + gdxFiles <- list.files(gdxFolder, pattern = "\\.gdx$", full.names = TRUE) + if (length(gdxFiles) == 0) { nonStoppingError(abortText) } + + # Prompt user to choose an existing gdx file + gdxClosest <- gdxFiles[which.min(adist(gdxConfig, gdxFiles))] # existing file with the closest name + abortOption <- paste0(crayon::red("ABORT"), ": you will then need to copy the gdx of your choice manually") + gdxFiles <- c(abortOption, gdxFiles) + gdxSelection <- gdxFiles[gms::chooseFromList( + ifelse(gdxFiles == gdxClosest, crayon::cyan(gdxFiles), gdxFiles), + type = "an existing gdx file that you would like to use", + userinfo = paste0("Leave empty to select existing gdx with ", crayon::cyan("most similar name")), + returnBoolean = TRUE, + multiple = FALSE + )] + + if (length(gdxSelection) == 0) { gdxSelection <- gdxClosest } # default option + if (gdxSelection == abortOption) { nonStoppingError(abortText) } # abort option + + if (file.copy(gdxSelection, gdxConfig)) { + message("Copied: ", gdxSelection, "\n to: ", gdxConfig, "\n") + + # Add the .gdx and .inc to list of possible names + addLine <- function(line, path = "files") { + if (!file.exists(path)) message(path, " does not exist, you may have to manually add ", line) + else if (!(line %in% readLines(path))) { + write(line, path, append = TRUE) + message("Added in ", path, " the line ", line) + } + } + addLine(paste0(cfg$gms$cm_CES_configuration, ".gdx"), path = file.path(gdxFolder, "files")) + addLine(paste0(cfg$gms$cm_CES_configuration, ".inc"), path = file.path("./modules/29_CES_parameters/load/input", "files")) + } + } + } + # =================== MAgPIE coupling =================== if (exists("scenarios_magpie")) { @@ -413,8 +463,7 @@ if (any(c("--reprepare", "--restart") %in% flags)) { cfg$files2export$start["input.gdx"] <- scenarios_magpie[scen, "continueFromHere"] message(" Continuing MAgPIE coupling from REMIND gdx ", scenarios_magpie[scen, "continueFromHere"]) } else { - message(red, "Error", NC, ": Could not find what is given in 'scenarios_magpie[scen, continueFromHere]': ", scenarios_magpie[scen, "continueFromHere"]) - errorsfound <- errorsfound + 1 + nonStoppingError("Could not find what is given in 'scenarios_magpie[scen, continueFromHere]': ", scenarios_magpie[scen, "continueFromHere"]) } } @@ -481,14 +530,12 @@ if (any(c("--reprepare", "--restart") %in% flags)) { # if no real file is given but a reference to another scenario (that has to run first) create path to the reference scenario #ghgprice_remindrun <- paste0(prefix_runname, scenarios_magpie[scen, "path_mif_ghgprice_land"], "-rem-", i) #path_mif_ghgprice_land <- file.path(path_remind, "output", ghgprice_remindrun, paste0("REMIND_generic_", ghgprice_remindrun, ".mif")) - message(red, "Error", NC, ": path_mif_ghgprice_land must be a path to an existing file and cannot reference another scenario by name currently: ", + nonStoppingError("path_mif_ghgprice_land must be a path to an existing file and cannot reference another scenario by name currently: ", scenarios_magpie[scen, "path_mif_ghgprice_land"]) - errorsfound <- errorsfound + 1 path_mif_ghgprice_land <- FALSE } else { - message(red, "Error", NC, ": path_mif_ghgprice_land neither an existing file nor a scenario that will be started: ", + nonStoppingError("path_mif_ghgprice_land is neither an existing file nor a scenario that will be started: ", scenarios_magpie[scen, "path_mif_ghgprice_land"]) - errorsfound <- errorsfound + 1 path_mif_ghgprice_land <- FALSE } cfg_mag$path_to_report_ghgprices <- path_mif_ghgprice_land From b3f27d98a6eecde0809c941b577dc48ff6cd6b74 Mon Sep 17 00:00:00 2001 From: lecfab Date: Tue, 24 Feb 2026 11:36:11 +0100 Subject: [PATCH 07/11] gdx calibration: improve error handling --- scripts/start/prepare.R | 5 +++- start.R | 65 ++++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/scripts/start/prepare.R b/scripts/start/prepare.R index 11cbff803b..559d91fdd3 100644 --- a/scripts/start/prepare.R +++ b/scripts/start/prepare.R @@ -188,7 +188,10 @@ prepare <- function() { if (file.copy(gdxConfig, gdxInput)) { message("Copied: ", gdxConfig, "\n to: ", gdxInput) } else { - stop("Could not copy gdx file:\n ", gdxConfig, "\n\n") + stop(ifelse (cfg$gms$CES_parameters == "calibrate", + "Calibration requires a starting gdx; please copy the gdx file with the closest configuration and paste it to:", + "Could not find gdx file:"), + "\n ", gdxConfig, "\n\n") } # choose which conopt files to copy diff --git a/start.R b/start.R index eb1426a7f1..854c294026 100755 --- a/start.R +++ b/start.R @@ -123,7 +123,7 @@ startedRuns <- 0 waitingRuns <- 0 modeltestRunsUsed <- 0 -nonStoppingError <- function(...) { # error display that still allows starting the other runs +nonStoppingError <- function(...) { # error display that increments the global errorsfound counter message(red, "Error", NC, ": ", ...) errorsfound <<- errorsfound + 1 # operator <<- ensures that the global variable errorsfound is modified } @@ -394,36 +394,38 @@ if (any(c("--reprepare", "--restart") %in% flags)) { # List available gdx files gdxFiles <- list.files(gdxFolder, pattern = "\\.gdx$", full.names = TRUE) - if (length(gdxFiles) == 0) { nonStoppingError(abortText) } - - # Prompt user to choose an existing gdx file - gdxClosest <- gdxFiles[which.min(adist(gdxConfig, gdxFiles))] # existing file with the closest name - abortOption <- paste0(crayon::red("ABORT"), ": you will then need to copy the gdx of your choice manually") - gdxFiles <- c(abortOption, gdxFiles) - gdxSelection <- gdxFiles[gms::chooseFromList( - ifelse(gdxFiles == gdxClosest, crayon::cyan(gdxFiles), gdxFiles), - type = "an existing gdx file that you would like to use", - userinfo = paste0("Leave empty to select existing gdx with ", crayon::cyan("most similar name")), - returnBoolean = TRUE, - multiple = FALSE - )] - - if (length(gdxSelection) == 0) { gdxSelection <- gdxClosest } # default option - if (gdxSelection == abortOption) { nonStoppingError(abortText) } # abort option - - if (file.copy(gdxSelection, gdxConfig)) { - message("Copied: ", gdxSelection, "\n to: ", gdxConfig, "\n") - - # Add the .gdx and .inc to list of possible names - addLine <- function(line, path = "files") { - if (!file.exists(path)) message(path, " does not exist, you may have to manually add ", line) - else if (!(line %in% readLines(path))) { - write(line, path, append = TRUE) - message("Added in ", path, " the line ", line) + if (length(gdxFiles) == 0) { + nonStoppingError(abortText) + } else { + # Prompt user to choose an existing gdx file + gdxClosest <- gdxFiles[which.min(adist(basename(gdxConfig), basename(gdxFiles)))] # existing file with the closest name + abortOption <- paste0(crayon::red("ABORT"), ": you will then need to copy the gdx of your choice manually") + gdxFiles <- c(abortOption, gdxFiles) + gdxSelection <- gdxFiles[gms::chooseFromList( + ifelse(gdxFiles == gdxClosest, crayon::cyan(gdxFiles), gdxFiles), + type = "an existing gdx file that you would like to use", + userinfo = paste0("Leave empty to select existing gdx with ", crayon::cyan("most similar name")), + returnBoolean = TRUE, + multiple = FALSE + )] + + if (length(gdxSelection) == 0) { gdxSelection <- gdxClosest } # default option + if (gdxSelection == abortOption || !file.copy(gdxSelection, gdxConfig)) { # abort option or copy failure + nonStoppingError(abortText) + } else { + message("Copied: ", gdxSelection, "\n to: ", gdxConfig, "\n") + + # Add the .gdx and .inc to list of possible names + addLine <- function(line, path = "files") { + if (!file.exists(path)) message(path, " does not exist, you may have to manually add ", line) + else if (!(line %in% readLines(path))) { + write(line, path, append = TRUE) + message("Added in ", path, " the line ", line) + } } + addLine(paste0(cfg$gms$cm_CES_configuration, ".gdx"), path = file.path(gdxFolder, "files")) + addLine(paste0(cfg$gms$cm_CES_configuration, ".inc"), path = file.path("./modules/29_CES_parameters/load/input", "files")) } - addLine(paste0(cfg$gms$cm_CES_configuration, ".gdx"), path = file.path(gdxFolder, "files")) - addLine(paste0(cfg$gms$cm_CES_configuration, ".inc"), path = file.path("./modules/29_CES_parameters/load/input", "files")) } } } @@ -578,8 +580,11 @@ if (any(c("--reprepare", "--restart") %in% flags)) { } else if (start_now) { if (errorsfound == 0) { if (cfg$gms$CES_parameters == "calibrate" && !dir.exists("calibration_results")) { - if (0 != system("./scripts/utils/set-local-calibration.sh")) + if (0 == system("./scripts/utils/set-local-calibration.sh")) { + message("Folder calibration_results/ has been automatically set up.") + } else { warning("Could not set up calibration_results/ automatically. Please run 'make set-local-calibration' manually.") + } } submit(cfg) } else { From 6ae8614988c9225b9bdaad19897c17ea1a3257f6 Mon Sep 17 00:00:00 2001 From: lecfab Date: Tue, 24 Feb 2026 14:54:54 +0100 Subject: [PATCH 08/11] update calibration tutorial --- tutorials/12_Calibrating_CES_Parameters.md | 125 +++++++++++---------- 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/tutorials/12_Calibrating_CES_Parameters.md b/tutorials/12_Calibrating_CES_Parameters.md index 34639488df..6b1da20070 100644 --- a/tutorials/12_Calibrating_CES_Parameters.md +++ b/tutorials/12_Calibrating_CES_Parameters.md @@ -1,6 +1,7 @@ # Calibrating CES Parameters -## CES Production Function Basics +## Theory +### CES Production Function Basics REMIND uses a nested CES production function of the form @@ -27,7 +28,9 @@ Therefore, > **Anytime either the REMIND model or the input data change in a way that affects the results of a baseline scenario, that baseline scenario needs to be calibrated.** -## Iterative Calibration + + +### Iterative Calibration As it is not possible to calculate $n$ parameters $\alpha_i$ from a single equation, we use an iterative approach. The @@ -61,37 +64,34 @@ $\pi_i^{(j)} = \alpha_i^{(j)} {V_i^{(j)}}^{\rho_o - 1} {V_o^{(j)}}^{1 - \rho_o}$ $\alpha_i^{(j+1)} = \pi_i^{(j)} \left(\frac{V_i^\ast}{V_o^\ast}\right)^{1 - \rho_o}$ -## Requirements - -For the calibration process to work, we need -1. trajectories for all primary production factors (`ppf`, final energy and - energy service demands, labour, capital) and the output (GDP), and -2. the previous iterations' `ppf` prices. +## Practice +### Requirements and settings -Trajectories under (1) come from the input files `./core/input/f_gdp.cs3r`, -`./core/input/f_pop.cs3r`, -`./modules/29_CES_parameters/calibrate/input/f29_capitalQuantity.cs4r`, and -`./core/input/f_fedemand.cs4r` which are generated automatically as part of the -input data generation and always present. +For the calibration process to work, we need both: -Prices under (2) are calculated using the `input.gdx` provided to the -calibration run. User intervention is only required when prices cannot be -derived from the `.gdx` file in case of a change in the CES structure: -when nodes have been added or removed from the CES tree -(technically when the set `cesOut2cesIn` differs between `input.gdx` and the -current calibration run), or seldom in cases of convergence problems. +1. Trajectories for all primary production factors (`ppf`: final energy and energy service demands, + labour, capital) and the output (GDP). They come from input files that are automatically + generated as part of the input data generation: + - `./core/input/f_gdp.cs3r` + - `./core/input/f_pop.cs3r` + - `./modules/29_CES_parameters/calibrate/input/f29_capitalQuantity.cs4r` + - `./core/input/f_fedemand.cs4r` +2. Prices of `ppf` at the previous iteration. They are calculated using the `input.gdx` provided to + the calibration run. User intervention is only required when prices cannot be derived from the `.gdx` + file in case of a change in the CES structure: when nodes have been added or removed from the CES tree + (technically when the set `cesOut2cesIn` differs between `input.gdx` and the current calibration run), + or seldom in cases of convergence problems. -## Settings -To set up a CES calibration run, simply set module 29 `CES_parameters` to the -`calibration` realisation. All data relevant to the calibration is configured -according to the selected scenario configuration. Keep them identical to the -baseline scenario you want to calibrate. -If your calibration depends on new input data, you need to update the configuration. -Use the command `lastrev` to find the latest input data revisions on the cluster. -If your choice is _rev1.23abc_, update the following line of the configuration file +To set up a CES calibration run, simply set module 29 `CES_parameters` to the `calibrate` realisation. +All data relevant to the calibration is configured according to the selected scenario configuration; +keep them identical to the baseline scenario you want to calibrate. +If your calibration depends on new input data (for instance an update in `mrremind`), +you need to update the configuration. +Use the command `lastrev` to find the latest input data revisions on the cluster; +if your choice is _rev1.23abc_, update the following line of the configuration file `./config/default.cfg` (without _rev_ and always with quotation marks): ```R @@ -122,47 +122,50 @@ The calibration can further be adjusted using the following switches: Can help finding a suitable value for `cm_CES_calibration_default_price`. -## Results -The CES calibration outputs a `.gdx` file and an `.inc` file with all -the CES parameters. Their long name, for instance -`indu_subsectors-buil_simple-tran_edge_esm-GDPpop_SSP2-En_SSP2-Kap_debt_limit-Reg_62eff8f7` -indicates the CES configuration, the GDP/population scenarios, the capital market -module realisation and the [region configuration](17_Regions.md) -(_62eff8f7_ for H12, _2b1450bc_ for EU21). -You don't need to change these names, they are matched automatically using the -switch `cm_CES_configuration`. The parameter files also include a counter for +### Results + +The CES calibration outputs a `.gdx` file and an `.inc` file containing all the CES parameters. +The long name of these files indicates the CES configuration, the GDP/population scenarios, +the capital market module realisation, and the [region configuration](17_Regions.md) +(_62eff8f7_ for H12, _2b1450bc_ for EU21), for instance: +`indu_subsectors-buil_simple-tran_edge_esm-GDPpop_SSP2-En_SSP2-Kap_debt_limit-Reg_62eff8f7`. +Do not change these names, as they are matched automatically with the +switch `cm_CES_configuration`. The parameter files also include a counter for the calibration iteration they resulted from (e.g. `_ITERATION_10.inc`). -Calibration results can be included the PIK calibration repository (for use by -all REMIND users), or used in a local directory (e.g. for project work). +Calibration results can be included in the PIK calibration repository (for use by +all REMIND users) or used in a local directory (for project work): 1. Prepare the calibration directory: - To include calibration results in the PIK calibration repository, navigate to - `/p/projects/remind/inputdata/CESparametersAndGDX/`. - - For use in a local directory, navigate to folder `calibration_results/`. It is - automatically created during CES calibration runs, or you can set it up manually + `/p/projects/remind/inputdata/CESparametersAndGDX/`. + - For use in a local directory, navigate to the folder `calibration_results/`. This folder + is automatically created during CES calibration runs, or you can set it up manually with `make set-local-calibration`. -2. Use the `collect_calibration` script with one ore more paths to the completed - calibration run directories as a parameter, for instance: - ```sh - ./collect_calibration /p/tmp/username/Remind/output/SSP2-calibrate_2024-12-31_23.59.59/ - ``` - Note that an absolute or relative path may be used. - The script copies the necessary `.inc` and `.gdx` files to the repository - (adjusting the file names as needed), stages and commits them; you may review and - modify the commit message before committing (or abort with `:cq` in vim). - The script then generates a `.tgz` archive, which is what REMIND will be looking for - in order to run. Finally it displays the commit hash and offers to include it as the - `CESandGDXrevision` in the REMIND configuration. -3. If the specific calibration settings (`cm_CES_configuration`) have never been -calibrated and used in REMIND before, add the name of the `.gdx` file to `./config/gdx-files/files` -and add the name of the `.inc` file to `./modules/29_CES_parameters/load/input/files`, so that the -new calibration results are copied into these directories during run setup. - - - -## Diagnostic and validity + +2. Use the `collect_calibration` script with one or more paths to the completed + calibration run directories as a parameter. For instance: + ```sh + ./collect_calibration /p/tmp/username/Remind/output/SSP2-calibrate_2024-12-31_23.59.59/ + ``` + Note that both absolute and relative paths may be used. + The script copies the necessary `.inc` and `.gdx` files to `calibration_results/`, + adjusting the file names as needed, stages and commits them (you may review and + modify the commit message, then commit with `:wq` or abort with `:cq`). + The script then displays the commit hash and offers to include it as the + `CESandGDXrevision` in the REMIND configuration, so that the next REMIND runs are + able to find the `.tgz` archive containing the calibration files. + +3. New calibration files are automatically copied from the `.tgz` archive to the relevant directories + during the run setup. This requires the calibration name `cm_CES_configuration` to be listed in + the following files (which happens automatically if you ran the calibration yourself): + - `./config/gdx-files/files` (for the `.gdx` file) + - `./modules/29_CES_parameters/load/input/files` (for the `.inc` file) + + + +### Diagnostic and validity To diagnose the outputs, you may use the `full.log` and `full.lst` files for each calibration iteration (`full_01.log` …), the file `CES_calibration.csv` From 7d117ed361876b2be9c67ae32368e32fcf39761e Mon Sep 17 00:00:00 2001 From: lecfab Date: Tue, 3 Mar 2026 16:03:59 +0100 Subject: [PATCH 09/11] Add calibration_results path to cfg when setting up local calibration automatically --- scripts/utils/set-local-calibration.sh | 10 +++++----- start.R | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/utils/set-local-calibration.sh b/scripts/utils/set-local-calibration.sh index 051bc91199..a5c256c760 100755 --- a/scripts/utils/set-local-calibration.sh +++ b/scripts/utils/set-local-calibration.sh @@ -12,7 +12,7 @@ grep -q "^$caldir/$" .gitignore || echo "$caldir/" >> .gitignore cd "$caldir" -git init > /dev/null +git init -b main > /dev/null cp ../scripts/utils/set-local-calibration/collect_calibration ./ cp ../scripts/utils/set-local-calibration/gitignore .gitignore chmod u+x collect_calibration @@ -24,10 +24,10 @@ cp ../scripts/utils/set-local-calibration/pre-commit .git/hooks cp ../scripts/utils/set-local-calibration/post-commit .git/hooks chmod u+x .git/hooks/pre-commit .git/hooks/post-commit -cd "$OLDPWD" - # create additional .Rprofile (sourced through default .Rprofile) echo -e "options(remind_repos = c(\n" \ " getOption(\"remind_repos\"),\n" \ - " stats::setNames(list(x = NULL), \"$PWD/$caldir/\")))" \ - > calibration_results/.Rprofile_calibration_results + " stats::setNames(list(x = NULL), \"$PWD\")))" \ + > .Rprofile_calibration_results + +cd "$OLDPWD" diff --git a/start.R b/start.R index 854c294026..2f23a6e52c 100755 --- a/start.R +++ b/start.R @@ -579,11 +579,13 @@ if (any(c("--reprepare", "--restart") %in% flags)) { errorsfound <- errorsfound + ! gcresult } else if (start_now) { if (errorsfound == 0) { - if (cfg$gms$CES_parameters == "calibrate" && !dir.exists("calibration_results")) { + caldir <- "calibration_results/" + if (cfg$gms$CES_parameters == "calibrate" && !dir.exists(caldir)) { if (0 == system("./scripts/utils/set-local-calibration.sh")) { - message("Folder calibration_results/ has been automatically set up.") + cfg$repositories <- append(cfg$repositories, setNames(list(NULL), normalizePath(caldir))) + message(" Folder ", caldir, " has been automatically set up.") } else { - warning("Could not set up calibration_results/ automatically. Please run 'make set-local-calibration' manually.") + warning(" Could not set up ", caldir, " automatically. Please run 'make set-local-calibration' manually.") } } submit(cfg) From 2c491ef07169edb0b3dcd1a02009966a12178e30 Mon Sep 17 00:00:00 2001 From: lecfab Date: Tue, 3 Mar 2026 17:24:49 +0100 Subject: [PATCH 10/11] ensuring that all runs starting simultaneously have the right cfg --- start.R | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/start.R b/start.R index 2f23a6e52c..b80906047b 100755 --- a/start.R +++ b/start.R @@ -332,8 +332,7 @@ if (any(c("--reprepare", "--restart") %in% flags)) { cfg$gms$optimization <- "testOneRegi" cfg$output <- NA cfg$results_folder <- paste0("output/", cfg$title) - # delete existing Results directory - cfg$force_replace <- TRUE + cfg$force_replace <- TRUE # delete existing Results directory if (testOneRegi_region != "") cfg$gms$c_testOneRegi_region <- testOneRegi_region } if ("--quick" %in% flags) { @@ -394,9 +393,7 @@ if (any(c("--reprepare", "--restart") %in% flags)) { # List available gdx files gdxFiles <- list.files(gdxFolder, pattern = "\\.gdx$", full.names = TRUE) - if (length(gdxFiles) == 0) { - nonStoppingError(abortText) - } else { + if (length(gdxFiles) > 0) { # length is zero when input data has not been collected yet and copying gdx is impossible # Prompt user to choose an existing gdx file gdxClosest <- gdxFiles[which.min(adist(basename(gdxConfig), basename(gdxFiles)))] # existing file with the closest name abortOption <- paste0(crayon::red("ABORT"), ": you will then need to copy the gdx of your choice manually") @@ -582,8 +579,9 @@ if (any(c("--reprepare", "--restart") %in% flags)) { caldir <- "calibration_results/" if (cfg$gms$CES_parameters == "calibrate" && !dir.exists(caldir)) { if (0 == system("./scripts/utils/set-local-calibration.sh")) { - cfg$repositories <- append(cfg$repositories, setNames(list(NULL), normalizePath(caldir))) message(" Folder ", caldir, " has been automatically set up.") + cfg$repositories <- append(cfg$repositories, setNames(list(NULL), normalizePath(caldir))) + source(file.path(caldir, .Rprofile_calibration_results)) } else { warning(" Could not set up ", caldir, " automatically. Please run 'make set-local-calibration' manually.") } From ed7a04484df16f818e8e206e7f2e609d5fe704e6 Mon Sep 17 00:00:00 2001 From: lecfab Date: Tue, 3 Mar 2026 17:36:01 +0100 Subject: [PATCH 11/11] forgot quotes --- start.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start.R b/start.R index b80906047b..ff2eb5416d 100755 --- a/start.R +++ b/start.R @@ -581,7 +581,7 @@ if (any(c("--reprepare", "--restart") %in% flags)) { if (0 == system("./scripts/utils/set-local-calibration.sh")) { message(" Folder ", caldir, " has been automatically set up.") cfg$repositories <- append(cfg$repositories, setNames(list(NULL), normalizePath(caldir))) - source(file.path(caldir, .Rprofile_calibration_results)) + source(file.path(caldir, ".Rprofile_calibration_results")) } else { warning(" Could not set up ", caldir, " automatically. Please run 'make set-local-calibration' manually.") }