diff --git a/modules/Visualization/Visualization.R b/modules/Visualization/Visualization.R index e5550344ce19b3e540e0abf69904497b0416b95c..11629578e10a3f6ac5ee5427718e599db9e13bba 100644 --- a/modules/Visualization/Visualization.R +++ b/modules/Visualization/Visualization.R @@ -23,7 +23,7 @@ Visualization <- function(recipe, skill_metrics = NULL, statistics = NULL, probabilities = NULL, - significance = F, + significance = FALSE, output_conf = NULL, logo = NULL) { # Try to produce and save several basic plots. diff --git a/tests/recipes/recipe-seasonal_visualization.yml b/tests/recipes/recipe-seasonal_visualization.yml new file mode 100644 index 0000000000000000000000000000000000000000..1223c30d244453833e5252ff62d1d3d493615401 --- /dev/null +++ b/tests/recipes/recipe-seasonal_visualization.yml @@ -0,0 +1,65 @@ +Description: + Author: nperez + Info: ECVs Oper ESS ECMWF SEAS5 Seasonal Forecast recipe (monthly mean, tas) + +Analysis: + Horizon: seasonal # Mandatory, str: either subseasonal, seasonal, or decadal + Variables: + name: tas + freq: monthly_mean + units: K + Datasets: + System: + name: ECMWF-SEAS5.1 # Mandatory, str: system5c3s system21_m1 system35c3s + Multimodel: no # Mandatory, bool: Either yes/true or no/false + Reference: + name: ERA5 # Mandatory, str: Reference codename. See docu. + Time: + sdate: '0601' ## MMDD + fcst_year: '2023' # Optional, int: Forecast year 'YYYY' + hcst_start: '2013' # Mandatory, int: Hindcast start year 'YYYY' + hcst_end: '2016' # Mandatory, int: Hindcast end year 'YYYY' + ftime_min: 1 # Mandatory, int: First leadtime time step in months + ftime_max: 1 # Mandatory, int: Last leadtime time step in months + Region: + latmin: 34 + latmax: 46 + lonmin: -10 + lonmax: 5 + Regrid: + method: bilinear # Mandatory, str: Interpolation method. See docu. + type: "to_system" + #type: /esarchive/scratch/nmilders/gitlab/git_clones/auto-s2s/conf/grid_description.txt #'r360x180' # Mandatory, str: to_system, to_reference, or CDO-accepted grid. + Workflow: + Anomalies: + compute: no + cross_validation: no + save: none + Calibration: + method: evmos # Mandatory, str: Calibration method. See docu. + cross_validation: yes + save: none + Skill: + metric: rpss + save: 'all' + cross_validation: yes + Probabilities: + percentiles: [[1/3, 2/3], [1/10, 9/10]] # frac: Quantile thresholds. + save: 'all' + Indicators: + index: no + Visualization: + plots: forecast_ensemble_mean skill_metrics + multi_panel: no + projection: 'robinson' + mask_terciles: both + file_format: 'PNG' + ncores: 4 # Optional, int: number of cores, defaults to 1 + remove_NAs: # Optional, bool: Whether NAs are removed, defaults to FALSE + Output_format: scorecards + logo: yes +Run: + Loglevel: INFO + Terminal: TRUE + output_dir: ./tests/out-logs/ ## dev: ../out-logs/ + code_dir: /esarchive/scratch/nperez/git6/sunset/ diff --git a/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_enscordots_ft01.png b/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_enscordots_ft01.png new file mode 100644 index 0000000000000000000000000000000000000000..0f2d9c7b407565646577ab9297b484babc8aaba5 Binary files /dev/null and b/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_enscordots_ft01.png differ diff --git a/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_enscormask_ft01.png b/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_enscormask_ft01.png new file mode 100644 index 0000000000000000000000000000000000000000..a1595c2d7db9e10012dd6b602f00c4bbf6592e61 Binary files /dev/null and b/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_enscormask_ft01.png differ diff --git a/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_ft01.png b/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_ft01.png new file mode 100644 index 0000000000000000000000000000000000000000..c78c88eaee5b5987863dcf22a78c82a0ce9f5c68 Binary files /dev/null and b/tests/testthat/_snaps/seasonal_visualization/forecast_ensemble_mean-20230601_ft01.png differ diff --git a/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_ft01.png b/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_ft01.png new file mode 100644 index 0000000000000000000000000000000000000000..02e1ced6c04bcd84014e9a8b7251f0c456e3edfb Binary files /dev/null and b/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_ft01.png differ diff --git a/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_rpssdots_ft01.png b/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_rpssdots_ft01.png new file mode 100644 index 0000000000000000000000000000000000000000..765e9237fa0b4d27aa14530c81f272849d3fa841 Binary files /dev/null and b/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_rpssdots_ft01.png differ diff --git a/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_rpssmask_ft01.png b/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_rpssmask_ft01.png new file mode 100644 index 0000000000000000000000000000000000000000..23621f69ae5f94feddece95d0b039ee501ce1826 Binary files /dev/null and b/tests/testthat/_snaps/seasonal_visualization/forecast_most_likely_tercile-20230601_rpssmask_ft01.png differ diff --git a/tests/testthat/_snaps/seasonal_visualization/rpss-june_ft01.png b/tests/testthat/_snaps/seasonal_visualization/rpss-june_ft01.png new file mode 100644 index 0000000000000000000000000000000000000000..237e457205a4ce168e3526ccf9622fb29f93d371 Binary files /dev/null and b/tests/testthat/_snaps/seasonal_visualization/rpss-june_ft01.png differ diff --git a/tests/testthat/_snaps/seasonal_visualization/rpss-june_ft01_mask.png b/tests/testthat/_snaps/seasonal_visualization/rpss-june_ft01_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..61c63f68c8b756bb9f4238a79a3153e1a63b3340 Binary files /dev/null and b/tests/testthat/_snaps/seasonal_visualization/rpss-june_ft01_mask.png differ diff --git a/tests/testthat/test-seasonal_visualization.R b/tests/testthat/test-seasonal_visualization.R new file mode 100644 index 0000000000000000000000000000000000000000..1aa973d30296869d5ca3ec77cabc27d46484a86b --- /dev/null +++ b/tests/testthat/test-seasonal_visualization.R @@ -0,0 +1,236 @@ +# Seasonal monthly data + +## TO REMOVE ## **************************************************************** +setwd("/home/Earth/abatalla/SUNSET_RS_GIT/sunset") ## dev +## library(testthat) +# ****************************************************************************** + +source("modules/Loading/Loading.R") +source("modules/Units/Units.R") +source("modules/Calibration/Calibration.R") +source("modules/Skill/Skill.R") +source("modules/Anomalies/Anomalies.R") +source("modules/Saving/Saving.R") +source("modules/Visualization/Visualization.R") + + +recipe_file <- "./tests/recipes/recipe-seasonal_visualization.yml" +recipe <- prepare_outputs(recipe_file, disable_checks = FALSE) + +# Load datasets +suppressWarnings({invisible(capture.output( + data <- Loading(recipe) +))}) + +# Units transformation +suppressWarnings({invisible(capture.output( + data <- Units(recipe, data) +))}) + +# Calibrate data +suppressWarnings({invisible(capture.output( + data <- Calibration(recipe, data) +))}) + +# Compute skill metrics +suppressWarnings({invisible(capture.output( + skill_metrics <- Skill(recipe, data) +))}) + +# Compute percentiles and probability bins +suppressWarnings({invisible(capture.output( + probabilities <- Probabilities(recipe, data) +))}) + + + +# ------- TESTS -------- + + +test_that("1. Loading", { + + expect_equal( + is.list(data), + TRUE + ) + + expect_true( + all(c("hcst", "fcst", "obs") %in% names(data)) + ) + + expect_equal( + class(data$hcst), + "s2dv_cube" + ) + + expect_equal( + class(data$fcst), + "s2dv_cube" + ) + + expect_equal( + class(data$obs), + "s2dv_cube" + ) + + expect_equal( + names(data$hcst), + c("data", "dims", "coords", "attrs") + ) + + expect_equal( + names(data$hcst), + names(data$fcst) + ) + + expect_equal( + names(data$hcst), + names(data$obs) + ) + + expect_equal( + dim(data$hcst$data), + c(dat = 1, var = 1, sday = 1, sweek = 1, syear = 4, time = 1, latitude = 12, longitude = 15, ensemble = 25) + ) + + expect_equal( + dim(data$fcst$data), + c(dat = 1, var = 1, sday = 1, sweek = 1, syear = 1, time = 1, latitude = 12, longitude = 15, ensemble = 51) + ) + + expect_equal( + dim(data$hcst$attrs$Dates), + c(sday = 1, sweek = 1, syear = 4, time = 1) + ) + +}) + + + +# --- SNAPSHOT TESTS --- + +Sys.setenv(TESTTHAT_EDITION = "3") + +outdir <- "./tests/testthat/_snaps/seasonal_visualization/" + +output_conf <- read_yaml("modules/Visualization/output_size.yml", + eval.exp = TRUE)$region + + +# plot_forecast_map + +save_fun_1 <- function(...) { + path <- "./tests/testthat/_snaps/seasonal_visualization/" ## dev: "./_snaps/" + do.call(plot_forecast_map(), list(...)) + return(path) +} + +rpss_sig <- skill_metrics$rpss_significance +rpss_sig[1, 1, 1:6, ] <- TRUE + +test_that("2. Snapshot checks: plot_forecast_map", { + + expect_snapshot_file( + paste0(save_fun_1(recipe = recipe, fcst = data$fcst, + outdir = outdir, output_conf = output_conf), + "forecast_ensemble_mean-20230601_ft01.png"), + name = "forecast_ensemble_mean-20230601_ft01.png") + + expect_snapshot_file( + paste0(save_fun_1(recipe = recipe, fcst = data$fcst, dots = rpss_sig, + outdir = outdir, output_conf = output_conf), + "forecast_ensemble_mean-20230601_enscordots_ft01.png"), + name = "forecast_ensemble_mean-20230601_enscordots_ft01.png") + + expect_snapshot_file( + paste0(save_fun_1(recipe = recipe, fcst = data$fcst, mask = rpss_sig, + outdir = outdir, output_conf = output_conf), + "forecast_ensemble_mean-20230601_enscormask_ft01.png"), + name = "forecast_ensemble_mean-20230601_enscormask_ft01.png") +}) + + +# ---------------------- + +# plot_metrics + +save_fun_2 <- function(...) { + path <- "./tests/testthat/_snaps/seasonal_visualization/" + do.call(plot_metrics, list(...)) + return(path) +} + +skill_metrics$rpss_significance[1, 1, 1:6, ] <- TRUE + +test_that("3. Snapshot checks: plot_metrics", { + + expect_snapshot_file( + paste0(save_fun_2(recipe = recipe, data_cube = data$hcst, + metrics = skill_metrics, outdir = outdir, + significance = FALSE, output_conf = output_conf), + "rpss-june_ft01.png"), + name = "rpss-june_ft01.png") + + expect_snapshot_file( + paste0(save_fun_2(recipe = recipe, data_cube = data$hcst, + metrics = skill_metrics, outdir = outdir, + significance = TRUE, output_conf = output_conf), + "rpss-june_ft01_mask.png"), + name = "rpss-june_ft01_mask.png") + +}) + + +# ---------------------- + +# plot_most_likely_terciles + +save_fun_3 <- function(...) { + path <- "./tests/testthat/_snaps/seasonal_visualization/" + do.call(plot_most_likely_terciles, list(...)) + return(path) +} + +test_that("4. Snapshot checks: plot_most_likely_terciles", { + + expect_snapshot_file( + paste0(save_fun_3(recipe = recipe, fcst = data$fcst, + probabilities = probabilities, + mask = NULL, dots = NULL, + outdir = outdir, output_conf = output_conf), + "forecast_most_likely_tercile-20230601_ft01.png"), + name = "forecast_most_likely_tercile-20230601_ft01.png") + + expect_snapshot_file( + paste0(save_fun_3(recipe = recipe, fcst = data$fcst, + probabilities = probabilities, + mask = rpss_sig, dots = NULL, + outdir = outdir, output_conf = output_conf), + "forecast_most_likely_tercile-20230601_rpssmask_ft01.png"), + name = "forecast_most_likely_tercile-20230601_rpssmask_ft01.png") + + expect_snapshot_file( + paste0(save_fun_3(recipe = recipe, fcst = data$fcst, + probabilities = probabilities, + mask = NULL, dots = rpss_sig, + outdir = outdir, output_conf = output_conf), + "forecast_most_likely_tercile-20230601_rpssdots_ft01.png"), + name = "forecast_most_likely_tercile-20230601_rpssdots_ft01.png") + +}) + +# Remove the pdf files +unlink(list.files(outdir, pattern = "\\.pdf$", full.names = TRUE)) + +# ---------------------- + +# To test locally: +# testthat::test_file("./tests/testthat/test-seasonal_visualization.R") + + +# How the snapshot tests work: +# the "save_fun" part creates the new plot, by default a pdf. It needs a name +# It is then compared to the png in the "name" part. +# If they are different, a png with the plot created in "save_fun" is created with the name from the "name" part + ".new". +# The first part needs a name so that it can be compared. Otherwise an empty ".new" png is created. +# Summarizing: now a pdf and a png (from the png) are created, If the png is the same as the one that exists, the test passes. Then the pdf is removed.