From 71d5f98fb2860b604c35c2553a037c8ab76121a5 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Wed, 23 Nov 2022 14:53:58 +0100 Subject: [PATCH 01/21] Add vitigeoss dict to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d95b20cb..ce85ad32 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ out-logs/ +conf/vitigeoss-vars-dict.yml -- GitLab From 1f80bdf61685b8eea370a9f953e284100eae1441 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Wed, 23 Nov 2022 17:15:19 +0100 Subject: [PATCH 02/21] First review of recipe checks (WIP) --- tools/check_recipe.R | 174 +++++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 81 deletions(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index 25536335..a25c5f80 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -1,65 +1,70 @@ -check_recipe <- function(recipe, logger) { +check_recipe <- function(recipe) { # recipe: yaml recipe already read it # output: errors or the total number of workflow (vars x regions) to compute - info(logger, paste("Checking recipe", recipe$filename)) + info(recipe$Run$logger, paste("Checking recipe", recipe$name)) # --------------------------------------------------------------------- # ANALYSIS CHECKS # --------------------------------------------------------------------- - TIME_SETTINGS = c('sdate','leadtimemin','leadtimemax','hcst_start','hcst_end') - PARAMS = c('Horizon','Time','Variables','Region','Regrid','Workflow','Datasets') - HORIZONS <- c('Subseasonal','Seasonal','Decadal') + TIME_SETTINGS = c('sdate', 'ftime_min', 'ftime_max', 'hcst_start', 'hcst_end') + PARAMS = c('Horizon', 'Time', 'Variables', 'Region', 'Regrid', 'Workflow', + 'Datasets') + HORIZONS <- c('subseasonal', 'seasonal', 'decadal') - # create output dirs: - if (!any(names(recipe) %in% "Analysis")) { - error(logger, "The recipe should contain an element called 'Analysis'.") + # Check basic elements in recipe:Analysis: + if (!("Analysis" %in% names(recipe))) { + error(recipe$Run$logger, + "The recipe must contain an element called 'Analysis'.") } if (!all(PARAMS %in% names(recipe$Analysis))) { error(logger, - paste("The element 'Analysis' in the recipe should contain these", - "elements:", paste(PARAMS, collapse = " "))) + paste("The element 'Analysis' in the recipe must contain these", + "elements:", paste(PARAMS, collapse = ", "))) } - if (!any(HORIZONS %in% recipe$Analysis$Horizon)) { + if (!any(HORIZONS %in% tolower(recipe$Analysis$Horizon))) { error(logger, - "The element 'Horizon' in the recipe should be one of the followings:", - paste(HORIZONS, collapse = " ")) + "The element 'Horizon' in the recipe must be one of the following:", + paste(HORIZONS, collapse = ", ")) } # Check temporal settings and # count the number of verifications if (!all(TIME_SETTINGS %in% names(recipe$Analysis$Time))) { - error(logger, - paste("The element 'Time' in the recipe should contain these elements:", + error(logger, + paste("The element 'Time' in the recipe must contain these elements:", paste(TIME_SETTINGS, collapse = " "))) } - if (is.null(recipe$Analysis$Time$sdate$fcst_year) || - recipe$Analysis$Time$sdate$fcst_year == 'None') { + ## TODO: Is this needed? + if (is.null(recipe$Analysis$Time$fcst_year) || + recipe$Analysis$Time$fcst_year == 'None') { stream <- "hindcast" - recipe$Analysis$Time$sdate$fcst_year <- 'YYYY' + # recipe$Analysis$Time$fcst_year <- 'YYYY' } else { stream <- "fcst" } - if (length(recipe$Analysis$Time$sdate$fcst_day) > 1 && - tolower(recipe$Analysis$Horizon) != "subseasonal") { + ## TODO: To be implemented in the future + # if (length(recipe$Analysis$Time$sdate$fcst_day) > 1 && + # tolower(recipe$Analysis$Horizon) != "subseasonal") { + # warn(logger, + # paste("Only subseasonal verification allows multiple forecast days."), + # "Element fcst_day in recipe set as 1.") + # recipe$Analysis$Time$sdate$fcst_day <- '01' + # } + ## TODO: Delete, this parameter was deprecated + # if (is.null(recipe$Analysis$Time$sdate$fcst_sday)) { + # error(logger, + # paste("The element 'fcst_sday' in the recipe should be defined.")) + # } + if (is.null(recipe$Analysis$Time$fcst_syear)) { warn(logger, - paste("Only subseasonal verification allows multiple forecast days."), - "Element fcst_day in recipe set as 1.") - recipe$Analysis$Time$sdate$fcst_day <- '01' + paste("The element 'fcst_year' is not defined in the recipe. + No forecast year will be used.")) } - if (is.null(recipe$Analysis$Time$sdate$fcst_sday)) { - error(logger, - paste("The element 'fcst_sday' in the recipe should be defined.")) - } - if (is.null(recipe$Analysis$Time$sdate$fcst_syear)) { - error(logger, - paste("The element 'fcst_syear' in the recipe should be defined.")) - } - - + ## TODO: Move this inside 'if'? fcst.sdate <- NULL for (syear in recipe$Analysis$Time$sdate$fcst_syear) { for (sday in recipe$Analysis$Time$sdate$fcst_sday) { @@ -72,22 +77,23 @@ check_recipe <- function(recipe, logger) { # Regrid checks: if (length(recipe$Analysis$Regrid) != 2) { error(logger, - "The 'Regrid' element should specified the 'method' and 'type'.") + "The 'Regrid' element should specify the 'method' and 'type'.") stop("EXECUTION FAILED") } -# more checks + # TODO: Add Workflow checks? # ... # calculate number of workflows to create for each variable and if (length(recipe$Analysis$Horizon) > 1) { - error(logger, "Only 1 Horizon can be specified in the recipe") - stop("EXECUTION FAILED") - } - nvar <- length(recipe$Analysis$Variables) - if (nvar > 2) { - error(logger, - "Only two type of Variables can be listed: ECVs and Indicators.") + error(logger, "Only one single Horizon can be specified in the recipe") stop("EXECUTION FAILED") } + ## TODO: Refine this + # nvar <- length(recipe$Analysis$Variables) + # if (nvar > 2) { + # error(logger, + # "Only two type of Variables can be listed: ECVs and Indicators.") + # stop("EXECUTION FAILED") + # } # remove NULL or None Indicators or ECVs from the recipe: if (!is.null(recipe$Analysis$Variables$Indicators) && !is.list(recipe$Analysis$Variables$Indicators)) { @@ -100,41 +106,44 @@ check_recipe <- function(recipe, logger) { -which(names(recipe$Analysis$Variables) == 'ECVs')] } # Only one Calibration method allowed: + ## TODO: Make sure this condition is correctly written if ((is.logical(recipe$Analysis$Workflow$Calibration[[1]]) && - recipe$Analysis$Workflow$Calibration[[1]] == FALSE) || - recipe$Analysis$Workflow$Calibration[[1]] == 'None' || - is.null(recipe$Analysis$Workflow$Calibration[[1]])) { + recipe$Analysis$Workflow$Calibration[[1]] == FALSE) || + recipe$Analysis$Workflow$Calibration[[1]] == 'None' || + is.null(recipe$Analysis$Workflow$Calibration[[1]])) { warn(logger, "There is no Calibration method selected, raw data verification.") - recipe$Analysis$Workflow$Calibration[[1]] <- FALSE + recipe$Analysis$Workflow$Calibration[[1]] <- 'raw' } else { # remove multiple calibration methods if (is.null(names(recipe$Analysis$Workflow$Calibration))) { error(logger, - "The 'Calibration' element should specified at least the 'method'.") + "The 'Calibration' element 'method' must be specified.") stop("EXECUTION FAILED") } } if ("Region" %in% names(recipe$Analysis)) { - nregions <- length(recipe$Analysis$Region$Regional) limits <- c('latmin', 'latmax', 'lonmin', 'lonmax') - for (i in 1:length(recipe$Analysis$Region)) { - if (!all(limits %in% names(recipe$Analysis$Region[[i]]))) { - limits <- paste(limits, collapse = " ") - error(logger, - paste("Each region defined in element 'Regional'", - "should have 4 elements:", - limits)) - stop("EXECUTION FAILED") - } - # are numeric? class list mode list + if (!all(limits %in% names(recipe$Analysis$Region))) { + error(recipe$Run$logger, paste("There must be 4 elements in 'Region':", + limits)) } + ## TODO: Implement multiple regions + # nregions <- length(recipe$Analysis$Region$Regional) + # for (i in 1:length(recipe$Analysis$Region)) { + # if (!all(limits %in% names(recipe$Analysis$Region[[i]]))) { + # limits <- paste(limits, collapse = " ") + # error(logger, + # paste("Each region defined in element 'Regional'", + # "should have 4 elements:", + # limits)) + # stop("EXECUTION FAILED") + # } + # # are numeric? class list mode list + # } } else { - error(logger, - paste("'Region'", - "should be defined", - limits)) + error(recipe$Run$logger, "'Region' must be defined.") stop("EXECUTION FAILED") } @@ -142,39 +151,42 @@ check_recipe <- function(recipe, logger) { # RUN CHECKS # --------------------------------------------------------------------- - RUN_FIELDS = c("Loglevel","Terminal","output_dir","code_dir") - LOG_LEVELS = c("INFO","DEBUG","WARNING","ERROR") + RUN_FIELDS = c("Loglevel", "Terminal", "output_dir", "code_dir") + LOG_LEVELS = c("INFO", "DEBUG", "WARN", "ERROR", "FATAL") - if (!any(names(recipe) %in% "Run")) { - error(logger, "The recipe should contain an element called 'Run'.") + if (!("Run" %in% names(recipe))) { + stop("The recipe must contain an element named 'Run'.") } if (!all(RUN_FIELDS %in% names(recipe$Run))) { - error(logger, paste0("Run should contain the fields: ", - paste(RUN_FIELDS,collapse=", "), ".")) + error(recipe$Run$logger, paste("Recipe element 'Run' must contain", + "all of the following fields:", + paste(RUN_FIELDS, collapse=", "), ".")) } if (!is.character(recipe$Run$output_dir)) { - error(logger, - paste("The Run element 'output_dir' in", recipe$filename,"file ", + error(recipe$Run$logger, + paste("The Run element 'output_dir' in", recipe$name, "file ", "should be a character string indicating the path ", - "where to save the outputs.")) + "where the outputs should be saved.")) } if (!is.character(recipe$Run$code_dir)) { - error(logger, - paste("The Run element 'code_dir' in", recipe$filename,"file ", + error(recipe$Run$logger, + paste("The Run element 'code_dir' in", recipe$name, "file ", "should be a character string indicating the path ", "where the code is.")) } if (!is.logical(recipe$Run$Terminal)) { - error(logger, - paste("The Run element 'Terminal' in", recipe$filename,"file ", - "should be a boolean value indicating wether to print or not the log", - "in the terminal.")) + error(recipe$Run$logger, + paste("The Run element 'Terminal' in", recipe$filename, "file ", + "should be a boolean value indicating whether or not to", + "print the logs in the terminal.")) } - if (!is.character(recipe$Run$Loglevel) || !any(recipe$Run$Loglevel %in% LOG_LEVELS)) { + ## TODO: Review this case, since default value is allowed + if (!is.character(recipe$Run$Loglevel) || + !any(recipe$Run$Loglevel %in% LOG_LEVELS)) { error(logger, - paste("The Run element 'Loglevel' in", recipe$filename,"file ", - "should be a character string indicating one of the levels available: ", - paste0(LOG_LEVELS,collapse='/'))) + paste("The Run element 'Loglevel' in", recipe$name, "file ", + "should be a character string specifying one of the levels available: ", + paste0(LOG_LEVELS, collapse='/'))) } # --------------------------------------------------------------------- -- GitLab From 3680e1cbca0c88df9d880a8e55a9b3fe69bb72e2 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Fri, 25 Nov 2022 10:20:18 +0100 Subject: [PATCH 03/21] Update check_recipe() --- .../testing_recipes/wrong_recipe_example.yml | 44 +++++++ tools/check_recipe.R | 124 +++++++++++------- 2 files changed, 117 insertions(+), 51 deletions(-) create mode 100644 modules/Loading/testing_recipes/wrong_recipe_example.yml diff --git a/modules/Loading/testing_recipes/wrong_recipe_example.yml b/modules/Loading/testing_recipes/wrong_recipe_example.yml new file mode 100644 index 00000000..e5d59ad8 --- /dev/null +++ b/modules/Loading/testing_recipes/wrong_recipe_example.yml @@ -0,0 +1,44 @@ +Description: + Author: V. Agudetse + Info: Incomplete recipe with incorrect fields to test the recipe checker. + +Analysis: + Horizon: Seasoning + Variables: + name: tas + freq: monthly_mean + Petaflops: + System: + name: system7c3s + Multimodel: False + Reference: + name: era5 + Time: + sdate: '1101' + fcst_year: '2020' + hcst_start: '1993' + hcst_end: '2016' + ftime_max: 6 + Region: + latmax: 10 + lonmin: 0 + lonmax: 20 + Regrid: + method: bilinear + type: to_system + Workflow: + Calibration: + method: + Skill: + metric: RPS RPSS + Probabilities: + percentiles: [[1/3, 2/3], [1/10, 9/10], [1/4, 2/4, 3/4]] + Indicators: + index: no + ncores: 7 + remove_NAs: yes + Output_format: S2S4E +Run: + Loglevel: INFO + Terminal: yes + output_dir: /esarchive/scratch/vagudets/repos/auto-s2s/out-logs/ diff --git a/tools/check_recipe.R b/tools/check_recipe.R index a25c5f80..dbbfd665 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -3,7 +3,7 @@ check_recipe <- function(recipe) { # recipe: yaml recipe already read it # output: errors or the total number of workflow (vars x regions) to compute - info(recipe$Run$logger, paste("Checking recipe", recipe$name)) + info(recipe$Run$logger, paste("Checking recipe:", recipe$name)) # --------------------------------------------------------------------- # ANALYSIS CHECKS @@ -13,30 +13,36 @@ check_recipe <- function(recipe) { PARAMS = c('Horizon', 'Time', 'Variables', 'Region', 'Regrid', 'Workflow', 'Datasets') HORIZONS <- c('subseasonal', 'seasonal', 'decadal') + # Define error status variable + error_status <- F # Check basic elements in recipe:Analysis: if (!("Analysis" %in% names(recipe))) { error(recipe$Run$logger, "The recipe must contain an element called 'Analysis'.") + error_status <- T } if (!all(PARAMS %in% names(recipe$Analysis))) { - error(logger, - paste("The element 'Analysis' in the recipe must contain these", - "elements:", paste(PARAMS, collapse = ", "))) + error(recipe$Run$logger, + paste0("The element 'Analysis' in the recipe must contain all of ", + "the following: ", paste(PARAMS, collapse = ", "), ".")) + error_status <- T } if (!any(HORIZONS %in% tolower(recipe$Analysis$Horizon))) { - error(logger, - "The element 'Horizon' in the recipe must be one of the following:", - paste(HORIZONS, collapse = ", ")) + error(recipe$Run$logger, + paste0("The element 'Horizon' in the recipe must be one of the ", + "following: ", paste(HORIZONS, collapse = ", "), ".")) + error_status <- T } # Check temporal settings and # count the number of verifications if (!all(TIME_SETTINGS %in% names(recipe$Analysis$Time))) { - error(logger, - paste("The element 'Time' in the recipe must contain these elements:", - paste(TIME_SETTINGS, collapse = " "))) + error(recipe$Run$logger, + paste0("The element 'Time' in the recipe must contain all of the ", + "following: ", paste(TIME_SETTINGS, collapse = ", "), ".")) + error_status <- T } ## TODO: Is this needed? if (is.null(recipe$Analysis$Time$fcst_year) || @@ -49,48 +55,49 @@ check_recipe <- function(recipe) { ## TODO: To be implemented in the future # if (length(recipe$Analysis$Time$sdate$fcst_day) > 1 && # tolower(recipe$Analysis$Horizon) != "subseasonal") { - # warn(logger, + # warn(recipe$Run$logger, # paste("Only subseasonal verification allows multiple forecast days."), # "Element fcst_day in recipe set as 1.") # recipe$Analysis$Time$sdate$fcst_day <- '01' # } ## TODO: Delete, this parameter was deprecated # if (is.null(recipe$Analysis$Time$sdate$fcst_sday)) { - # error(logger, + # error(recipe$Run$logger, # paste("The element 'fcst_sday' in the recipe should be defined.")) # } - if (is.null(recipe$Analysis$Time$fcst_syear)) { - warn(logger, - paste("The element 'fcst_year' is not defined in the recipe. - No forecast year will be used.")) + if (is.null(recipe$Analysis$Time$fcst_year)) { + warn(recipe$Run$logger, + paste("The element 'fcst_year' is not defined in the recipe.", + "No forecast year will be used.")) } - ## TODO: Move this inside 'if'? + ## TODO: Adapt and move this inside 'if'? fcst.sdate <- NULL - for (syear in recipe$Analysis$Time$sdate$fcst_syear) { - for (sday in recipe$Analysis$Time$sdate$fcst_sday) { - fcst.sdate <- c(fcst.sdate, - paste0(syear, - sprintf("%04d", as.numeric(sday)))) - } + for (syear in recipe$Analysis$Time$fcst_year) { + for (sday in recipe$Analysis$Time$sdate) { + fcst.sdate <- c(fcst.sdate, + paste0(syear, + sprintf("%04d", as.numeric(sday)))) + } } fcst.sdate <- list(stream = stream, fcst.sdate = fcst.sdate) # Regrid checks: if (length(recipe$Analysis$Regrid) != 2) { - error(logger, - "The 'Regrid' element should specify the 'method' and 'type'.") - stop("EXECUTION FAILED") + error(recipe$Run$logger, + "The 'Regrid' element must specify the 'method' and 'type'.") + error_status <- T } # TODO: Add Workflow checks? # ... # calculate number of workflows to create for each variable and if (length(recipe$Analysis$Horizon) > 1) { - error(logger, "Only one single Horizon can be specified in the recipe") - stop("EXECUTION FAILED") + error(recipe$Run$logger, + "Only one single Horizon can be specified in the recipe") + error_status <- T } ## TODO: Refine this # nvar <- length(recipe$Analysis$Variables) # if (nvar > 2) { - # error(logger, + # error(recipe$Run$logger, # "Only two type of Variables can be listed: ECVs and Indicators.") # stop("EXECUTION FAILED") # } @@ -107,34 +114,36 @@ check_recipe <- function(recipe) { } # Only one Calibration method allowed: ## TODO: Make sure this condition is correctly written - if ((is.logical(recipe$Analysis$Workflow$Calibration[[1]]) && - recipe$Analysis$Workflow$Calibration[[1]] == FALSE) || - recipe$Analysis$Workflow$Calibration[[1]] == 'None' || - is.null(recipe$Analysis$Workflow$Calibration[[1]])) { - warn(logger, - "There is no Calibration method selected, raw data verification.") - recipe$Analysis$Workflow$Calibration[[1]] <- 'raw' + if ((is.logical(recipe$Analysis$Workflow$Calibration$method) && + recipe$Analysis$Workflow$Calibration$method == FALSE) || + recipe$Analysis$Workflow$Calibration$method == 'None' || + is.null(recipe$Analysis$Workflow$Calibration$method)) { + warn(recipe$Run$logger, + "No Calibration method was specified, raw data verification.") + recipe$Analysis$Workflow$Calibration$method <- 'raw' } else { # remove multiple calibration methods if (is.null(names(recipe$Analysis$Workflow$Calibration))) { - error(logger, + error(recipe$Run$logger, "The 'Calibration' element 'method' must be specified.") - stop("EXECUTION FAILED") + error_status <- T } - } + } if ("Region" %in% names(recipe$Analysis)) { limits <- c('latmin', 'latmax', 'lonmin', 'lonmax') if (!all(limits %in% names(recipe$Analysis$Region))) { - error(recipe$Run$logger, paste("There must be 4 elements in 'Region':", - limits)) + error(recipe$Run$logger, + paste0("There must be 4 elements in 'Region': ", + paste(limits, collapse = ", "), ".")) + error_status <- T } ## TODO: Implement multiple regions # nregions <- length(recipe$Analysis$Region$Regional) # for (i in 1:length(recipe$Analysis$Region)) { # if (!all(limits %in% names(recipe$Analysis$Region[[i]]))) { # limits <- paste(limits, collapse = " ") - # error(logger, + # error(recipe$Run$logger, # paste("Each region defined in element 'Regional'", # "should have 4 elements:", # limits)) @@ -144,7 +153,7 @@ check_recipe <- function(recipe) { # } } else { error(recipe$Run$logger, "'Region' must be defined.") - stop("EXECUTION FAILED") + error_status <- T } # --------------------------------------------------------------------- @@ -161,24 +170,28 @@ check_recipe <- function(recipe) { error(recipe$Run$logger, paste("Recipe element 'Run' must contain", "all of the following fields:", paste(RUN_FIELDS, collapse=", "), ".")) + error_status <- T } if (!is.character(recipe$Run$output_dir)) { error(recipe$Run$logger, - paste("The Run element 'output_dir' in", recipe$name, "file ", - "should be a character string indicating the path ", - "where the outputs should be saved.")) + paste("The Run element 'output_dir' in", recipe$name, "file", + "should be a character string indicating the path where", + "the outputs should be saved.")) + error_status <- T } if (!is.character(recipe$Run$code_dir)) { error(recipe$Run$logger, paste("The Run element 'code_dir' in", recipe$name, "file ", "should be a character string indicating the path ", "where the code is.")) + error_status <- T } if (!is.logical(recipe$Run$Terminal)) { error(recipe$Run$logger, - paste("The Run element 'Terminal' in", recipe$filename, "file ", + paste("The Run element 'Terminal' in", recipe$name, "file ", "should be a boolean value indicating whether or not to", "print the logs in the terminal.")) + error_status <- T } ## TODO: Review this case, since default value is allowed if (!is.character(recipe$Run$Loglevel) || @@ -187,6 +200,7 @@ check_recipe <- function(recipe) { paste("The Run element 'Loglevel' in", recipe$name, "file ", "should be a character string specifying one of the levels available: ", paste0(LOG_LEVELS, collapse='/'))) + error_status <- T } # --------------------------------------------------------------------- @@ -194,10 +208,18 @@ check_recipe <- function(recipe) { # --------------------------------------------------------------------- # Check workflow: need to define restrictions? # e.g. only one calibration method - nverifications <- check_number_of_dependent_verifications(recipe) - info(logger, paste("Start Dates", paste(fcst.sdate, collapse = " "))) - info(logger, "Recipe checked succsessfully.") - return(append(nverifications, fcst.sdate)) + ## TODO: Implement numbr of dependent verifications + #nverifications <- check_number_of_dependent_verifications(recipe) + info(recipe$Run$logger, paste("Start Dates:", + paste(fcst.sdate, collapse = " "))) + + # Return error if any check has failed + if (error_status) { + stop("Recipe check failed. Please check the logs for a list of errors.") + } else { + info(recipe$Run$logger, "##### RECIPE CHECKS WERE SUCCESSFULL #####.") + # return(append(nverifications, fcst.sdate)) + } } check_number_of_dependent_verifications <- function(recipe) { -- GitLab From cae758b0e71e818fc27f5b76acda85f5f01accc1 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Mon, 12 Dec 2022 15:25:19 +0100 Subject: [PATCH 04/21] Add example of main recipe --- recipes/recipe_example.yml | 58 ++++++++++++++++++++++++++++++++++++++ tools/divide_recipe.R | 6 ++-- 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 recipes/recipe_example.yml diff --git a/recipes/recipe_example.yml b/recipes/recipe_example.yml new file mode 100644 index 00000000..27f74df9 --- /dev/null +++ b/recipes/recipe_example.yml @@ -0,0 +1,58 @@ +################################################################################ +## RECIPE DESCRIPTION +################################################################################ + +Description: + Author: V. Agudetse + Info: Test for main recipe + +################################################################################ +## ANALYSIS CONFIGURATION +################################################################################ + +Analysis: + Horizon: Seasonal + Variables: # ECVs and Indicators? + - {name: tas, freq: monthly_mean} + - {name: prlr, freq: monthly_mean} + Datasets: + System: # multiple systems for single model, split if Multimodel = F + - {name: system7c3s} + - {name: system5c3s} + Multimodel: False # single option + Reference: + name: era5 # multiple references for single model? + Time: + sdate: '1101' # list, split + fcst_year: '2020' # list, don't split, handled internally + hcst_start: '1993' # single option + hcst_end: '2016' # single option + ftime_min: 1 # single option + ftime_max: 6 # singl eoption + Region: # multiple lists, split? Add region name if length(Region) > 1 + - {name: "global", latmin: -90, latmax: 90, lonmin: 0, lonmax: 360} + - {name: "nino34", latmin: -5, latmax: 5, lonmin: -10, lonmax: 60} + Regrid: + method: bilinear # allow multiple methods? + type: to_system + Workflow: + Calibration: + method: mse_min # list, split? + Skill: + metric: RPS, RPSS, CRPS, CRPSS, FRPSS, BSS10, BSS90, mean_bias, mean_bias_SS # list, don't split + Probabilities: + percentiles: [[1/3, 2/3], [1/10, 9/10], [1/4, 2/4, 3/4]] # list, don't split + Indicators: + index: no # ? + ncores: 7 + remove_NAs: yes # bool, don't split + Output_format: S2S4E # string, don't split + +################################################################################ +## Run CONFIGURATION +################################################################################ +Run: + Loglevel: INFO + Terminal: yes + output_dir: /esarchive/scratch/vagudets/repos/auto-s2s/out-logs/ + code_dir: /esarchive/scratch/vagudets/repos/auto-s2s/ diff --git a/tools/divide_recipe.R b/tools/divide_recipe.R index dafc8704..8f4ef35a 100644 --- a/tools/divide_recipe.R +++ b/tools/divide_recipe.R @@ -2,6 +2,8 @@ # verifications: the output from check_recipe # folder: the name of the output folder for this run # logger: the log file obtain from prepare_outputs + +## TODO: Rethink strategy and adapt function accordingly divide_recipe <- function(recipe, verifications, folder, logger) { info(logger, "Spliting recipe in single verifications.") beta_recipe <- list(Description = append(recipe$Description, @@ -14,8 +16,8 @@ divide_recipe <- function(recipe, verifications, folder, logger) { Regrid = recipe$Analysis$Regrid, Workflow = recipe$Analysis$Workflow, Output_format = - recipe$Analysis$Output_format), - Run = recipe$Run) + recipe$Analysis$Output_format), + Run = recipe$Run) # duplicate recipe by Variables considering dep and indep: all_recipes <- list(beta_recipe) i <- 1 # to get track of the recipe number -- GitLab From fbb329af5930396261b8f8e4e28ff3cca2ddd106 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Tue, 13 Dec 2022 11:43:21 +0100 Subject: [PATCH 05/21] Formatting --- tools/check_recipe.R | 2 +- tools/data_summary.R | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index dbbfd665..316b26f7 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -26,7 +26,7 @@ check_recipe <- function(recipe) { if (!all(PARAMS %in% names(recipe$Analysis))) { error(recipe$Run$logger, paste0("The element 'Analysis' in the recipe must contain all of ", - "the following: ", paste(PARAMS, collapse = ", "), ".")) + "the following: ", paste(PARAMS, collapse = ", "), ".")) error_status <- T } diff --git a/tools/data_summary.R b/tools/data_summary.R index 34b6bd6e..cddd4f9e 100644 --- a/tools/data_summary.R +++ b/tools/data_summary.R @@ -8,15 +8,15 @@ data_summary <- function(data_cube, recipe) { # Get name, leadtime months and date range object_name <- deparse(substitute(data_cube)) if (recipe$Analysis$Variables$freq == "monthly_mean") { - date_format <- '%b %Y' + date_format <- "%b %Y" } else if (recipe$Analysis$Variables$freq == "daily_mean") { - date_format <- '%b %d %Y' + date_format <- "%b %d %Y" } - months <- unique(format(as.Date(data_cube$Dates[[1]]), format = '%B')) - months <- paste(as.character(months), collapse=", ") + months <- unique(format(as.Date(data_cube$Dates[[1]]), format = "%B")) + months <- paste(as.character(months), collapse = ", ") sdate_min <- format(min(as.Date(data_cube$Dates[[1]])), format = date_format) sdate_max <- format(max(as.Date(data_cube$Dates[[1]])), format = date_format) - + # Create log instance and sink output to logfile and terminal info(recipe$Run$logger, "DATA SUMMARY:") sink(recipe$Run$logfile, append = TRUE, split = TRUE) @@ -29,4 +29,3 @@ data_summary <- function(data_cube, recipe) { print("---------------------------------------------") sink() } - -- GitLab From 8da9afb6c3de5ba472b7aaf22f69c70a4d7662d3 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Thu, 15 Dec 2022 15:09:52 +0100 Subject: [PATCH 06/21] Update wrong example and recipe checker --- .../testing_recipes/wrong_recipe_example.yml | 2 +- tools/check_recipe.R | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/modules/Loading/testing_recipes/wrong_recipe_example.yml b/modules/Loading/testing_recipes/wrong_recipe_example.yml index e5d59ad8..12e2fc06 100644 --- a/modules/Loading/testing_recipes/wrong_recipe_example.yml +++ b/modules/Loading/testing_recipes/wrong_recipe_example.yml @@ -15,7 +15,7 @@ Analysis: name: era5 Time: sdate: '1101' - fcst_year: '2020' + fcst_syear: '2020' hcst_start: '1993' hcst_end: '2016' ftime_max: 6 diff --git a/tools/check_recipe.R b/tools/check_recipe.R index 316b26f7..8f945b6c 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -139,16 +139,21 @@ check_recipe <- function(recipe) { error_status <- T } ## TODO: Implement multiple regions - # nregions <- length(recipe$Analysis$Region$Regional) + # nregions <- length(recipe$Analysis$Region) # for (i in 1:length(recipe$Analysis$Region)) { # if (!all(limits %in% names(recipe$Analysis$Region[[i]]))) { # limits <- paste(limits, collapse = " ") # error(recipe$Run$logger, - # paste("Each region defined in element 'Regional'", - # "should have 4 elements:", - # limits)) - # stop("EXECUTION FAILED") + # paste0("Each region defined in element 'Region' ", + # "should have 4 elements: ", + # paste(limits, collapse = ", "), ".")) + # error_status <- T # } + # if (length(recipe$Analysis$Region) > 1) { + # if (!("name" %in% names(recipe$Analysis$Region[[i]]))) { + # error(recipe$Run$logger, + # paste("If multiple regions are requested, each region must", + # "have a 'name'".) # # are numeric? class list mode list # } } else { @@ -182,7 +187,7 @@ check_recipe <- function(recipe) { if (!is.character(recipe$Run$code_dir)) { error(recipe$Run$logger, paste("The Run element 'code_dir' in", recipe$name, "file ", - "should be a character string indicating the path ", + "should be a character string indicating the path", "where the code is.")) error_status <- T } @@ -197,8 +202,8 @@ check_recipe <- function(recipe) { if (!is.character(recipe$Run$Loglevel) || !any(recipe$Run$Loglevel %in% LOG_LEVELS)) { error(logger, - paste("The Run element 'Loglevel' in", recipe$name, "file ", - "should be a character string specifying one of the levels available: ", + paste("The Run element 'Loglevel' in", recipe$name, "file", + "should be a character string specifying one of the levels available:", paste0(LOG_LEVELS, collapse='/'))) error_status <- T } @@ -208,16 +213,17 @@ check_recipe <- function(recipe) { # --------------------------------------------------------------------- # Check workflow: need to define restrictions? # e.g. only one calibration method - ## TODO: Implement numbr of dependent verifications + ## TODO: Implement number of dependent verifications #nverifications <- check_number_of_dependent_verifications(recipe) info(recipe$Run$logger, paste("Start Dates:", paste(fcst.sdate, collapse = " "))) # Return error if any check has failed if (error_status) { - stop("Recipe check failed. Please check the logs for a list of errors.") + error(recipe$Run$logger, "RECIPE CHECK FAILED.") + stop("The recipe contains some errors. The full list is in the logs.") } else { - info(recipe$Run$logger, "##### RECIPE CHECKS WERE SUCCESSFULL #####.") + info(recipe$Run$logger, "##### RECIPE CHECK SUCCESSFULL #####") # return(append(nverifications, fcst.sdate)) } } -- GitLab From d3193f9aaf3a4dba672b277f36befd40ce6e022e Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Tue, 20 Dec 2022 16:07:51 +0100 Subject: [PATCH 07/21] update checker --- tools/check_recipe.R | 104 ++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index 8f945b6c..a70a5b90 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -46,12 +46,13 @@ check_recipe <- function(recipe) { } ## TODO: Is this needed? if (is.null(recipe$Analysis$Time$fcst_year) || - recipe$Analysis$Time$fcst_year == 'None') { + tolower(recipe$Analysis$Time$fcst_year) == 'none') { stream <- "hindcast" # recipe$Analysis$Time$fcst_year <- 'YYYY' } else { stream <- "fcst" } + ## TODO: To be implemented in the future # if (length(recipe$Analysis$Time$sdate$fcst_day) > 1 && # tolower(recipe$Analysis$Horizon) != "subseasonal") { @@ -65,6 +66,7 @@ check_recipe <- function(recipe) { # error(recipe$Run$logger, # paste("The element 'fcst_sday' in the recipe should be defined.")) # } + if (is.null(recipe$Analysis$Time$fcst_year)) { warn(recipe$Run$logger, paste("The element 'fcst_year' is not defined in the recipe.", @@ -88,12 +90,13 @@ check_recipe <- function(recipe) { } # TODO: Add Workflow checks? # ... - # calculate number of workflows to create for each variable and + # calculate number of workflows to create for each variable and if (length(recipe$Analysis$Horizon) > 1) { error(recipe$Run$logger, "Only one single Horizon can be specified in the recipe") error_status <- T } + ## TODO: Refine this # nvar <- length(recipe$Analysis$Variables) # if (nvar > 2) { @@ -101,6 +104,7 @@ check_recipe <- function(recipe) { # "Only two type of Variables can be listed: ECVs and Indicators.") # stop("EXECUTION FAILED") # } + # remove NULL or None Indicators or ECVs from the recipe: if (!is.null(recipe$Analysis$Variables$Indicators) && !is.list(recipe$Analysis$Variables$Indicators)) { @@ -112,32 +116,15 @@ check_recipe <- function(recipe) { recipe$Analysis$Variables <- recipe$Analysis$Variables[ -which(names(recipe$Analysis$Variables) == 'ECVs')] } - # Only one Calibration method allowed: - ## TODO: Make sure this condition is correctly written - if ((is.logical(recipe$Analysis$Workflow$Calibration$method) && - recipe$Analysis$Workflow$Calibration$method == FALSE) || - recipe$Analysis$Workflow$Calibration$method == 'None' || - is.null(recipe$Analysis$Workflow$Calibration$method)) { - warn(recipe$Run$logger, - "No Calibration method was specified, raw data verification.") - recipe$Analysis$Workflow$Calibration$method <- 'raw' - } else { - # remove multiple calibration methods - if (is.null(names(recipe$Analysis$Workflow$Calibration))) { - error(recipe$Run$logger, - "The 'Calibration' element 'method' must be specified.") - error_status <- T - } - } - if ("Region" %in% names(recipe$Analysis)) { - limits <- c('latmin', 'latmax', 'lonmin', 'lonmax') - if (!all(limits %in% names(recipe$Analysis$Region))) { - error(recipe$Run$logger, - paste0("There must be 4 elements in 'Region': ", - paste(limits, collapse = ", "), ".")) - error_status <- T - } + # Region checks: + LIMITS <- c('latmin', 'latmax', 'lonmin', 'lonmax') + if (!all(LIMITS %in% names(recipe$Analysis$Region))) { + error(recipe$Run$logger, + paste0("There must be 4 elements in 'Region': ", + paste(LIMITS, collapse = ", "), ".")) + error_status <- T + } ## TODO: Implement multiple regions # nregions <- length(recipe$Analysis$Region) # for (i in 1:length(recipe$Analysis$Region)) { @@ -156,11 +143,66 @@ check_recipe <- function(recipe) { # "have a 'name'".) # # are numeric? class list mode list # } + # --------------------------------------------------------------------- + # WORKFLOW CHECKS + # --------------------------------------------------------------------- + + # Only one Calibration method allowed: + if ((is.logical(recipe$Analysis$Workflow$Calibration$method) && + recipe$Analysis$Workflow$Calibration$method == FALSE) || + tolower(recipe$Analysis$Workflow$Calibration$method) == 'none' || + is.null(recipe$Analysis$Workflow$Calibration$method)) { + warn(recipe$Run$logger, + "No Calibration method was specified, raw data verification.") + recipe$Analysis$Workflow$Calibration$method <- 'raw' } else { - error(recipe$Run$logger, "'Region' must be defined.") + if (is.null(recipe$Analysis$Workflow$Calibration$method)) { + error(recipe$Run$logger, + "The 'Calibration' element 'method' must be specified.") + error_status <- T + } + } + # Anomalies + if ("Anomalies" %in% names(recipe$Analysis$Workflow)) { + if (!is.null(recipe$Analysis$Workflow$Anomalies$compute)) { + error(recipe$Run$logger, + "Parameter 'compute' must be defined under 'Anomalies'.") + error_status <- T + } else if (!(is.logical(recipe$Analysis$Workflows$Anomalies$compute))) { + error(recipe$Run$logger, + paste("Parameter 'Anomalies:compute' must be a logical value", + "(True/False or yes/no).")) + error_status <- T + } else if ((recipe$Analysis$Workflows$Anomalies$compute) && + (!is.logical(recipe$Analysis$Workflow$Anomalies))) { + error(recipe$Run$logger, + paste("If anomaly computation is requested, parameter", + "'cross_validation' must be defined under 'Anomalies', + and it must be a logical value (True/False or yes/no).")) + error_status <- T + } + } + # Skill + if (("Skill" %in% names(recipe$Analysis$Workflow)) && + (is.null(recipe$Analysis$Workflow$Skill$metric))) { + error(recipe$Run$logger, + "Parameter 'metric' must be defined under 'Skill'.") error_status <- T } - + # Probabilities + if ("Probabilities" %in% names(recipe$Analysis$Workflow)) { + if (is.null(recipe$Analysis$Workflow$Probabilities$percentiles)) { + error(recipe$Run$logger, + "Parameter 'percentiles' must be defined under 'Skill'.") + error_status <- T + } else if (!is.list(recipe$Analysis$Workflow$Probabilities$percentiles)) { + error(recipe$Run$logger, + paste("Parameter 'Probabilities:percentiles' expects a list.", + "See documentation in the wiki for examples.")) + error_status <- T + } + } + # --------------------------------------------------------------------- # RUN CHECKS # --------------------------------------------------------------------- @@ -215,8 +257,8 @@ check_recipe <- function(recipe) { # e.g. only one calibration method ## TODO: Implement number of dependent verifications #nverifications <- check_number_of_dependent_verifications(recipe) - info(recipe$Run$logger, paste("Start Dates:", - paste(fcst.sdate, collapse = " "))) + # info(recipe$Run$logger, paste("Start Dates:", + # paste(fcst.sdate, collapse = " "))) # Return error if any check has failed if (error_status) { -- GitLab From a20f175b79fe2910ce25851d4aa7276b537fc087 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Tue, 20 Dec 2022 16:08:15 +0100 Subject: [PATCH 08/21] Add recipe checker to prepare_outputs --- modules/test_seasonal.R | 2 +- tools/prepare_outputs.R | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/test_seasonal.R b/modules/test_seasonal.R index 854e293f..c74fd1b1 100644 --- a/modules/test_seasonal.R +++ b/modules/test_seasonal.R @@ -6,7 +6,7 @@ source("modules/Saving/Saving.R") source("modules/Visualization/Visualization.R") recipe_file <- "modules/Loading/testing_recipes/recipe_seasonal-tests.yml" -recipe <- prepare_outputs(recipe_file) +recipe <- prepare_outputs(recipe_file, enable_checks = F) ## archive <- read_yaml(paste0(recipe$Run$code_dir, "conf/archive.yml"))$archive # Load datasets diff --git a/tools/prepare_outputs.R b/tools/prepare_outputs.R index da5d2562..d6eb970e 100644 --- a/tools/prepare_outputs.R +++ b/tools/prepare_outputs.R @@ -20,10 +20,11 @@ #' #'@export -prepare_outputs <- function(recipe_file) { +prepare_outputs <- function(recipe_file, + enable_checks = TRUE) { -# recipe: the content of the readed recipe -# file: the recipe file name +# recipe_file: path to recipe YAML file +# enable_checker: Whether or not to run the recipe checker recipe <- read_yaml(recipe_file) recipe$recipe_path <- recipe_file @@ -33,32 +34,30 @@ prepare_outputs <- function(recipe_file) { # Create output folders: folder_name <- paste0(gsub(".yml", "", gsub("/", "_", recipe$name)), "_", gsub(" ", "", gsub(":", "", gsub("-", "", Sys.time())))) - print("Saving all outputs to:") print(output_dir) print(folder_name) - dir.create(file.path(output_dir, folder_name, 'outputs'), recursive = TRUE) dir.create(file.path(output_dir, folder_name, 'logs')) dir.create(file.path(output_dir, folder_name, 'logs', 'recipes')) - + # Copy recipe to output folder file.copy(recipe$recipe_path, file.path(output_dir, folder_name, 'logs', 'recipes')) - + # Create log output file logfile <- file.path(output_dir, folder_name, 'logs', 'log.txt') file.create(logfile) - # Set default behaviour of log output file: + # Set default behaviour of logger if (is.null(recipe$Run)) { recipe$Run <- list(Loglevel = 'INFO', Terminal = TRUE) } if (is.null(recipe$Run$Loglevel)) { recipe$Run$Loglevel <- 'INFO' } - if (!is.logical(recipe$Run$Terminal)) { recipe$Run$Terminal <- TRUE } + # logger set-up if (recipe$Run$Terminal) { logger <- log4r::logger(threshold = recipe$Run$Loglevel, appenders = list(console_appender(layout = default_log_layout()), @@ -69,10 +68,13 @@ prepare_outputs <- function(recipe_file) { appenders = list(file_appende(logfile, append = TRUE, layout = default_log_layout()))) } - recipe$Run$output_dir <- file.path(output_dir, folder_name) recipe$Run$logger <- logger recipe$Run$logfile <- logfile + # Run recipe checker + if (enable_checks) { + check_recipe(recipe) + } return(recipe) } -- GitLab From ba419683e13cc1d14217b0116edabbb22bf317e2 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Tue, 20 Dec 2022 16:18:58 +0100 Subject: [PATCH 09/21] Print full path to recipe; fix bug in Anomalies check --- tools/check_recipe.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index a70a5b90..c17a7cd9 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -3,7 +3,7 @@ check_recipe <- function(recipe) { # recipe: yaml recipe already read it # output: errors or the total number of workflow (vars x regions) to compute - info(recipe$Run$logger, paste("Checking recipe:", recipe$name)) + info(recipe$Run$logger, paste("Checking recipe:", recipe$path)) # --------------------------------------------------------------------- # ANALYSIS CHECKS @@ -164,7 +164,7 @@ check_recipe <- function(recipe) { } # Anomalies if ("Anomalies" %in% names(recipe$Analysis$Workflow)) { - if (!is.null(recipe$Analysis$Workflow$Anomalies$compute)) { + if (is.null(recipe$Analysis$Workflow$Anomalies$compute)) { error(recipe$Run$logger, "Parameter 'compute' must be defined under 'Anomalies'.") error_status <- T -- GitLab From 89ed146257cbded2fb8e7cc52cad5c54a0bc07d5 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Tue, 20 Dec 2022 16:19:48 +0100 Subject: [PATCH 10/21] Add TODO --- tools/check_recipe.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index c17a7cd9..2381309b 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -1,7 +1,7 @@ check_recipe <- function(recipe) { # recipe: yaml recipe already read it - # output: errors or the total number of workflow (vars x regions) to compute + ## TODO: Adapt to decadal case info(recipe$Run$logger, paste("Checking recipe:", recipe$path)) -- GitLab From 508683900950925f1bb894a447f49d2cdc34e841 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Tue, 20 Dec 2022 16:22:02 +0100 Subject: [PATCH 11/21] Fix bug caused by typo --- tools/check_recipe.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index 2381309b..153c4a18 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -168,12 +168,12 @@ check_recipe <- function(recipe) { error(recipe$Run$logger, "Parameter 'compute' must be defined under 'Anomalies'.") error_status <- T - } else if (!(is.logical(recipe$Analysis$Workflows$Anomalies$compute))) { + } else if (!(is.logical(recipe$Analysis$Workflow$Anomalies$compute))) { error(recipe$Run$logger, paste("Parameter 'Anomalies:compute' must be a logical value", "(True/False or yes/no).")) error_status <- T - } else if ((recipe$Analysis$Workflows$Anomalies$compute) && + } else if ((recipe$Analysis$Workflow$Anomalies$compute) && (!is.logical(recipe$Analysis$Workflow$Anomalies))) { error(recipe$Run$logger, paste("If anomaly computation is requested, parameter", -- GitLab From eb36caeeb545ce7811d2f8d2fb239f59b6f71a0a Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Tue, 20 Dec 2022 16:35:37 +0100 Subject: [PATCH 12/21] Fix pipeline --- tools/check_recipe.R | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index 153c4a18..fcd30ef3 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -3,16 +3,18 @@ check_recipe <- function(recipe) { # recipe: yaml recipe already read it ## TODO: Adapt to decadal case - info(recipe$Run$logger, paste("Checking recipe:", recipe$path)) + info(recipe$Run$logger, paste("Checking recipe:", recipe$recipe_path)) # --------------------------------------------------------------------- # ANALYSIS CHECKS # --------------------------------------------------------------------- - TIME_SETTINGS = c('sdate', 'ftime_min', 'ftime_max', 'hcst_start', 'hcst_end') - PARAMS = c('Horizon', 'Time', 'Variables', 'Region', 'Regrid', 'Workflow', - 'Datasets') - HORIZONS <- c('subseasonal', 'seasonal', 'decadal') + TIME_SETTINGS_SEASONAL <- c("sdate", "ftime_min", "ftime_max", "hcst_start", + "hcst_end") + TIME_SETTINGS_DECADAL <- c("ftime_min", "ftime_max", "hcst_start", "hcst_end") + PARAMS <- c("Horizon", "Time", "Variables", "Region", "Regrid", "Workflow", + "Datasets") + HORIZONS <- c("subseasonal", "seasonal", "decadal") # Define error status variable error_status <- F @@ -38,11 +40,22 @@ check_recipe <- function(recipe) { } # Check temporal settings and # count the number of verifications - if (!all(TIME_SETTINGS %in% names(recipe$Analysis$Time))) { - error(recipe$Run$logger, - paste0("The element 'Time' in the recipe must contain all of the ", - "following: ", paste(TIME_SETTINGS, collapse = ", "), ".")) - error_status <- T + if (tolower(recipe$Analysis$Horizon) == "seasonal") { + if (!all(TIME_SETTINGS_SEASONAL %in% names(recipe$Analysis$Time))) { + error(recipe$Run$logger, + paste0("The element 'Time' in the recipe must contain all of the ", + "following: ", paste(TIME_SETTINGS_SEASONAL, + collapse = ", "), ".")) + error_status <- T + } + } else if (tolower(recipe$Analysis$Horizon) == "decadal") { + if (!all(TIME_SETTINGS_DECADAL %in% names(recipe$Analysis$Time))) { + error(recipe$Run$logger, + paste0("The element 'Time' in the recipe must contain all of the ", + "following: ", paste(TIME_SETTINGS_DECADAL, + collapse = ", "), ".")) + error_status <- T + } } ## TODO: Is this needed? if (is.null(recipe$Analysis$Time$fcst_year) || -- GitLab From 7a985badf5d0cb06eec6504d56f20c1100ca1d57 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Fri, 23 Dec 2022 15:01:01 +0100 Subject: [PATCH 13/21] Add more detailed time section checks --- tools/check_recipe.R | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index fcd30ef3..5fca3597 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -38,8 +38,7 @@ check_recipe <- function(recipe) { "following: ", paste(HORIZONS, collapse = ", "), ".")) error_status <- T } - # Check temporal settings and - # count the number of verifications + # Check time settings if (tolower(recipe$Analysis$Horizon) == "seasonal") { if (!all(TIME_SETTINGS_SEASONAL %in% names(recipe$Analysis$Time))) { error(recipe$Run$logger, @@ -57,6 +56,42 @@ check_recipe <- function(recipe) { error_status <- T } } + # Check ftime_min and ftime_max + if ((!(recipe$Analysis$Time$ftime_min > 0)) || + (!is.integer(recipe$Analysis$Time$ftime_min))) { + error(recipe$Run$logger, + "The element 'ftime_min' must be an integer larger than 0.") + error_status <- T + } + if ((!(recipe$Analysis$Time$ftime_max > 0)) || + (!is.integer(recipe$Analysis$Time$ftime_max))) { + error(recipe$Run$logger, + "The element 'ftime_max' must be an integer larger than 0.") + error_status <- T + } + if (recipe$Analysis$Time$ftime_max < recipe$Analysis$Time$ftime_min) { + error(recipe$Run$logger, + "'ftime_max' cannot be smaller than 'ftime_min'.") + error_status <- T + } + # Check consistency of hindcast years + if (!(as.numeric(recipe$Analysis$Time$hcst_start) %% 1 == 0) || + (!(recipe$Analysis$Time$hcst_start > 0))) { + error(recipe$Run$logger, + "The element 'hcst_start' must be a valid year.") + error_status <- T + } + if (!(as.numeric(recipe$Analysis$Time$hcst_end) %% 1 == 0) || + (!(recipe$Analysis$Time$hcst_end > 0))) { + error(recipe$Run$logger, + "The element 'hcst_end' must be a valid year.") + error_status <- T + } + if (recipe$Analysis$Time$hcst_end < recipe$Analysis$Time$hcst_start) { + error(recipe$Run$logger, + "'hcst_end' cannot be smaller than 'hcst_start'.") + error_status <- T + } ## TODO: Is this needed? if (is.null(recipe$Analysis$Time$fcst_year) || tolower(recipe$Analysis$Time$fcst_year) == 'none') { -- GitLab From d7ea2794b6df6e1492888546679aeba8758f63e3 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Thu, 29 Dec 2022 15:11:47 +0100 Subject: [PATCH 14/21] Update script to divide recipe --- ...ample.yml => recipe_splitting_example.yml} | 16 +-- tools/divide_recipe.R | 98 ++++++++++--------- 2 files changed, 61 insertions(+), 53 deletions(-) rename recipes/{recipe_example.yml => recipe_splitting_example.yml} (86%) diff --git a/recipes/recipe_example.yml b/recipes/recipe_splitting_example.yml similarity index 86% rename from recipes/recipe_example.yml rename to recipes/recipe_splitting_example.yml index 27f74df9..e62611ab 100644 --- a/recipes/recipe_example.yml +++ b/recipes/recipe_splitting_example.yml @@ -4,7 +4,7 @@ Description: Author: V. Agudetse - Info: Test for main recipe + Info: Test for recipe splitting ################################################################################ ## ANALYSIS CONFIGURATION @@ -21,23 +21,25 @@ Analysis: - {name: system5c3s} Multimodel: False # single option Reference: - name: era5 # multiple references for single model? + - {name: era5} # multiple references for single model? Time: - sdate: '1101' # list, split + sdate: # list, split + - '1101' + - '1201' fcst_year: '2020' # list, don't split, handled internally hcst_start: '1993' # single option hcst_end: '2016' # single option ftime_min: 1 # single option - ftime_max: 6 # singl eoption + ftime_max: 6 # single option Region: # multiple lists, split? Add region name if length(Region) > 1 - - {name: "global", latmin: -90, latmax: 90, lonmin: 0, lonmax: 360} + - {name: "global", latmin: -90, latmax: 90, lonmin: 0, lonmax: 359.9} - {name: "nino34", latmin: -5, latmax: 5, lonmin: -10, lonmax: 60} Regrid: - method: bilinear # allow multiple methods? + method: bilinear ## TODO: allow multiple methods? type: to_system Workflow: Calibration: - method: mse_min # list, split? + method: mse_min ## TODO: list, split? Skill: metric: RPS, RPSS, CRPS, CRPSS, FRPSS, BSS10, BSS90, mean_bias, mean_bias_SS # list, don't split Probabilities: diff --git a/tools/divide_recipe.R b/tools/divide_recipe.R index 8f4ef35a..fd4efc6e 100644 --- a/tools/divide_recipe.R +++ b/tools/divide_recipe.R @@ -1,11 +1,8 @@ -# recipe: the content of the recipe -# verifications: the output from check_recipe -# folder: the name of the output folder for this run -# logger: the log file obtain from prepare_outputs +# recipe: the recipe as returned by prepare_outputs() +divide_recipe <- function(recipe) { -## TODO: Rethink strategy and adapt function accordingly -divide_recipe <- function(recipe, verifications, folder, logger) { - info(logger, "Spliting recipe in single verifications.") + ## TODO: Implement dependent vs independent verifications? + info(recipe$Run$logger, "Spliting recipe in single verifications.") beta_recipe <- list(Description = append(recipe$Description, "split version"), Analysis = list(Horizon = recipe$Analysis$Horizon, @@ -17,37 +14,37 @@ divide_recipe <- function(recipe, verifications, folder, logger) { Workflow = recipe$Analysis$Workflow, Output_format = recipe$Analysis$Output_format), - Run = recipe$Run) - # duplicate recipe by Variables considering dep and indep: - all_recipes <- list(beta_recipe) - i <- 1 # to get track of the recipe number - for (indep in verifications$independent) { - all_recipes[[i]]$Analysis$Variables <- indep - i = i + 1 - all_recipes <- append(all_recipes, list(beta_recipe)) - } - for (dep in verifications$dependent) { - all_recipes[[i]]$Analysis$Variables <- dep - i = i + 1 - all_recipes <- append(all_recipes, list(beta_recipe)) + Run = recipe$Run[c("Loglevel", "output_dir", "Terminal", + "code_dir", "logfile")]) + + # duplicate recipe by independent variables: + all_recipes <- rep(list(beta_recipe), length(recipe$Analysis$Variables)) + for (var in 1:length(recipe$Analysis$Variables)) { + all_recipes[[var]]$Analysis$Variables <- recipe$Analysis$Variables[[var]] } - all_recipes <- all_recipes[-length(all_recipes)] + # for (dep in verifications$dependent) { + # all_recipes[[i]]$Analysis$Variables <- dep + # i = i + 1 + # all_recipes <- append(all_recipes, list(beta_recipe)) + # } + # all_recipes <- all_recipes[-length(all_recipes)] # wth does this do + # duplicate recipe by Datasets: # check Systems if (recipe$Analysis$Datasets$Multimodel) { for (reci in 1:length(all_recipes)) { - all_recipes[[reci]]$Analysis$Datasets <- list( - System = recipe$Analysis$Datasets$System, - Multimodel = recipe$Analysis$Datasets$Multimodel, - Reference = NULL) + all_recipes[[reci]]$Analysis$Datasets <- + list(System = recipe$Analysis$Datasets$System, + Multimodel = recipe$Analysis$Datasets$Multimodel, + Reference = NULL) } } else { for (sys in 1:length(recipe$Analysis$Datasets$System)) { for (reci in 1:length(all_recipes)) { - all_recipes[[reci]]$Analysis$Datasets <- list( - System = recipe$Analysis$Datasets$System[[sys]], - Multimodel = recipe$Analysis$Datasets$Multimodel, - Reference = NULL) + all_recipes[[reci]]$Analysis$Datasets <- + list(System = recipe$Analysis$Datasets$System[[sys]], + Multimodel = recipe$Analysis$Datasets$Multimodel, + Reference = NULL) } if (sys == 1) { recipes <- all_recipes @@ -74,28 +71,28 @@ divide_recipe <- function(recipe, verifications, folder, logger) { # Duplicate recipe by Region recipes <- list() for (reg in 1:length(recipe$Analysis$Region)) { - if (length(recipe$Analysis$Region[[reg]]) == 4) { ##TODO: THIS SHOULD BE ONLY CHECK IN THE RECIPE CHECKER? + # if (length(recipe$Analysis$Region[[reg]]) == 4) { ##TODO: THIS SHOULD BE ONLY CHECK IN THE RECIPE CHECKER? for (reci in 1:length(all_recipes)) { - all_recipes[[reci]]$Analysis$Region <- - recipe$Analysis$Region[[reg]] + all_recipes[[reci]]$Analysis$Region <- recipe$Analysis$Region[[reg]] } recipes <- append(recipes, all_recipes) - } + # } } all_recipes <- recipes rm(list = 'recipes') + # Duplicate recipe by start date if (tolower(recipe$Analysis$Horizon) == 'seasonal') { - for (sday in 1:length(recipe$Analysis$Time$sdate$fcst_sday)) { + for (sdate in 1:length(recipe$Analysis$Time$sdate)) { for (reci in 1:length(all_recipes)) { - all_recipes[[reci]]$Analysis$Time <- list(sdate = list( - fcst_syear = recipe$Analysis$Time$sdate$fcst_syear, - fcst_sday = recipe$Analysis$Time$sdate$fcst_sday[[sday]]), - hcst_start = recipe$Analysis$Time$hcst_start, - hcst_end = recipe$Analysis$Time$hcst_end, - leadtimemin = recipe$Analysis$Time$leadtimemin, - leadtimemax = recipe$Analysis$Time$leadtimemax) + all_recipes[[reci]]$Analysis$Time <- + list(sdate = recipe$Analysis$Time$sdate[[sdate]], + fcst_year = recipe$Analysis$Time$fcst_year, + hcst_start = recipe$Analysis$Time$hcst_start, + hcst_end = recipe$Analysis$Time$hcst_end, + ftime_min = recipe$Analysis$Time$ftime_min, + ftime_max = recipe$Analysis$Time$ftime_max) } - if (sday == 1) { + if (sdate == 1) { recipes <- all_recipes } else { recipes <- append(recipes, all_recipes) @@ -104,12 +101,21 @@ divide_recipe <- function(recipe, verifications, folder, logger) { all_recipes <- recipes rm(list = 'recipes') } # Rest of horizons - # Finally, save all recipes in saparated yaml files + # Save all recipes in separate YAML files + ## TODO: Re-add recipe$Run$logger for (reci in 1:length(all_recipes)) { + if (reci < 10) { + recipe_number <- paste0("0", reci) + } else { + recipe_number <- reci + } write_yaml(all_recipes[[reci]], - paste0(folder, "/logs/recipes/recipe_", reci, ".yml")) + paste0(recipe$Run$output_dir, "/logs/recipes/recipe_", + recipe_number, ".yml")) + all_recipes[[reci]]$Run$logger <- recipe$Run$logger } - text <- paste0("See folder ",folder,"/logs/recipes/ to see the individual recipes.") - info(logger, text) + text <- paste0("See recipe$Run$output_dir ", recipe$Run$output_dir, + "/logs/recipes/ to see the individual recipes.") + info(recipe$Run$logger, text) return(all_recipes) } -- GitLab From 449258ad087a30faa1ae236eb381bd4e711dd481 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Thu, 29 Dec 2022 15:14:25 +0100 Subject: [PATCH 15/21] Move old test recipes to new directory --- recipes/tests/{ => old_tests}/execute_tests.R | 0 recipes/tests/{ => old_tests}/seasonal_testWorkflow1.yml | 0 recipes/tests/{ => old_tests}/seasonal_testWorkflow2.yml | 0 recipes/tests/{ => old_tests}/seasonal_testWorkflow3.yml | 0 recipes/tests/{ => old_tests}/seasonal_testWorkflow4.yml | 0 recipes/tests/{ => old_tests}/seasonal_testWorkflow5.yml | 0 recipes/tests/{ => old_tests}/seasonal_testWorkflow6.yml | 0 recipes/tests/{ => old_tests}/seasonal_testWorkflow7.yml | 0 recipes/tests/{ => old_tests}/seasonal_testWorkflow8.yml | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename recipes/tests/{ => old_tests}/execute_tests.R (100%) rename recipes/tests/{ => old_tests}/seasonal_testWorkflow1.yml (100%) rename recipes/tests/{ => old_tests}/seasonal_testWorkflow2.yml (100%) rename recipes/tests/{ => old_tests}/seasonal_testWorkflow3.yml (100%) rename recipes/tests/{ => old_tests}/seasonal_testWorkflow4.yml (100%) rename recipes/tests/{ => old_tests}/seasonal_testWorkflow5.yml (100%) rename recipes/tests/{ => old_tests}/seasonal_testWorkflow6.yml (100%) rename recipes/tests/{ => old_tests}/seasonal_testWorkflow7.yml (100%) rename recipes/tests/{ => old_tests}/seasonal_testWorkflow8.yml (100%) diff --git a/recipes/tests/execute_tests.R b/recipes/tests/old_tests/execute_tests.R similarity index 100% rename from recipes/tests/execute_tests.R rename to recipes/tests/old_tests/execute_tests.R diff --git a/recipes/tests/seasonal_testWorkflow1.yml b/recipes/tests/old_tests/seasonal_testWorkflow1.yml similarity index 100% rename from recipes/tests/seasonal_testWorkflow1.yml rename to recipes/tests/old_tests/seasonal_testWorkflow1.yml diff --git a/recipes/tests/seasonal_testWorkflow2.yml b/recipes/tests/old_tests/seasonal_testWorkflow2.yml similarity index 100% rename from recipes/tests/seasonal_testWorkflow2.yml rename to recipes/tests/old_tests/seasonal_testWorkflow2.yml diff --git a/recipes/tests/seasonal_testWorkflow3.yml b/recipes/tests/old_tests/seasonal_testWorkflow3.yml similarity index 100% rename from recipes/tests/seasonal_testWorkflow3.yml rename to recipes/tests/old_tests/seasonal_testWorkflow3.yml diff --git a/recipes/tests/seasonal_testWorkflow4.yml b/recipes/tests/old_tests/seasonal_testWorkflow4.yml similarity index 100% rename from recipes/tests/seasonal_testWorkflow4.yml rename to recipes/tests/old_tests/seasonal_testWorkflow4.yml diff --git a/recipes/tests/seasonal_testWorkflow5.yml b/recipes/tests/old_tests/seasonal_testWorkflow5.yml similarity index 100% rename from recipes/tests/seasonal_testWorkflow5.yml rename to recipes/tests/old_tests/seasonal_testWorkflow5.yml diff --git a/recipes/tests/seasonal_testWorkflow6.yml b/recipes/tests/old_tests/seasonal_testWorkflow6.yml similarity index 100% rename from recipes/tests/seasonal_testWorkflow6.yml rename to recipes/tests/old_tests/seasonal_testWorkflow6.yml diff --git a/recipes/tests/seasonal_testWorkflow7.yml b/recipes/tests/old_tests/seasonal_testWorkflow7.yml similarity index 100% rename from recipes/tests/seasonal_testWorkflow7.yml rename to recipes/tests/old_tests/seasonal_testWorkflow7.yml diff --git a/recipes/tests/seasonal_testWorkflow8.yml b/recipes/tests/old_tests/seasonal_testWorkflow8.yml similarity index 100% rename from recipes/tests/seasonal_testWorkflow8.yml rename to recipes/tests/old_tests/seasonal_testWorkflow8.yml -- GitLab From db4d66fb1edb19085f882ed545df2b66740de349 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Thu, 29 Dec 2022 15:18:56 +0100 Subject: [PATCH 16/21] Change enable_checks param to disable_checks, default FALSE --- tools/prepare_outputs.R | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/prepare_outputs.R b/tools/prepare_outputs.R index d6eb970e..56ab56c6 100644 --- a/tools/prepare_outputs.R +++ b/tools/prepare_outputs.R @@ -1,7 +1,7 @@ #'Read recipe YAML file and create and store logfile info #' #'The purpose of this function is to read the recipe configuration for Auto-S2S -#'workflows and create logfiles stores in an the output directory specified in +#'workflows and create logfiles stored in an the output directory specified in #'the recipe. It returns an object of class logger that stores information on #'the recipe configuration and errors. #' @@ -21,10 +21,10 @@ #'@export prepare_outputs <- function(recipe_file, - enable_checks = TRUE) { + disable_checks = FALSE) { # recipe_file: path to recipe YAML file -# enable_checker: Whether or not to run the recipe checker +# disable_checks: If TRUE, does not perform checks on recipe recipe <- read_yaml(recipe_file) recipe$recipe_path <- recipe_file @@ -72,9 +72,8 @@ prepare_outputs <- function(recipe_file, recipe$Run$logger <- logger recipe$Run$logfile <- logfile # Run recipe checker - if (enable_checks) { + if !(disable_checks) { check_recipe(recipe) } - return(recipe) } -- GitLab From 9187ec11aa61e6ad87d0f257ffb3e593328e832f Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Thu, 29 Dec 2022 16:36:28 +0100 Subject: [PATCH 17/21] New recipe; test multiple verifications run serially; fix bug in prepare_outputs() --- modules/test_seasonal.R | 3 +- .../tests/recipe_seasonal_two-variables.yml | 60 +++++++++++++++++++ tools/divide_recipe.R | 7 ++- tools/prepare_outputs.R | 2 +- 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 recipes/tests/recipe_seasonal_two-variables.yml diff --git a/modules/test_seasonal.R b/modules/test_seasonal.R index c74fd1b1..d776609d 100644 --- a/modules/test_seasonal.R +++ b/modules/test_seasonal.R @@ -5,8 +5,9 @@ source("modules/Skill/Skill.R") source("modules/Saving/Saving.R") source("modules/Visualization/Visualization.R") -recipe_file <- "modules/Loading/testing_recipes/recipe_seasonal-tests.yml" +recipe_file <- "recipes/recipe_splitting_example.yml" recipe <- prepare_outputs(recipe_file, enable_checks = F) +atomic_recipes <- divide_recipe(recipe) ## archive <- read_yaml(paste0(recipe$Run$code_dir, "conf/archive.yml"))$archive # Load datasets diff --git a/recipes/tests/recipe_seasonal_two-variables.yml b/recipes/tests/recipe_seasonal_two-variables.yml new file mode 100644 index 00000000..89406ece --- /dev/null +++ b/recipes/tests/recipe_seasonal_two-variables.yml @@ -0,0 +1,60 @@ +################################################################################ +## RECIPE DESCRIPTION +################################################################################ + +Description: + Author: V. Agudetse + Info: Test Independent verification of two variables + +################################################################################ +## ANALYSIS CONFIGURATION +################################################################################ + +Analysis: + Horizon: Seasonal + Variables: # ECVs and Indicators? + - {name: tas, freq: monthly_mean} + - {name: prlr, freq: monthly_mean} + Datasets: + System: # multiple systems for single model, split if Multimodel = F + - {name: system5c3s} + Multimodel: False # single option + Reference: + - {name: era5} # multiple references for single model? + Time: + sdate: # list, split + - '0101' + fcst_year: '2020' # list, don't split, handled internally + hcst_start: '2000' # single option + hcst_end: '2016' # single option + ftime_min: 1 # single option + ftime_max: 3 # single option + Region: # multiple lists, split? Add region name if length(Region) > 1 + - {name: "nino34", latmin: -5, latmax: 5, lonmin: -10, lonmax: 60} + Regrid: + method: bilinear ## TODO: allow multiple methods? + type: to_system + Workflow: + Anomalies: + compute: yes + cross_validation: yes + Calibration: + method: mse_min ## TODO: list, split? + Skill: + metric: RPS, RPSS, CRPS, CRPSS, FRPSS, BSS10, BSS90, mean_bias, mean_bias_SS # list, don't split + Probabilities: + percentiles: [[1/3, 2/3], [1/10, 9/10], [1/4, 2/4, 3/4]] # list, don't split + Indicators: + index: no # ? + ncores: 7 + remove_NAs: yes # bool, don't split + Output_format: S2S4E # string, don't split + +################################################################################ +## Run CONFIGURATION +################################################################################ +Run: + Loglevel: INFO + Terminal: yes + output_dir: /esarchive/scratch/vagudets/repos/auto-s2s/out-logs/ + code_dir: /esarchive/scratch/vagudets/repos/auto-s2s/ diff --git a/tools/divide_recipe.R b/tools/divide_recipe.R index fd4efc6e..6cc8ce4a 100644 --- a/tools/divide_recipe.R +++ b/tools/divide_recipe.R @@ -114,8 +114,11 @@ divide_recipe <- function(recipe) { recipe_number, ".yml")) all_recipes[[reci]]$Run$logger <- recipe$Run$logger } - text <- paste0("See recipe$Run$output_dir ", recipe$Run$output_dir, - "/logs/recipes/ to see the individual recipes.") + info(recipe$Run$logger, + paste("The main recipe has been devided into", length(all_recipes), + "atomic recipes.")) + text <- paste0("See output directory ", recipe$Run$output_dir, + "/logs/recipes/ to see all the individual atomic recipes.") info(recipe$Run$logger, text) return(all_recipes) } diff --git a/tools/prepare_outputs.R b/tools/prepare_outputs.R index 56ab56c6..72fc5433 100644 --- a/tools/prepare_outputs.R +++ b/tools/prepare_outputs.R @@ -72,7 +72,7 @@ prepare_outputs <- function(recipe_file, recipe$Run$logger <- logger recipe$Run$logfile <- logfile # Run recipe checker - if !(disable_checks) { + if (!disable_checks) { check_recipe(recipe) } return(recipe) -- GitLab From 8b30132f06d7cce5b4202d68400ee36b30e382a5 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Thu, 29 Dec 2022 17:01:50 +0100 Subject: [PATCH 18/21] Add info to atomic recipe, fix typo --- tools/divide_recipe.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/divide_recipe.R b/tools/divide_recipe.R index 6cc8ce4a..18962f93 100644 --- a/tools/divide_recipe.R +++ b/tools/divide_recipe.R @@ -4,7 +4,9 @@ divide_recipe <- function(recipe) { ## TODO: Implement dependent vs independent verifications? info(recipe$Run$logger, "Spliting recipe in single verifications.") beta_recipe <- list(Description = append(recipe$Description, - "split version"), + list(Origin = paste("Atomic recipe,", + "split from:", + recipe$name))), Analysis = list(Horizon = recipe$Analysis$Horizon, Variables = NULL, Datasets = NULL, @@ -115,7 +117,7 @@ divide_recipe <- function(recipe) { all_recipes[[reci]]$Run$logger <- recipe$Run$logger } info(recipe$Run$logger, - paste("The main recipe has been devided into", length(all_recipes), + paste("The main recipe has been divided into", length(all_recipes), "atomic recipes.")) text <- paste0("See output directory ", recipe$Run$output_dir, "/logs/recipes/ to see all the individual atomic recipes.") -- GitLab From 8a589bfe32da3bf2e5ddeb3662d96294f4988481 Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Fri, 30 Dec 2022 10:16:56 +0100 Subject: [PATCH 19/21] Simple example using a for loop --- modules/test_seasonal.R | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/modules/test_seasonal.R b/modules/test_seasonal.R index d776609d..921e8f96 100644 --- a/modules/test_seasonal.R +++ b/modules/test_seasonal.R @@ -5,23 +5,25 @@ source("modules/Skill/Skill.R") source("modules/Saving/Saving.R") source("modules/Visualization/Visualization.R") -recipe_file <- "recipes/recipe_splitting_example.yml" -recipe <- prepare_outputs(recipe_file, enable_checks = F) +recipe_file <- "recipes/tests/recipe_seasonal_two-variables.yml" +recipe <- prepare_outputs(recipe_file, disable_checks = T) atomic_recipes <- divide_recipe(recipe) ## archive <- read_yaml(paste0(recipe$Run$code_dir, "conf/archive.yml"))$archive -# Load datasets -data <- load_datasets(recipe) -# Calibrate datasets -calibrated_data <- calibrate_datasets(recipe, data) -# Compute anomalies -calibrated_data <- compute_anomalies(recipe, calibrated_data) -# Compute skill metrics -skill_metrics <- compute_skill_metrics(recipe, calibrated_data) -# Compute percentiles and probability bins -probabilities <- compute_probabilities(recipe, calibrated_data) -# Export all data to netCDF -save_data(recipe, calibrated_data, skill_metrics, probabilities) -# Plot data -plot_data(recipe, calibrated_data, skill_metrics, probabilities, - significance = T) +for (atomic_recipe in atomic_recipes) { + # Load datasets + data <- load_datasets(atomic_recipe) + # Calibrate datasets + calibrated_data <- calibrate_datasets(atomic_recipe, data) + # Compute anomalies + calibrated_data <- compute_anomalies(atomic_recipe, calibrated_data) + # Compute skill metrics + skill_metrics <- compute_skill_metrics(atomic_recipe, calibrated_data) + # Compute percentiles and probability bins + probabilities <- compute_probabilities(atomic_recipe, calibrated_data) + # Export all data to netCDF + save_data(atomic_recipe, calibrated_data, skill_metrics, probabilities) + # Plot data + plot_data(atomic_recipe, calibrated_data, skill_metrics, probabilities, + significance = T) +} -- GitLab From 200d3eefd7b197e11701da2cc92e83da726a198d Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Fri, 30 Dec 2022 10:18:34 +0100 Subject: [PATCH 20/21] Add a TODO --- modules/Saving/paths2save.R | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/Saving/paths2save.R b/modules/Saving/paths2save.R index 2d6353fe..2d5a0a4e 100644 --- a/modules/Saving/paths2save.R +++ b/modules/Saving/paths2save.R @@ -1,4 +1,6 @@ ## TODO: Separate by time aggregation +## TODO: Build a default path that accounts for: +## variable, system, reference, start date and region name get_filename <- function(dir, recipe, var, date, agg, file.type) { # This function builds the path of the output file based on directory, -- GitLab From a65b43cdcae8dfd49c1cf6b86873b9086fbf425d Mon Sep 17 00:00:00 2001 From: Victoria Agudetse Roures Date: Tue, 31 Jan 2023 12:01:58 +0100 Subject: [PATCH 21/21] Comment lines --- tools/check_recipe.R | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tools/check_recipe.R b/tools/check_recipe.R index 307bc3eb..541319f5 100644 --- a/tools/check_recipe.R +++ b/tools/check_recipe.R @@ -124,15 +124,16 @@ check_recipe <- function(recipe) { "No forecast year will be used.")) } ## TODO: Adapt and move this inside 'if'? - fcst.sdate <- NULL - for (syear in recipe$Analysis$Time$fcst_year) { - for (sday in recipe$Analysis$Time$sdate) { - fcst.sdate <- c(fcst.sdate, - paste0(syear, - sprintf("%04d", as.numeric(sday)))) - } - } - fcst.sdate <- list(stream = stream, fcst.sdate = fcst.sdate) + # fcst.sdate <- NULL + # for (syear in recipe$Analysis$Time$fcst_year) { + # for (sday in recipe$Analysis$Time$sdate) { + # fcst.sdate <- c(fcst.sdate, + # paste0(syear, + # sprintf("%04d", as.numeric(sday)))) + # } + # } + # fcst.sdate <- list(stream = stream, fcst.sdate = fcst.sdate) + # Regrid checks: if (length(recipe$Analysis$Regrid) != 2) { error(recipe$Run$logger, -- GitLab