diff --git a/.Rbuildignore b/.Rbuildignore index 4c1e99a..a318840 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -3,3 +3,9 @@ ^Makefile$ ^workflow$ ^.*CITATION.cff$ +^.*\.Rproj$ +^\.Rproj\.user$ +^.lintr$ +^tests/.lintr$ +^codecov\.yml$ +^\.github$ diff --git a/.buildlibrary b/.buildlibrary index 3d84775..ed81782 100644 --- a/.buildlibrary +++ b/.buildlibrary @@ -1,7 +1,10 @@ -ValidationKey: '39524' +ValidationKey: '198020' AutocreateReadme: yes AcceptedWarnings: - 'Warning: package ''.*'' was built under R version' - 'Warning: namespace ''.*'' is not available and has been replaced' -AcceptedNotes: ~ -allowLinterWarnings: no +AcceptedNotes: +- no visible binding for global variable +- 'Found the following hidden files and directories:' +allowLinterWarnings: yes +enforceVersionUpdate: no diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 870f216..46f518a 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -49,6 +49,11 @@ jobs: shell: Rscript {0} run: lucode2:::validkey(stopIfInvalid = TRUE) + - name: Verify that lucode2::buildLibrary was successful + if: github.event_name == 'pull_request' + shell: Rscript {0} + run: lucode2:::isVersionUpdated() + - name: Checks shell: Rscript {0} run: | diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bcf5ae9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.Rproj.user +*.Rhistory +*.RData +*.Ruserdata +*.html +.Rproj.user diff --git a/.lintr b/.lintr new file mode 100644 index 0000000..c02af8f --- /dev/null +++ b/.lintr @@ -0,0 +1,2 @@ +linters: lucode2::lintrRules() +encoding: "UTF-8" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2f13466..243f46a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: - id: mixed-line-ending - repo: https://github.com/lorenzwalthert/precommit - rev: v0.3.2.9025 + rev: v0.4.0 hooks: - id: parsable-R - id: deps-in-desc diff --git a/CITATION.cff b/CITATION.cff index 5a9e975..c741838 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,14 +2,16 @@ cff-version: 1.2.0 message: If you use this software, please cite it using the metadata from this file. type: software title: 'piamValidation: Validation Tools for PIK-PIAM' -version: 0.0.2 -date-released: '2024-02-09' +version: 0.1.0 +date-released: '2024-03-20' abstract: The piamValidation package provides validation tools for the Potsdam Integrated Assessment Modelling environment. authors: - family-names: Weigmann given-names: Pascal email: pascal.weigmann@pik-potsdam.de +- family-names: Richters + given-names: Oliver license: LGPL-3.0 repository-code: https://github.com/pik-piam/piamValidation diff --git a/DESCRIPTION b/DESCRIPTION index 5641a1b..f37af56 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,14 +1,30 @@ Type: Package Package: piamValidation Title: Validation Tools for PIK-PIAM -Version: 0.0.2 -Date: 2024-02-09 +Version: 0.1.0 +Date: 2024-03-20 Authors@R: - person("Pascal", "Weigmann", , "pascal.weigmann@pik-potsdam.de", role = c("aut", "cre")) + c(person("Pascal", "Weigmann",, "pascal.weigmann@pik-potsdam.de", role = c("aut", "cre")), + person("Oliver", "Richters",, role = "aut")) Description: The piamValidation package provides validation tools for the Potsdam Integrated Assessment Modelling environment. License: LGPL-3 URL: https://github.com/pik-piam/piamValidation +Imports: + devtools, + dplyr (>= 1.1.1), + ggplot2, + ggthemes, + htmltools, + knitr, + plotly, + quitte (>= 0.3123.0), + readxl, + tibble, + tidyr Suggests: - testthat + testthat, + remind2 Encoding: UTF-8 RoxygenNote: 7.3.1 +Depends: + R (>= 2.10) diff --git a/NAMESPACE b/NAMESPACE index 6ae9268..ff53516 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,2 +1,17 @@ # Generated by roxygen2: do not edit by hand +export(validateScenarios) +export(validationHeatmap) +export(validationPass) +import(ggplot2) +importFrom(dplyr,"%>%") +importFrom(dplyr,filter) +importFrom(dplyr,group_by) +importFrom(dplyr,mutate) +importFrom(dplyr,select) +importFrom(dplyr,summarise) +importFrom(ggthemes,theme_tufte) +importFrom(plotly,ggplotly) +importFrom(readxl,excel_sheets) +importFrom(readxl,read_excel) +importFrom(utils,read.csv2) diff --git a/R/appendTooltips.R b/R/appendTooltips.R new file mode 100644 index 0000000..4426552 --- /dev/null +++ b/R/appendTooltips.R @@ -0,0 +1,96 @@ +# construct tooltips for interactive plots +appendTooltips <- function(df) { + + df$text <- NA + region <- period <- NULL + + # historic - relative + df[df$category == "historic" & df$metric == "relative", ] <- + df[df$category == "historic" & df$metric == "relative", ] %>% + mutate(text = paste0(.data$region, "\n", + .data$period, "\n", + "Value: ", round(value, 2), "\n", + "Ref_Value: ", round(ref_value, 2), "\n", + "Ref_Source: ", ref_model, "\n", + "Deviation:", round(check_value)*100, "%\n", + "Thresholds (yel/red): \n", + paste0("Max: ", max_yel*100, "% / ", max_red*100, "%") + ) + ) + + # historic - difference + df[df$category == "historic" & df$metric == "difference", ] <- + df[df$category == "historic" & df$metric == "difference", ] %>% + mutate(text = paste0(region, "\n", + period, "\n", + "Value: ", round(value, 2), "\n", + "Ref_Value: ", round(ref_value, 2), "\n", + "Ref_Source: ", ref_model, "\n", + "Thresholds (yel/red): \n", + "Max: ", max_yel, " / ", max_red + ) + ) + + # scenario - relative + df[df$category == "scenario" & df$metric == "relative", ] <- + df[df$category == "scenario" & df$metric == "relative", ] %>% + mutate(text = paste0(region, "\n", + period, "\n", + ifelse(!is.na(ref_model), + paste("Model:", model, "\n"), + ""), + "Value: ", round(value, 2), "\n", + "Ref ", ifelse(!is.na(ref_period), + paste("Period:", ref_period), + ifelse(!is.na(ref_scenario), + paste("Scenario:", ref_scenario), + paste("Model:", ref_model)) + ), "\n", + "Ref Value: ", round(ref_value, 2), "\n", + "Rel Deviation: ", round(check_value, 2)*100, "% \n", + "Thresholds (yel/red): \n", + "Min: ", min_yel*100, "% / ", min_red*100,"% \n", + "Max: ", max_yel*100, "% / ", max_red*100, "%") + ) + + # scenario - difference + df[df$category == "scenario" & df$metric == "difference", ] <- + df[df$category == "scenario" & df$metric == "difference", ] %>% + mutate(text = paste0(region, "\n", + period, "\n", + "Value: ", round(value, 2), "\n", + "Ref Value: ", round(ref_value, 2), "\n", + "Difference: ", round(check_value, 2), "\n", + "Thresholds (yel/red): \n", + "Min: ", min_yel, " / ", min_red,"\n", + "Max: ", max_yel, " / ", max_red + ) + ) + + # scenario - absolute + df[df$category == "scenario" & df$metric == "absolute", ] <- + df[df$category == "scenario" & df$metric == "absolute", ] %>% + mutate(text = paste0(region, "\n", + period, "\n", + "Value: ", round(value, 2), "\n", + "Thresholds (yel/red): \n", + "Min: ", min_yel, " / ", min_red,"\n", + "Max: ", max_yel, " / ", max_red + ) + ) + + # scenario - growthrate + df[df$category == "scenario" & df$metric == "growthrate", ] <- + df[df$category == "scenario" & df$metric == "growthrate", ] %>% + mutate(text = paste0(region, "\n", + period, "\n", + "Avg. growth/yr: ", round(check_value)*100, "% \n", + "Absolute value: ", round(value, 2), " \n", + "Thresholds (yel/red): \n", + "Min: ", min_yel*100, "% / ", min_red*100, "% \n", + "Max: ", max_yel*100, "% / ", max_red*100, "%" + ) + ) + + return(df) +} diff --git a/R/combineData.R b/R/combineData.R new file mode 100644 index 0000000..468d3d9 --- /dev/null +++ b/R/combineData.R @@ -0,0 +1,168 @@ +#' @importFrom dplyr filter select mutate summarise group_by %>% + +# for one row of cfg: filter and merge relevant scenario data with cfg +# results in one df that contains scenario data, reference data and thresholds +combineData <- function(data, cfgRow, refData = NULL) { + + # shorten as it will be used a lot + c <- cfg_row + + # full dimensions and important slices + all_mod <- unique(data$model) + all_sce <- unique(data$scenario) + all_reg <- unique(data$region) + all_per <- unique(data$period) # not a factor, convert? + all_per <- all_per[all_per <= 2100] + ref_per <- c(2005, 2010, 2015, 2020) + + + # create filters #### + # check whether regions, periods, scenarios are specified, else use all + mod <- if (is.na(c$model)) all_mod else + strsplit(c$model, split = ", |, ")[[1]] + sce <- if (is.na(c$scenario)) all_sce else + strsplit(c$scenario, split = ", |, ")[[1]] + reg <- if (is.na(c$region)) all_reg else + strsplit(c$region, split = ", |,")[[1]] + + + # empty "period" field means different years for historic or scenario category + if (c$category == "historic") { + per <- if (is.na(c$period)) ref_per else + strsplit(as.character(c$period), split = ", |,")[[1]] + } else { + per <- if (is.na(c$period)) all_per else + strsplit(as.character(c$period), split = ", |,")[[1]] + } + + # apply filters #### + # filter scenario data according to each row in cfg + d <- data %>% + filter(variable %in% c$variable, # alternative approach for multiple variables could be used here + model %in% mod, + scenario %in% sce, + region %in% reg, + period %in% per) + + # attach cfg information to data slice, which is independent of category + d <- d %>% + mutate(min_red = c$min_red, + min_yel = c$min_yel, + max_yel = c$max_yel, + max_red = c$max_red, + category = c$category, + metric = c$metric, + critical = c$critical) + + # historic #### + # depending on category: filter and attach reference values if they are needed + if (c$category == "historic") { + # historic data for relevant variable and dimensions (all sources) + h <- ref_data %>% + filter(variable %in% c$variable, + region %in% reg, + period %in% per) %>% + mutate(ref_value = value, ref_model = model) %>% + select(-c("scenario", "unit", "value", "model")) + + # test whether ref_model exists and has data to compare to + if (nrow(h) == 0) { + cat(paste0("No reference data for variable ", c$variable, " found.\n")) + df <- data.frame() + } else { + + # in case one or more sources are specified, filter for them + if (!is.na(c$ref_model)) { + h <- filter( + h, ref_model %in% strsplit(c$ref_model, split = ", |,")[[1]] + ) + } + + # in case of multiple sources, use mean (of all available sources) + if (length(unique(h$ref_model)) > 1) { + h_mean <- group_by(h, period, region) %>% + summarise(ref_value = mean(ref_value, na.rm = TRUE)) + h <- mutate(h_mean, variable = c$variable, ref_model = "multiple") + } + + # merge with historical data adds columns ref_value and ref_model + df <- merge(d, h) + + # add columns which are not used in this category, fill NA + df <- df %>% mutate(ref_period = NA, ref_scenario = NA) + } + + # scenario #### + # filter and attach reference values if they are needed; scenario data + } else if (c$category == "scenario") { + + # no reference values needed for these metrics, fill NA + if (c$metric %in% c("absolute", "growthrate")) { + df <- d %>% + mutate(ref_value = NA, + ref_model = NA, + ref_scenario = NA, + ref_period = NA) + + + # get reference values for these metrics + # TODO: support choosing ref_period AND model/scenario? + } else if (c$metric %in% c("relative", "difference")) { + + # if a reference model should be used, same scenario, same period + if (!is.na(c$model)) { + ref <- data %>% + filter(variable %in% c$variable, + model %in% c$ref_model, # expects exactly one model for now + scenario %in% sce, + region %in% reg, + period %in% per) %>% + + mutate(ref_value = value, ref_model = model) %>% + select(-c(model, value)) %>% + # add columns which are not used in this category, fill NA + mutate(ref_scenario = NA, ref_period = NA) + + # if a reference scenario should be used, same period, same model + } else if (!is.na(c$ref_scenario)) { + ref <- data %>% + filter(variable %in% c$variable, + model %in% mod, + scenario %in% c$ref_scenario, # expects exactly one scenario + region %in% reg, + period %in% per) %>% + + mutate(ref_value = value, ref_scenario = scenario) %>% + select(-c(scenario, value)) %>% + # add columns which are not used in this category, fill NA + mutate(ref_model = NA, ref_period = NA) + + # if a reference period should be used, same scenario, same model + } else if (!is.na(c$ref_period)) { + ref <- data %>% + filter(variable %in% c$variable, + model %in% mod, + scenario %in% sce, + region %in% reg, + period %in% c$ref_period) %>% # expects exactly one period + + mutate(ref_value = value, ref_period = period) %>% + select(-c(period, value)) %>% + # add columns which are not used in this category, fill NA + mutate(ref_model = NA, ref_scenario = NA) + } + + df <- merge(d, ref) + + } else { + # TODO: have this warning here or earlier when cleaning config? + warning("'metric' for category 'scenario' must be either 'absolute', + 'relative', 'difference' or 'growthrate'.") + } + + } else { + warning("'category' must be either 'historic' or 'scenario'.") + } + + return(df) +} diff --git a/R/evaluateThresholds.R b/R/evaluateThresholds.R new file mode 100644 index 0000000..5550c01 --- /dev/null +++ b/R/evaluateThresholds.R @@ -0,0 +1,99 @@ +#' @importFrom dplyr filter select mutate summarise group_by %>% + +# cleanInf = TRUE: replace "Inf" and "-Inf" which were introduced +# for ease of calculations with "-" +evaluateThresholds <- function(df, cleanInf = TRUE) { + + # first calculate values that will be compared to thresholds for each category + # ("check_value") and metric separately, then perform evaluation for all together + + # historic - relative #### + his_rel <- df[df$category == "historic" & df$metric == "relative", ] %>% + mutate(check_value = ifelse( + is.na(ref_value), + NA, + # absolute relative deviation above/below reference + abs((value - ref_value) / ref_value) + ) + ) + + # historic - difference #### + his_dif <- df[df$category == "historic" & df$metric == "difference", ] %>% + # absolute difference to reference + mutate(check_value = abs(value - ref_value)) + + # scenario - relative #### + sce_rel <- df[df$category == "scenario" & df$metric == "relative", ] %>% + mutate(check_value = ifelse( + is.na(ref_value), + NA, + # relative deviation above/below reference + (value - ref_value)/ref_value + ) + ) + + # scenario - difference #### + sce_dif <- df[df$category == "scenario" & df$metric == "difference", ] %>% + mutate(check_value = ifelse( + is.na(ref_value), + NA, + # difference to reference + value - ref_value + ) + ) + + # scenario - absolute #### + sce_abs <- df[df$category == "scenario" & df$metric == "absolute", ] %>% + mutate(check_value = value) + + # scenario - growthrate #### + # calculate average growth rate of the last 5 years between 2010 and 2060 + # TODO: in case of a need to look further than 2060, split df and add + # calculations for 10-year step periods + sce_gro <- filter(df[df$category == "scenario" & df$metric == "growthrate", ], + period <= 2060 & period >= 2010) + + # add a column with the value 5 years ago for later calculations + tmp <- sce_gro + sce_gro$period <- sce_gro$period + 5 + sce_gro <- sce_gro %>% + mutate(value_5y_ago = value) %>% + select(-value) %>% + merge(tmp) + + # calculate the yearly average growth rate + sce_gro <- sce_gro %>% + mutate(check_value = (value/value_5y_ago)^(1/5) - 1) %>% + select(-value_5y_ago) + + + # reassemble data.frame + df <- do.call("rbind", + list(his_rel, his_dif, sce_rel, sce_dif, sce_abs, sce_gro)) + + # evaluation #### + # perform comparison to thresholds for whole data.frame at once + # TODO: not as robust as previously thought. Partially fails if only max_red is given + df <- df %>% + mutate(check = ifelse(is.na(check_value), + "grey", + ifelse( + # first check whether red threshold is violated... + check_value > max_red | check_value < min_red, + "red", + # otherwise check if yellow threshold is violated... + ifelse( + check_value > max_yel | check_value < min_yel, + "yellow", + # ... else green + "green" + ) + ) + ) + ) + + # after evaluation, "Inf" can be removed + if (cleanInf) df[df == "Inf" | df == "-Inf"] <- NA + + return(df) +} diff --git a/R/importFunctions.R b/R/importFunctions.R new file mode 100644 index 0000000..ffe0d82 --- /dev/null +++ b/R/importFunctions.R @@ -0,0 +1,129 @@ +#' @importFrom dplyr filter select mutate %>% +#' @importFrom readxl read_excel excel_sheets +#' @importFrom utils read.csv2 + +# scenarioPath: one or multiple paths to .mif or .csv file(s) containing +# scenario data in IAM format +importScenarioData <- function(scenarioPath) { + data <- remind2::deletePlus(quitte::as.quitte(scenarioPath, na.rm = TRUE)) + + # change ordering of factors, global elements first + new_order <- unique(intersect(c("World", "GLO", + levels(data$region)), levels(data$region))) + data$region <- factor(data$region, levels = new_order) + + # remove rows without values + data <- data[!is.na(data$value), ] + + return(data) +} + +# import historical or other reference data for comparison +importReferenceData <- function(referencePath) { + ref <- quitte::as.quitte(referencePath) + + # remove all historical data before 1990 + ref <- dplyr::filter(ref, period >= 1990) + + return(ref) +} + +# see README for rules on how to fill the config +getConfig <- function(configName) { + path <- system.file(paste0("config/", configName), package = "piamValidation") + if (! file.exists(path) && file.exists(configName)) path <- configName + + # config can be .xlsx or .csv, use "config" sheet in .xlsx if available + if (grepl("\\.xlsx$", path)) { + cfg <- read_excel( + path = path, + sheet = if ("config" %in% excel_sheets(path)) "config" else 1 + ) + cfg <- filter(cfg, ! grepl("^#", cfg[[1]])) + } else { + cfg <- tibble::as_tibble( + read.csv2(path, na.strings = "", comment.char = "#")) + } + message("loading config file: ", configName, "\n") + return(cfg) +} + +# fill empty threshold columns with Infinity for easier evaluation +fillInf <- function(cfg) { + cfg <- cfg %>% + mutate(min_red = as.numeric(ifelse(is.na(min_red), -Inf, min_red)), + min_yel = as.numeric(ifelse(is.na(min_yel), -Inf, min_yel)), + max_yel = as.numeric(ifelse(is.na(max_yel), Inf, max_yel)), + max_red = as.numeric(ifelse(is.na(max_red), Inf, max_red)) + ) + + return(cfg) +} + + +# replace period ranges using ":" with comma-separated list of years +expandPeriods <- function(cfg, data) { + per_expand_idx <- grep("\\:", cfg$period) + all_per <- unique(data$period) + + # iterate through rows with ":" + for (i in per_expand_idx) { + # check format and compatibility of data and ref periods + if (nchar(cfg[i, "period"]) != 9) { + stop("Invalid range detected. Make sure to enter years as YYYY:YYYY.\n") + } else { + # find scenario-years that are withing indicated range + start <- substr(cfg[i, "period"], 1, 4) + if (start < min(all_per)) { + warning("Selected period starts earlier than scenario data.\n") + } + stop <- substr(cfg[i, "period"], 6, 9) + if (stop > max(all_per)) { + warning("Selected period ends later than scenario data.\n") + } + selected_per <- all_per[all_per >= start & all_per <= stop] + + # overwrite ":" notation with list + cfg[i, "period"] <- paste0(selected_per, collapse = ", ") + } + } + return(cfg) +} + + +# takes config entries specifying a set of variables via "*" and expands it so +# that every variable corresponds to one row in cfg +# * matches everything until the next |, while ** matches including | +expandVariables <- function(cfg, data) { + # create the expanded config, starting with the not-to-expand rows, + # then appending the rows with expanded variable names + var_expand <- cfg[grepl("\\*", cfg$variable), ] + cfg_new <- dplyr::anti_join(cfg, var_expand, by = colnames(cfg)) + + if (length(var_expand > 0)) { + all_vars <- unique(data$variable) + for (i in seq(nrow(var_expand))) { + # prepare strings for grepping by adding escape characters and "." + vartoexpand <- var_expand$variable[i] + # escape "|" + vargrep <- gsub("|", "\\|", vartoexpand, fixed = TRUE) + # convert * into "everything except |" + vargrep <- gsub("*", "[^\\|]*", vargrep, fixed = TRUE) + # convert what was ** back to .* + vargrep <- gsub("[^\\|]*[^\\|]*", ".*", vargrep, fixed = TRUE) + # make sure you match the full variable, not just a part + vargrep <- paste0("^", vargrep, "$") + selected_vars <- all_vars[grepl(vargrep, all_vars)] + message(var_expand$variable[i], " was expanded into ", + length(selected_vars), " sub-variables.") + + # take the original row for the current set of variables and repeat it + # once for each sub-variable, overwrite with sub-variable names + c <- cfg[i,] %>% + dplyr::slice(rep(1, each = length(selected_vars))) + c$variable <- selected_vars + cfg_new <- rbind(cfg_new, c) + } + } + return(cfg_new) +} diff --git a/R/outputFunctions.R b/R/outputFunctions.R new file mode 100644 index 0000000..5dc5b31 --- /dev/null +++ b/R/outputFunctions.R @@ -0,0 +1,20 @@ +#' returns information on whether scenarios passed critical validation checks +#' +#' @param data data.frame as returned from ``validateScenarios()`` +#' @param yellowFail if set to TRUE a yellow check result of a critical +#' variable will lead to the scenario not passing as validated +#' +#' @importFrom dplyr %>% +#' @export +validationPass <- function(data, yellowFail = FALSE) { + + fail_color <- ifelse(yellowFail, c("red", "yellow"), "red") + + # see if any critical variables have failed per scenario and model + pass <- data %>% + dplyr::filter(check %in% fail_color, critical == "yes") %>% + dplyr::group_by(model, scenario) %>% + dplyr::summarize(pass = dplyr::n() == 0, n_fail = dplyr::n()) + + return(pass) +} diff --git a/R/piamValidation-package.R b/R/piamValidation-package.R index a65cf64..05db633 100644 --- a/R/piamValidation-package.R +++ b/R/piamValidation-package.R @@ -1,6 +1,7 @@ -#' @keywords internal +#' Validation Tools for PIK-PIAM +#' +#' The piamValidation package provides validation tools for the Potsdam Integrated Assessment Modelling environment. +#' +#' @name piamValidation-package +#' @aliases piamValidation-package piamValidation "_PACKAGE" - -## usethis namespace: start -## usethis namespace: end -NULL diff --git a/R/resolveDuplicates.R b/R/resolveDuplicates.R new file mode 100644 index 0000000..85dd163 --- /dev/null +++ b/R/resolveDuplicates.R @@ -0,0 +1,31 @@ +# check for duplicates that are a result of config definition overlaps +# only the latest occurrence of a data point is kept (lowest row of config) +resolveDuplicates <- function(df) { + + # TODO: needs to be double checked for edge-cases + # might be easier or safer to handle categories separately + + # these are the duplicates that are from the lower rows and should be kept + duplicates <- df[duplicated(df[c("model", + "scenario", + "variable", + "region", + "period", + "category", + "metric")]), ] + + # here, all instances of the data that was duplicated are removed + no_dupl <- dplyr::anti_join(df, duplicates, by = c("model", + "scenario", + "variable", + "region", + "period", + "category", + "metric")) + + # reattach one instance to get a complete data set without duplicates + df <- rbind(no_dupl, duplicates) + + + return(df) +} diff --git a/R/validateScenarios.R b/R/validateScenarios.R new file mode 100644 index 0000000..29f89f3 --- /dev/null +++ b/R/validateScenarios.R @@ -0,0 +1,41 @@ +#' performs the validation checks from a config on a scenario dataset +#' +#' @param scenarioPath one or multiple path(s) to scenario data in .mif or .csv +#' format +#' @param configName select config from inst/config +#' @param referencePath in case of historic comparison, choose path to ref data +#' +#' @importFrom dplyr filter select mutate group_by %>% +#' @export +validateScenarios <- function(scenarioPath, configName, referencePath = NULL) { + + data <- importScenarioData(scenarioPath) + + hist <- importReferenceData(referencePath) + + cfg <- configName %>% + getConfig() %>% + fillInf() %>% + expandPeriods(data) %>% + expandVariables(data) + + + # combine data for each row of the config and bind together + df <- data.frame() + for (i in 1:nrow(cfg)) { + # TODO: hist should only be needed if category "historical" is in config + # validation generally should work without hist data + df_row <- combineData(data, cfg[i, ], refData = hist) + df <- rbind(df, df_row) + cat(paste0("Combined config row ", i, " of ", nrow(cfg), "\n")) + } + + df <- resolveDuplicates(df) + + df <- evaluateThresholds(df) + + return(df) +} + + + diff --git a/R/validationHeatmap.R b/R/validationHeatmap.R new file mode 100644 index 0000000..6007063 --- /dev/null +++ b/R/validationHeatmap.R @@ -0,0 +1,85 @@ +#' takes the output of "validateScenarios()" and plots heatmaps per variable +#' +#' @param data data.frame as returned by ``validateScenarios()`` +#' @param var variable to be plotted +#' @param cat choose category from "historical" or "scenario" +#' @param met choose metric from "relative", "difference", "absolute" or +#' "growthrate" +#' @param interactive return plots as interactive plotly plots by default +#' @param compareModels if TRUE, plots compare models instead of scenarios +#' +#' @importFrom dplyr filter select mutate %>% +#' @import ggplot2 +#' @importFrom ggthemes theme_tufte +#' @importFrom plotly ggplotly +#' @export + +validationHeatmap <- function(data, var, cat, met, + interactive = T, compareModels = T) { + + # possible extension: when giving multiple vars, plot as facets in same row + + # prepare data slice + d <- data %>% + filter(variable == var, + category == cat, + metric == met) + + # warn if no data is found for combination of var, cat and met + if (nrow(d) == 0) { + data$cm <- paste(category, metric, sep = "-") + warning( + paste0( + "No data found for variable in this category and metric.\n + variable ", var ," is available for the following category-metric + combinations: ", unique(data[data$variable == var, "cm"]) + ) + ) + } + + d$period <- as.character(d$period) + colors <- c(green = "#008450", + yellow = "#EFB700", + red = "#B81D13", + grey = "#808080") + + + # classic ggplot, with text in aes + p <- ggplot(d, aes(x = region, y = period, fill=check, text=text)) + + geom_tile(color="white", linewidth=0.0) + + scale_fill_manual(values = colors, breaks = colors) + + facet_grid(model~scenario) + + # make it beautiful + # from https://www.r-bloggers.com/2016/02/making-faceted-heatmaps-with-ggplot2 + p <- p + labs(x = NULL, y = NULL, title = paste0(var, + " [", d$unit[1], "] - ", + cat, "/", met)) + p <- p + theme_tufte(base_family = "Helvetica") # creates warnings + p <- p + theme(axis.ticks = element_blank()) + p <- p + theme(axis.text = element_text(size = 7)) + p <- p + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) + p <- p + coord_equal() + p <- p + theme(legend.position = "none") + + + # p + theme(panel.spacing = unit(2, "lines")) + + # not great, only works with "World" being the first region + if("World" %in% d$region) { + p <- p + geom_vline(xintercept = 1.5, linewidth = 0.8, color = "white") + } + fig <- ggplotly(p, tooltip = "text") + + # improve plotly layout, kinda works but very manual + # TODO: can this be extended to a general, useful function? + #fig <- fig %>% subplot(heights = 0.3) %>% + # layout(title = list(y=0.64)) + + if (interactive) { + return(fig) + } else { + return(p) + } + +} diff --git a/README.md b/README.md index 1585caf..08b90be 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Validation Tools for PIK-PIAM -R package **piamValidation**, version **0.0.2** +R package **piamValidation**, version **0.1.0** [![CRAN status](https://www.r-pkg.org/badges/version/piamValidation)](https://cran.r-project.org/package=piamValidation) [![R build status](https://github.com/pik-piam/piamValidation/workflows/check/badge.svg)](https://github.com/pik-piam/piamValidation/actions) [![codecov](https://codecov.io/gh/pik-piam/piamValidation/branch/master/graph/badge.svg)](https://app.codecov.io/gh/pik-piam/piamValidation) @@ -38,16 +38,16 @@ In case of questions / problems please contact Pascal Weigmann . +Weigmann P, Richters O (2024). _piamValidation: Validation Tools for PIK-PIAM_. R package version 0.1.0, . A BibTeX entry for LaTeX users is ```latex @Manual{, title = {piamValidation: Validation Tools for PIK-PIAM}, - author = {Pascal Weigmann}, + author = {Pascal Weigmann and Oliver Richters}, year = {2024}, - note = {R package version 0.0.2}, + note = {R package version 0.1.0}, url = {https://github.com/pik-piam/piamValidation}, } ``` diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..cf6328e --- /dev/null +++ b/codecov.yml @@ -0,0 +1,18 @@ +comment: true +github_checks: + annotations: false +coverage: + precision: 2 + round: down + range: "0...95" + status: + project: + default: + target: auto + threshold: 1% + informational: true + patch: + default: + target: auto + threshold: 1% + informational: true diff --git a/inst/config/validationConfig.csv b/inst/config/validationConfig.csv new file mode 100644 index 0000000..f72a115 --- /dev/null +++ b/inst/config/validationConfig.csv @@ -0,0 +1,23 @@ +category;metric;critical;variable;model;scenario;region;period;min_red;min_yel;max_yel;max_red;ref_model;ref_scenario;ref_period;source/link to discussion +historic;relative;yes;Emi|CO2|Energy;;;World;;;;0.05;0.1;EDGAR6;;; +historic;relative;yes;Emi|CO2|Energy;;;DEU;;;;0.1;0.2;EDGAR6;;; +historic;relative;yes;PE|Coal;;;;;;;;0.1;BP, IEA;;; +historic;relative;yes;PE|Coal;;;World;;;;;0.2;BP, IEA;;; +historic;difference;yes;PE|Oil;;;;;;;;2;;;; +historic;difference;yes;PE|Oil;;;World;;;;;5;;;; +historic;relative;yes;FE|Electricity;;Bal;;;;;;0.1;;;; +historic;relative;yes;FE|Electricity;;Bal;World;;;;;0.2;;;; +scenario;absolute;yes;Price|Carbon;;;;2030;;;300;600;;;; +scenario;absolute;yes;Price|Carbon;;;;2050;;;;1000;;;; +scenario;absolute;yes;Price|Carbon;;;DEU;2030;50;100;400;800;;;; +scenario;absolute;yes;Price|Carbon;;;DEU;2050;100;150;600;1200;;;; +scenario;absolute;no;Price|Carbon;;;LAM, MEA, SSA, OAS, IND;2030, 2050;;;;200;;;; +scenario;absolute;yes;Emi|GHG;;Bal;DEU, USA, JPN, REF, CHA, CAZ, EUR;2050;-2000;-1000;0;500;;;; +scenario;absolute;no;Emi|GHG;;Bal;LAM, MEA, SSA, OAS, IND;2060;-2000;-1000;0;500;;;; +scenario;absolute;yes;Temperature|Global Mean;;Bal;;2100;;;1.5;1.55;;;; +scenario;relative;yes;Emi|CO2;;;;2030;-0.5;-0.3;;;;;2020; +scenario;relative;yes;Emi|CO2;;;;2030;-0.6;-0.4;;;;;2015; +scenario;relative;no;FE;;;;2030, 2040;-0.4;-0.2;;;;;2020; +scenario;absolute;yes;Price|Secondary Energy|Electricity;;Bal;;;5.56;;;;;;; +scenario;growthrate;yes;New Cap|Electricity|Solar|PV;;;;;;;0.5;1;;;; +scenario;relative;yes;Price|Carbon;;Bal;;2030, 2040, 2050;1;;;;;NPi;; diff --git a/inst/config/validationConfig_COMMITTED.csv b/inst/config/validationConfig_COMMITTED.csv new file mode 100644 index 0000000..c364404 --- /dev/null +++ b/inst/config/validationConfig_COMMITTED.csv @@ -0,0 +1,3 @@ +category;metric;critical;variable;model;scenario;region;period;min_red;min_yel;max_yel;max_red;ref_model;ref_scenario;ref_period;source/link to discussion +historic;relative;yes;Emi|CO2|*;;;;2020;;;0.1;0.2;;;; +historic;relative;yes;FE|*;;;;2020;;;0.1;0.2;;;; diff --git a/inst/config/validationConfig_NGFS.csv b/inst/config/validationConfig_NGFS.csv new file mode 100644 index 0000000..3ff24a3 --- /dev/null +++ b/inst/config/validationConfig_NGFS.csv @@ -0,0 +1,11 @@ +category;metric;critical;variable;model;scenario;region;period;min_red;min_yel;max_yel;max_red;ref_model;ref_scenario;ref_period;source/link to discussion +scenario;relative;yes;Emissions|*;REMIND-MAgPIE 3.3-4.7;;;;;;50;100;GCAM 6.0 NGFS;;; +scenario;relative;yes;Emissions|*;REMIND-MAgPIE 3.3-4.7;;World;;;;30;50;GCAM 6.0 NGFS;;; +scenario;relative;yes;Final Energy|*|Chemicals|*;REMIND-MAgPIE 3.3-4.7;;;;;;50;100;GCAM 6.0 NGFS;;; +scenario;relative;yes;Final Energy|*;REMIND-MAgPIE 3.3-4.7;;World;;;;30;50;GCAM 6.0 NGFS;;; +scenario;relative;yes;Secondary Energy|*;REMIND-MAgPIE 3.3-4.7;;;;;;50;100;GCAM 6.0 NGFS;;; +scenario;relative;yes;Secondary Energy|*;REMIND-MAgPIE 3.3-4.7;;World;;;;30;50;GCAM 6.0 NGFS;;; +scenario;relative;yes;Primary Energy|*;REMIND-MAgPIE 3.3-4.7;;;;;;50;100;GCAM 6.0 NGFS;;; +scenario;relative;yes;Primary Energy|*;REMIND-MAgPIE 3.3-4.7;;World;;;;30;50;GCAM 6.0 NGFS;;; +scenario;relative;yes;Carbon Sequestration|*;REMIND-MAgPIE 3.3-4.7;;;;;;50;100;GCAM 6.0 NGFS;;; +scenario;relative;yes;Carbon Sequestration|*;REMIND-MAgPIE 3.3-4.7;;World;;;;30;50;GCAM 6.0 NGFS;;; diff --git a/inst/markdown/COMMITTEDvalidation.Rmd b/inst/markdown/COMMITTEDvalidation.Rmd new file mode 100644 index 0000000..e75ecb3 --- /dev/null +++ b/inst/markdown/COMMITTEDvalidation.Rmd @@ -0,0 +1,158 @@ +--- +title: "piamValidation: COMMITED" +date: "`r format(Sys.Date())`" +output: + html_document: + toc: true + toc_float: true + code_folding: hide +params: + mif: "" + ref: "" + cfg: "" + warning: false + message: false + figWidth: 8 +--- + +```{r include=FALSE} +devtools::load_all(".") +library(knitr) +library(ggplot2) +library(ggthemes) +library(plotly) + +knitr::opts_chunk$set( + echo = FALSE, + error = TRUE, + #fig.width = params$figWidth, + message = params$message, + warning = params$warning +) +``` + +## Import and Prepare Data + + +```{r, message = FALSE} +# Data Preparation +path <- "C:/Users/pascalwe/Data/vs/" +mifs <- c(paste0(path, "REMIND_generic_Bal.mif"), + paste0(path, "REMIND_generic_NPi.mif")) + +hmif <- paste0(path, "historical.mif") + +configName <- "validationConfig_COMMITED.csv" + +df <- validateScenarios(mifs, config, referencePath = hmif) +df <- appendTooltips(df) + +``` + +## Heatmaps by Category + +### Historic - Relative +Relative deviation to historical reference data. Absolute numbers, so no +difference is made between being above or below the reference value. +```{r} +c <- "historic" +m <- "relative" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Historic - Difference +Absolute difference to historical reference data +```{r} +c <- "historic" +m <- "difference" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Relative +Relative deviation to data point from either: + +- period (same scenario/model) +- scenario (same period/model) +- model (same period/scenario - Not supported yet) + +```{r} +c <- "scenario" +m <- "relative" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Difference +```{r} +c <- "scenario" +m <- "difference" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Absolute +```{r} +c <- "scenario" +m <- "absolute" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + + +### Scenario - Growthrate +```{r} +c <- "scenario" +m <- "growthrate" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` diff --git a/inst/markdown/NGFSvalidation.Rmd b/inst/markdown/NGFSvalidation.Rmd new file mode 100644 index 0000000..9e53ca3 --- /dev/null +++ b/inst/markdown/NGFSvalidation.Rmd @@ -0,0 +1,163 @@ +--- +title: "piamValidation: NGFS" +date: "`r format(Sys.Date())`" +output: + html_document: + toc: true + toc_float: true + code_folding: hide +params: + mif: "" + ref: "" + cfg: "" + warning: false + message: false + figWidth: 8 +--- + +```{r include=FALSE} +devtools::load_all(".") +library(knitr) +library(ggplot2) +library(ggthemes) +library(plotly) + +knitr::opts_chunk$set( + echo = FALSE, + error = TRUE, + #fig.width = params$figWidth, + message = params$message, + warning = params$warning +) + +# rmarkdown::render('my_markdown_report.Rmd', +# output_file = paste('report.', Sys.Date(), +# '.pdf', sep='')) +``` + +## Import and Prepare Data + + +```{r, message = FALSE} +# Data Preparation +path <- "C:/Users/pascalwe/Data/NGFS/phase5/" +snapshot <- "1709886115166-snapshot-24/snapshot_all_regions.csv" +config <- "validationConfig_NGFS.csv" + +scenarioPath <- paste0(path, snapshot) + +df <- validateScenarios(scenarioPath, config) +df <- appendTooltips(df) + +# remove all-green rows (all regions green) +df_noGreen <- df[df$check != "green", ] +df_noGreennoGrey <- df[df$check %in% c("red", "yellow"), ] +``` + +## Heatmaps by Category + +### Historic - Relative +Relative deviation to historical reference data. Absolute numbers, so no +difference is made between being above or below the reference value. +```{r} +c <- "historic" +m <- "relative" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Historic - Difference +Absolute difference to historical reference data +```{r} +c <- "historic" +m <- "difference" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Relative +Relative deviation to data point from either: + +- period (same scenario/model) +- scenario (same period/model) +- model (same period/scenario - Not supported yet) + +```{r} +c <- "scenario" +m <- "relative" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Difference +```{r} +c <- "scenario" +m <- "difference" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Absolute +```{r} +c <- "scenario" +m <- "absolute" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + + +### Scenario - Growthrate +```{r} +c <- "scenario" +m <- "growthrate" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` diff --git a/inst/markdown/REMINDvalidation.Rmd b/inst/markdown/REMINDvalidation.Rmd new file mode 100644 index 0000000..1a94d1d --- /dev/null +++ b/inst/markdown/REMINDvalidation.Rmd @@ -0,0 +1,174 @@ +--- +title: "piamValidation: REMIND" +date: "`r format(Sys.Date())`" +output: + html_document: + toc: true + toc_float: true + code_folding: hide +params: + mif: "" + ref: "" + cfg: "" + warning: false + message: false + figWidth: 8 +--- + +```{r include=FALSE} +devtools::load_all(".") +library(knitr) +library(dplyr) +library(ggplot2) +library(ggthemes) +library(plotly) + +knitr::opts_chunk$set( + echo = FALSE, + error = TRUE, + #fig.width = params$figWidth, + message = params$message, + warning = params$warning +) +``` + +## Import and Prepare Data + + +```{r, message = FALSE} +# Data Preparation +path <- "C:/Users/pascalwe/Data/vs/" +mifs <- c(paste0(path, "REMIND_generic_Bal.mif"), + paste0(path, "REMIND_generic_NPi.mif")) + +hmif <- paste0(path, "historical.mif") + +config <- "validationConfig.csv" + +df <- validateScenarios(mifs, config, referencePath = hmif) +df <- appendTooltips(df) +``` +## Validation + +### Summary +```{r} +# find "critical == yes" data points, that are red/yellow +colors <- c(green = "#008450", + yellow = "#EFB700", + red = "#B81D13", + grey = "#808080") +dt <- dplyr::count(df, critical, check) +# kbl(dt) %>% +# kable_styling(bootstrap_options = c("striped", "hover"), +# full_width = F) %>% +# column_spec(2, color = colors[dt$check], +# background = colors[dt$check]) +``` + +## Heatmaps by Category + +### Historic - Relative +Relative deviation to historical reference data. Absolute numbers, so no +difference is made between being above or below the reference value. +```{r} +c <- "historic" +m <- "relative" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Historic - Difference +Absolute difference to historical reference data +```{r} +c <- "historic" +m <- "difference" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Relative +Relative deviation to data point from either: + +- period (same scenario/model) +- scenario (same period/model) +- model (same period/scenario - Not supported yet) + +```{r} +c <- "scenario" +m <- "relative" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Difference +```{r} +c <- "scenario" +m <- "difference" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + +### Scenario - Absolute +```{r} +c <- "scenario" +m <- "absolute" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` + + +### Scenario - Growthrate +```{r} +c <- "scenario" +m <- "growthrate" +d <- df[df$category == c & df$metric == m, ] + +if (nrow(d) > 0) { + vars <- unique(d$variable) + plot_list <- htmltools::tagList() + for (i in 1:length(vars)) { + plot_list[[i]] <- validationHeatmap(d, vars[i], cat = c, met = m) + } + plot_list +} +``` diff --git a/man/piamValidation-package.Rd b/man/piamValidation-package.Rd index 962bd73..47bb351 100644 --- a/man/piamValidation-package.Rd +++ b/man/piamValidation-package.Rd @@ -2,9 +2,9 @@ % Please edit documentation in R/piamValidation-package.R \docType{package} \name{piamValidation-package} -\alias{piamValidation} \alias{piamValidation-package} -\title{piamValidation: Validation Tools for PIK-PIAM} +\alias{piamValidation} +\title{Validation Tools for PIK-PIAM} \description{ The piamValidation package provides validation tools for the Potsdam Integrated Assessment Modelling environment. } @@ -18,5 +18,9 @@ Useful links: \author{ \strong{Maintainer}: Pascal Weigmann \email{pascal.weigmann@pik-potsdam.de} +Authors: +\itemize{ + \item Oliver Richters +} + } -\keyword{internal} diff --git a/man/validateScenarios.Rd b/man/validateScenarios.Rd new file mode 100644 index 0000000..c768758 --- /dev/null +++ b/man/validateScenarios.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/validateScenarios.R +\name{validateScenarios} +\alias{validateScenarios} +\title{performs the validation checks from a config on a scenario dataset} +\usage{ +validateScenarios(scenarioPath, configName, referencePath = NULL) +} +\arguments{ +\item{scenarioPath}{one or multiple path(s) to scenario data in .mif or .csv +format} + +\item{configName}{select config from inst/config} + +\item{referencePath}{in case of historic comparison, choose path to ref data} +} +\description{ +performs the validation checks from a config on a scenario dataset +} diff --git a/man/validationHeatmap.Rd b/man/validationHeatmap.Rd new file mode 100644 index 0000000..e74794a --- /dev/null +++ b/man/validationHeatmap.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/validationHeatmap.R +\name{validationHeatmap} +\alias{validationHeatmap} +\title{takes the output of "validateScenarios()" and plots heatmaps per variable} +\usage{ +validationHeatmap(data, var, cat, met, interactive = T, compareModels = T) +} +\arguments{ +\item{data}{data.frame as returned by ``validateScenarios()``} + +\item{var}{variable to be plotted} + +\item{cat}{choose category from "historical" or "scenario"} + +\item{met}{choose metric from "relative", "difference", "absolute" or +"growthrate"} + +\item{interactive}{return plots as interactive plotly plots by default} + +\item{compareModels}{if TRUE, plots compare models instead of scenarios} +} +\description{ +takes the output of "validateScenarios()" and plots heatmaps per variable +} diff --git a/man/validationPass.Rd b/man/validationPass.Rd new file mode 100644 index 0000000..4d969c1 --- /dev/null +++ b/man/validationPass.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/outputFunctions.R +\name{validationPass} +\alias{validationPass} +\title{returns information on whether scenarios passed critical validation checks} +\usage{ +validationPass(data, yellowFail = FALSE) +} +\arguments{ +\item{data}{data.frame as returned from ``validateScenarios()``} + +\item{yellowFail}{if set to TRUE a yellow check result of a critical +variable will lead to the scenario not passing as validated} +} +\description{ +returns information on whether scenarios passed critical validation checks +} diff --git a/piamValidation.Rproj b/piamValidation.Rproj new file mode 100644 index 0000000..497f8bf --- /dev/null +++ b/piamValidation.Rproj @@ -0,0 +1,20 @@ +Version: 1.0 + +RestoreWorkspace: Default +SaveWorkspace: Default +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source diff --git a/tests/.lintr b/tests/.lintr new file mode 100644 index 0000000..165993b --- /dev/null +++ b/tests/.lintr @@ -0,0 +1,2 @@ +linters: lucode2::lintrRules(allowUndesirable = TRUE) +encoding: "UTF-8" diff --git a/tests/testthat/test-dummy.R b/tests/testthat/test-dummy.R deleted file mode 100644 index 909f17c..0000000 --- a/tests/testthat/test-dummy.R +++ /dev/null @@ -1 +0,0 @@ -skip("dummy test") diff --git a/tests/testthat/test-expandVariables.R b/tests/testthat/test-expandVariables.R new file mode 100644 index 0000000..71c85e6 --- /dev/null +++ b/tests/testthat/test-expandVariables.R @@ -0,0 +1,49 @@ +test_that("expandVariables works", { + varinconfig <- function(data, varentry, expectedvars) { + configfile <- file.path(tempdir(), "config.csv") + configheader <- paste0( + "category;metric;critical;variable;model;scenario;region;period;", + "min_red;min_yel;max_yel;max_red;ref_model;ref_scenario;ref_period;") + + configstring <- paste0("scenario;relative;yes;", + varentry, + ";m;s;r;2005;;;10;100;refmodel1;;;") + writeLines(c(configheader, configstring), configfile) + cfg <- expandVariables(getConfig(configfile), data) + missing <- setdiff(expectedvars, cfg$variable) + if (length(missing) > 0) { + warning("For ", varentry, + ", the following variables are expected but missing:\n", + paste0(missing, collapse = ", ")) + } + expect_true(length(missing) == 0) + toomuch <- setdiff(cfg$variable, expectedvars) + if (length(toomuch) > 0) { + warning("For ", varentry, + ", the following variables were not expected but matched:\n", + paste0(toomuch, collapse = ", ")) + } + expect_true(length(toomuch) == 0) + } + + data <- list() + depth1 <- c("Emi|CO", "Emi|BC", "Emi|GHG") + depth2CO <- c("Emi|CO|Trans", "Emi|CO|Build", "Emi|CO|Indus") + depth2BC <- c("Emi|BC|Trans", "Emi|BC|Build", "Emi|BC|Indus") + depth3CO <- c("Emi|CO|Trans|Gas", "Emi|CO|Trans|Oil") + whatever <- c("Emi", "Final Energy", "CO|Trans") + data$variable <- c(depth1, depth2CO, depth2BC, depth3CO, whatever) + + # first argument is what is stated in the config, second what should be matched + varinconfig(data, "Final Energy", "Final Energy") + varinconfig(data, "Em*", "Emi") + varinconfig(data, "Emi", "Emi") + varinconfig(data, "Emi|*", depth1) + varinconfig(data, "Emi|*|*", c(depth2CO, depth2BC)) + varinconfig(data, "Emi|*|*|*", depth3CO) + varinconfig(data, "*|CO|*", depth2CO) + varinconfig(data, "CO|*", "CO|Trans") + varinconfig(data, "Final Energy|**", NULL) + varinconfig(data, "Emi|**", c(depth1, depth2CO, depth2BC, depth3CO)) + varinconfig(data, "Em**", c("Emi", depth1, depth2CO, depth2BC, depth3CO)) +})