Skip to content

Commit f6d6892

Browse files
committed
Add check for RMSEs
1 parent e0837a8 commit f6d6892

File tree

2 files changed

+88
-9
lines changed

2 files changed

+88
-9
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import ClimaAnalysis
2+
import Dates
3+
import Test: @test, @testset
4+
5+
include("data_sources.jl")
6+
7+
"""
8+
test_rmse_thresholds(diagnostics_folder_path, spinup)
9+
10+
Test that the annual RMSE values for specific variables have not increased
11+
beyond acceptable thresholds. The variables tested are:
12+
- pr (precipitation)
13+
- rsut (top of atmosphere outgoing shortwave radiation)
14+
- rsutcs (clear-sky top of atmosphere outgoing shortwave radiation)
15+
16+
The spinup is the number of months to remove from the beginning of the
17+
simulation.
18+
19+
More variables can be added by adding the short name and RMSE pair to the
20+
dictionary returned by `get_rmse_thresholds`.
21+
22+
If this test fails, it indicates a regression in the model's physics, resulting
23+
in a higher RMSE. If this increased RMSE is considered acceptable, then the
24+
thresholds should be updated accordingly.
25+
"""
26+
function test_rmse_thresholds(diagnostics_folder_path, spinup)
27+
sim_var_dict = get_sim_var_dict(diagnostics_folder_path)
28+
obs_var_dict = get_obs_var_dict()
29+
rmse_thresholds = get_rmse_thresholds()
30+
31+
sim_vars = (sim_var_dict[short_name]() for short_name in keys(rmse_thresholds))
32+
obs_vars =
33+
(obs_var_dict[ClimaAnalysis.short_name(sim_var)](sim_var.attributes["start_date"]) for sim_var in sim_vars)
34+
short_names = (ClimaAnalysis.short_name(var) for var in sim_vars)
35+
36+
rmses = map(sim_vars, obs_vars) do sim_var, obs_var
37+
# Remove first spin_up_months from simulation
38+
spinup_cutoff = spinup * 31 * 86400.0
39+
ClimaAnalysis.times(sim_var)[end] >= spinup_cutoff &&
40+
(sim_var = ClimaAnalysis.window(sim_var, "time", left = spinup_cutoff))
41+
42+
obs_var = ClimaAnalysis.resampled_as(obs_var, sim_var)
43+
obs_var = ClimaAnalysis.average_time(obs_var)
44+
sim_var = ClimaAnalysis.average_time(sim_var)
45+
46+
ClimaAnalysis.global_rmse(sim_var, obs_var)
47+
end
48+
49+
@testset "RMSE thresholds" begin
50+
for (short_name, rmse) in zip(short_names, rmses)
51+
@info "RMSE for $short_name: $rmse"
52+
@test rmse < rmse_thresholds[short_name]
53+
end
54+
end
55+
end
56+
57+
"""
58+
get_rmse_thresholds()
59+
60+
Return a dictionary mapping short names to maximum acceptable RMSE values.
61+
"""
62+
function get_rmse_thresholds()
63+
rmse_thresholds = Dict(
64+
"pr" => 3.0, # mm/day
65+
"rsut" => 20.0, # W/m²
66+
"rsutcs" => 7.0, # W/m²
67+
)
68+
return rmse_thresholds
69+
end
70+
71+
if abspath(PROGRAM_FILE) == @__FILE__
72+
if length(ARGS) != 1
73+
error("Usage: julia test_rmses.jl <Filepath to simulation data>")
74+
end
75+
leaderboard_base_path = ARGS[begin]
76+
spinup = 3
77+
test_rmse_thresholds(leaderboard_base_path, spinup)
78+
end

experiments/ClimaEarth/user_io/postprocessing.jl

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
include("debug_plots.jl")
33
include("diagnostics_plots.jl")
44
include("../leaderboard/leaderboard.jl")
5+
include("../leaderboard/test_rmses.jl")
56

67
"""
78
postprocess_sim(cs, postprocessing_vars)
@@ -32,15 +33,15 @@ function postprocess_sim(cs, postprocessing_vars)
3233

3334
# If we have enough data (in time, but also enough variables), plot the leaderboard.
3435
# We need pressure to compute the leaderboard.
35-
pressure_in_output = "pfull" in CAN.available_vars(CAN.SimDir(atmos_output_dir))
36-
if pressure_in_output
37-
times = CAN.times(get(CAN.SimDir(atmos_output_dir), "pfull"))
38-
t_end = times[end]
39-
if t_end > 84600 * 31 * 3 # 3 months for spin up
40-
leaderboard_base_path = artifact_dir
41-
compute_leaderboard(leaderboard_base_path, atmos_output_dir, 3)
42-
compute_pfull_leaderboard(leaderboard_base_path, atmos_output_dir, 6)
43-
end
36+
simdir = CAN.SimDir(atmos_output_dir)
37+
pressure_in_output = "pfull" in CAN.available_vars(simdir)
38+
times = CAN.times(get(CAN.SimDir(atmos_output_dir), first(CAN.available_vars(simdir))))
39+
t_end = times[end]
40+
if t_end > 84600 * 31 * 3 # 3 months for spin up
41+
leaderboard_base_path = artifact_dir
42+
compute_leaderboard(leaderboard_base_path, atmos_output_dir, 3)
43+
test_rmse_thresholds(atmos_output_dir, 3)
44+
pressure_in_output && compute_pfull_leaderboard(leaderboard_base_path, atmos_output_dir, 6)
4445
end
4546

4647
# Perform conservation checks if they exist

0 commit comments

Comments
 (0)