climate-ref-esmvaltool 0.7.0__tar.gz → 0.8.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/PKG-INFO +2 -1
  2. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/pyproject.toml +2 -1
  3. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/base.py +19 -2
  4. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/cloud_radiative_effects.py +15 -2
  5. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/ecs.py +3 -3
  6. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/enso.py +46 -26
  7. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/regional_historical_changes.py +32 -12
  8. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/sea_ice_area_basic.py +36 -37
  9. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/tcr.py +2 -2
  10. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/tcre.py +2 -2
  11. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/zec.py +2 -2
  12. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/.gitignore +0 -0
  13. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/LICENCE +0 -0
  14. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/NOTICE +0 -0
  15. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/README.md +0 -0
  16. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/__init__.py +0 -0
  17. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/_version.py +0 -0
  18. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/dataset_registry/data.txt +0 -0
  19. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/__init__.py +0 -0
  20. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/climate_at_global_warming_levels.py +0 -0
  21. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/climate_drivers_for_fire.py +0 -0
  22. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/cloud_scatterplots.py +0 -0
  23. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/example.py +0 -0
  24. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/diagnostics/sea_ice_sensitivity.py +0 -0
  25. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/py.typed +0 -0
  26. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/recipe.py +0 -0
  27. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/recipes.txt +0 -0
  28. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/requirements/conda-lock.yml +0 -0
  29. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/requirements/environment.yml +0 -0
  30. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/src/climate_ref_esmvaltool/types.py +0 -0
  31. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/integration/test_diagnostics.py +0 -0
  32. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_climate_at_global_warming_levels.yml +0 -0
  33. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_climate_drivers_for_fire.yml +0 -0
  34. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_cloud_radiative_effects.yml +0 -0
  35. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_cloud_scatterplots_cli_ta.yml +0 -0
  36. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_cloud_scatterplots_clivi_lwcre.yml +0 -0
  37. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_cloud_scatterplots_clt_swcre.yml +0 -0
  38. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_cloud_scatterplots_clwvi_pr.yml +0 -0
  39. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_cloud_scatterplots_reference.yml +0 -0
  40. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_enso_basic_climatology.yml +0 -0
  41. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_enso_characteristics.yml +0 -0
  42. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_equilibrium_climate_sensitivity.yml +0 -0
  43. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_global_mean_timeseries.yml +0 -0
  44. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_regional_historical_annual_cycle.yml +0 -0
  45. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_regional_historical_timeseries.yml +0 -0
  46. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_regional_historical_trend.yml +0 -0
  47. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_sea_ice_area_basic.yml +0 -0
  48. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_sea_ice_sensitivity.yml +0 -0
  49. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_transient_climate_response.yml +0 -0
  50. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_transient_climate_response_emissions.yml +0 -0
  51. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/recipes/recipe_zero_emission_commitment.yml +0 -0
  52. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/test_base.py +0 -0
  53. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/diagnostics/test_recipes.py +0 -0
  54. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/test_diagnostic.py +0 -0
  55. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/test_provider.py +0 -0
  56. {climate_ref_esmvaltool-0.7.0 → climate_ref_esmvaltool-0.8.1}/tests/unit/test_recipe.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: climate-ref-esmvaltool
3
- Version: 0.7.0
3
+ Version: 0.8.1
4
4
  Summary: ESMValTool diagnostic provider for the Rapid Evaluation Framework
5
5
  Author-email: ESMValTool development team <esmvaltool-dev@listserv.dfn.de>, Jared Lewis <jared.lewis@climate-resource.com>
6
6
  License-Expression: Apache-2.0
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Topic :: Scientific/Engineering
20
20
  Requires-Python: >=3.11
21
21
  Requires-Dist: climate-ref-core
22
+ Requires-Dist: netcdf4>=1.7.2
22
23
  Requires-Dist: pooch>=1.8
23
24
  Requires-Dist: pyyaml
24
25
  Requires-Dist: xarray>=2023.3.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "climate-ref-esmvaltool"
3
- version = "0.7.0"
3
+ version = "0.8.1"
4
4
  description = "ESMValTool diagnostic provider for the Rapid Evaluation Framework"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -24,6 +24,7 @@ classifiers = [
24
24
  ]
25
25
  dependencies = [
26
26
  "climate-ref-core",
27
+ "netcdf4>=1.7.2",
27
28
  "pooch >= 1.8",
28
29
  "pyyaml",
29
30
  "xarray >= 2023.3.0",
@@ -4,6 +4,8 @@ from collections.abc import Iterable
4
4
  from pathlib import Path
5
5
  from typing import ClassVar
6
6
 
7
+ import netCDF4
8
+ import numpy as np
7
9
  import pandas
8
10
  import xarray as xr
9
11
  import yaml
@@ -23,6 +25,18 @@ from climate_ref_esmvaltool.recipe import load_recipe, prepare_climate_data
23
25
  from climate_ref_esmvaltool.types import MetricBundleArgs, OutputBundleArgs, Recipe
24
26
 
25
27
 
28
+ def mask_fillvalues(array: np.ndarray) -> np.ma.MaskedArray: # type: ignore[type-arg]
29
+ """Convert netCDF4 fill values in an array to a mask."""
30
+ # Workaround for https://github.com/pydata/xarray/issues/2742
31
+ defaults = {np.dtype(k): v for k, v in netCDF4.default_fillvals.items()}
32
+ return np.ma.masked_equal(array, defaults[array.dtype]) # type: ignore[no-untyped-call,no-any-return]
33
+
34
+
35
+ def fillvalues_to_nan(array: np.ndarray) -> np.ndarray: # type: ignore[type-arg]
36
+ """Convert netCDF4 fill values in an array to NaN."""
37
+ return mask_fillvalues(array).filled(np.nan) # type: ignore[no-untyped-call,no-any-return]
38
+
39
+
26
40
  class ESMValToolDiagnostic(CommandLineDiagnostic):
27
41
  """ESMValTool Diagnostic base class."""
28
42
 
@@ -213,7 +227,9 @@ class ESMValToolDiagnostic(CommandLineDiagnostic):
213
227
  # Add the plots and data files
214
228
  series = []
215
229
  plot_suffixes = {".png", ".jpg", ".pdf", ".ps"}
216
- for metadata_file in result_dir.glob("run/*/*/diagnostic_provenance.yml"):
230
+ # Sort metadata files for stable processing
231
+ metadata_files = sorted(result_dir.glob("run/*/*/diagnostic_provenance.yml"))
232
+ for metadata_file in metadata_files:
217
233
  metadata = yaml.safe_load(metadata_file.read_text(encoding="utf-8"))
218
234
  for filename in metadata:
219
235
  caption = metadata[filename].get("caption", "")
@@ -300,6 +316,7 @@ class ESMValToolDiagnostic(CommandLineDiagnostic):
300
316
  attributes[f"value_{attr}"] = dataset[series_def.values_name].attrs[attr]
301
317
  if attr in dataset[series_def.index_name].attrs:
302
318
  attributes[f"index_{attr}"] = dataset[series_def.index_name].attrs[attr]
319
+ # TODO: Handle masked values in the index
303
320
  index = dataset[series_def.index_name].values.tolist()
304
321
  if hasattr(index[0], "calendar"):
305
322
  attributes["calendar"] = index[0].calendar
@@ -310,7 +327,7 @@ class ESMValToolDiagnostic(CommandLineDiagnostic):
310
327
  series.append(
311
328
  SeriesMetricValue(
312
329
  dimensions={**input_selectors, **series_def.dimensions},
313
- values=dataset[series_def.values_name].values.tolist(),
330
+ values=fillvalues_to_nan(dataset[series_def.values_name].values).tolist(),
314
331
  index=index,
315
332
  index_name=series_def.index_name,
316
333
  attributes=attributes,
@@ -61,13 +61,26 @@ class CloudRadiativeEffects(ESMValToolDiagnostic):
61
61
  series = tuple(
62
62
  SeriesDefinition(
63
63
  file_pattern=f"plot_profiles/plot/variable_vs_lat_{var_name}_*.nc",
64
- sel={"dim0": 0}, # Select the model and not the observations.
65
- dimensions={"statistic": f"{var_name} zonal mean"},
64
+ sel={"dim0": 0}, # Select the model.
65
+ dimensions={"variable_id": var_name, "statistic": "zonal mean"},
66
66
  values_name=var_name,
67
67
  index_name="lat",
68
68
  attributes=[],
69
69
  )
70
70
  for var_name in ["lwcre", "swcre"]
71
+ ) + tuple(
72
+ SeriesDefinition(
73
+ file_pattern=f"plot_profiles/plot/variable_vs_lat_{var_name}_*.nc",
74
+ sel={"dim0": i}, # Select the observation.
75
+ dimensions={"variable_id": var_name, "statistic": "zonal mean", "reference_source_id": source_id},
76
+ values_name=var_name,
77
+ index_name="lat",
78
+ attributes=[],
79
+ )
80
+ for var_name in ["lwcre", "swcre"]
81
+ for i, source_id in enumerate(
82
+ ["CERES-EBAF-Ed4.2", "ESACCI-CLOUD-AVHRR-AMPM-fv3.0", "ISCCP-FH"], start=1
83
+ )
71
84
  )
72
85
 
73
86
  @staticmethod
@@ -14,7 +14,7 @@ from climate_ref_core.diagnostics import DataRequirement
14
14
  from climate_ref_core.metric_values.typing import SeriesDefinition
15
15
  from climate_ref_core.pycmec.metric import CMECMetric, MetricCV
16
16
  from climate_ref_core.pycmec.output import CMECOutput
17
- from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
17
+ from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic, fillvalues_to_nan
18
18
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
19
19
  from climate_ref_esmvaltool.types import MetricBundleArgs, OutputBundleArgs, Recipe
20
20
 
@@ -139,9 +139,9 @@ class EquilibriumClimateSensitivity(ESMValToolDiagnostic):
139
139
  ) -> tuple[CMECMetric, CMECOutput]:
140
140
  """Format the result."""
141
141
  ecs_ds = xarray.open_dataset(result_dir / "work" / "ecs" / "calculate" / "ecs.nc")
142
- ecs = float(ecs_ds["ecs"].values[0])
142
+ ecs = float(fillvalues_to_nan(ecs_ds["ecs"].values)[0])
143
143
  lambda_ds = xarray.open_dataset(result_dir / "work" / "ecs" / "calculate" / "lambda.nc")
144
- lambda_ = float(lambda_ds["lambda"].values[0])
144
+ lambda_ = float(fillvalues_to_nan(lambda_ds["lambda"].values)[0])
145
145
 
146
146
  # Update the diagnostic bundle arguments with the computed diagnostics.
147
147
  metric_args[MetricCV.DIMENSIONS.value] = {
@@ -67,55 +67,75 @@ class ENSOBasicClimatology(ESMValToolDiagnostic):
67
67
  series = (
68
68
  tuple(
69
69
  SeriesDefinition(
70
- file_pattern=f"diagnostic_metrics/plot_script/{{source_id}}_eq_{var_name}_bias.nc",
71
- dimensions={
72
- "statistic": (
73
- f"zonal bias in the time-mean {var_name} structure across the equatorial Pacific"
74
- ),
75
- },
70
+ file_pattern=f"diagnostic_metrics/plot_script/{source_id}_eq_{var_name}_bias.nc",
71
+ dimensions=(
72
+ {
73
+ "statistic": (
74
+ f"zonal bias in the time-mean {var_name} structure across the equatorial Pacific"
75
+ ),
76
+ }
77
+ | ({} if source_id == "{source_id}" else {"reference_source_id": source_id})
78
+ ),
76
79
  values_name="tos" if var_name == "sst" else var_name,
77
80
  index_name="lon",
78
81
  attributes=[],
79
82
  )
80
- for var_name in ["pr", "sst", "tauu"]
83
+ for var_name in ("pr", "sst", "tauu")
84
+ for source_id in ("{source_id}", "GPCP-V2.3", "TROPFLUX")
81
85
  )
82
86
  + tuple(
83
87
  SeriesDefinition(
84
88
  file_pattern=f"diagnostic_metrics/plot_script/{{source_id}}_eq_{var_name}_seacycle.nc",
85
- dimensions={
86
- "statistic": (
87
- "zonal bias in the amplitude of the mean seasonal cycle of "
88
- f"{var_name} in the equatorial Pacific"
89
- ),
90
- },
89
+ dimensions=(
90
+ {
91
+ "statistic": (
92
+ "zonal bias in the amplitude of the mean seasonal cycle of "
93
+ f"{var_name} in the equatorial Pacific"
94
+ ),
95
+ }
96
+ | ({} if source_id == "{source_id}" else {"reference_source_id": source_id})
97
+ ),
91
98
  values_name="tos" if var_name == "sst" else var_name,
92
99
  index_name="lon",
93
100
  attributes=[],
94
101
  )
95
- for var_name in ["pr", "sst", "tauu"]
102
+ for var_name in ("pr", "sst", "tauu")
103
+ for source_id in ("{source_id}", "GPCP-V2.3", "TROPFLUX")
96
104
  )
97
- + (
105
+ + tuple(
98
106
  SeriesDefinition(
99
107
  file_pattern="diagnostic_metrics/plot_script/{source_id}_pr_double.nc",
100
- dimensions={
101
- "statistic": ("meridional bias in the time-mean pr structure across the eastern Pacific"),
102
- },
108
+ dimensions=(
109
+ {
110
+ "statistic": (
111
+ "meridional bias in the time-mean pr structure across the eastern Pacific"
112
+ ),
113
+ }
114
+ | ({} if source_id == "{source_id}" else {"reference_source_id": source_id})
115
+ ),
103
116
  values_name="pr",
104
117
  index_name="lat",
105
118
  attributes=[],
106
- ),
119
+ )
120
+ for source_id in ("{source_id}", "GPCP-V2.3")
121
+ )
122
+ + tuple(
107
123
  SeriesDefinition(
108
124
  file_pattern="diagnostic_metrics/plot_script/*_pr_double_seacycle.nc",
109
- dimensions={
110
- "statistic": (
111
- "meridional bias in the amplitude of the mean seasonal "
112
- "pr cycle in the eastern Pacific"
113
- ),
114
- },
125
+ dimensions=(
126
+ {
127
+ "statistic": (
128
+ "meridional bias in the amplitude of the mean seasonal "
129
+ "pr cycle in the eastern Pacific"
130
+ ),
131
+ }
132
+ | ({} if source_id == "{source_id}" else {"reference_source_id": source_id})
133
+ ),
115
134
  values_name="pr",
116
135
  index_name="lat",
117
136
  attributes=[],
118
- ),
137
+ )
138
+ for source_id in ("{source_id}", "GPCP-V2.3")
119
139
  )
120
140
  )
121
141
 
@@ -16,7 +16,7 @@ from climate_ref_core.diagnostics import DataRequirement
16
16
  from climate_ref_core.metric_values.typing import SeriesDefinition
17
17
  from climate_ref_core.pycmec.metric import CMECMetric, MetricCV
18
18
  from climate_ref_core.pycmec.output import CMECOutput
19
- from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
19
+ from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic, fillvalues_to_nan
20
20
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
21
21
  from climate_ref_esmvaltool.types import MetricBundleArgs, OutputBundleArgs, Recipe
22
22
 
@@ -87,6 +87,15 @@ def normalize_region(region: str) -> str:
87
87
  return region.replace("&", "-and-").replace("/", "-and-")
88
88
 
89
89
 
90
+ REFERENCE_DATASETS = {
91
+ "hus": "ERA-5",
92
+ "pr": "GPCP-V2.3",
93
+ "psl": "ERA-5",
94
+ "tas": "HadCRUT5-5.0.1.0-analysis",
95
+ "ua": "ERA-5",
96
+ }
97
+
98
+
90
99
  class RegionalHistoricalAnnualCycle(ESMValToolDiagnostic):
91
100
  """
92
101
  Plot regional historical annual cycle of climate variables.
@@ -166,13 +175,21 @@ class RegionalHistoricalAnnualCycle(ESMValToolDiagnostic):
166
175
  SeriesDefinition(
167
176
  file_pattern=f"anncyc-{region}/allplots/*_{var_name}_*.nc",
168
177
  sel={"dim0": 0}, # Select the model and not the observation.
169
- dimensions={"region": region, "statistic": f"{var_name} regional mean"},
178
+ dimensions=(
179
+ {
180
+ "region": region,
181
+ "variable_id": var_name,
182
+ "statistic": "mean",
183
+ }
184
+ | ({} if i == 0 else {"reference_source_id": REFERENCE_DATASETS[var_name]})
185
+ ),
170
186
  values_name=var_name,
171
187
  index_name="month_number",
172
188
  attributes=[],
173
189
  )
174
190
  for var_name in variables
175
191
  for region in REGIONS
192
+ for i in range(2)
176
193
  )
177
194
 
178
195
  @staticmethod
@@ -292,15 +309,15 @@ class RegionalHistoricalTimeSeries(RegionalHistoricalAnnualCycle):
292
309
  series = tuple(
293
310
  SeriesDefinition(
294
311
  file_pattern=f"{diagnostic}-{region}/allplots/*_{var_name}_*.nc",
295
- sel={"dim0": 0}, # Select the model and not the observation.
296
- dimensions={
297
- "region": region,
298
- "statistic": (
299
- f"{var_name} regional mean"
300
- if diagnostic == "timeseries_abs"
301
- else f"{var_name} regional mean anomaly"
302
- ),
303
- },
312
+ sel={"dim0": i},
313
+ dimensions=(
314
+ {
315
+ "region": region,
316
+ "variable_id": var_name,
317
+ "statistic": ("mean" if diagnostic == "timeseries_abs" else "mean anomaly"),
318
+ }
319
+ | ({} if i == 0 else {"reference_source_id": REFERENCE_DATASETS[var_name]})
320
+ ),
304
321
  values_name=var_name,
305
322
  index_name="time",
306
323
  attributes=[],
@@ -308,6 +325,7 @@ class RegionalHistoricalTimeSeries(RegionalHistoricalAnnualCycle):
308
325
  for var_name in variables
309
326
  for region in REGIONS
310
327
  for diagnostic in ["timeseries_abs", "timeseries"]
328
+ for i in range(2)
311
329
  )
312
330
 
313
331
 
@@ -425,7 +443,9 @@ class RegionalHistoricalTrend(ESMValToolDiagnostic):
425
443
  variable_id = next(iter(ds.data_vars.keys()))
426
444
  metric_args[MetricCV.DIMENSIONS.value]["variable_id"][variable_id] = {}
427
445
  metric_args[MetricCV.RESULTS.value][variable_id] = {}
428
- for region_value, trend_value in zip(ds.shape_id.astype(str).values, ds[variable_id].values):
446
+ for region_value, trend_value in zip(
447
+ ds.shape_id.astype(str).values, fillvalues_to_nan(ds[variable_id].values)
448
+ ):
429
449
  region = region_value.strip()
430
450
  trend = float(trend_value)
431
451
  if region not in metric_args[MetricCV.DIMENSIONS.value]["region"]:
@@ -13,6 +13,16 @@ from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
13
13
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
14
14
  from climate_ref_esmvaltool.types import Recipe
15
15
 
16
+ REGIONS = {
17
+ "nh": "Northern Hemisphere",
18
+ "sh": "Southern Hemisphere",
19
+ }
20
+
21
+ MONTHS = {
22
+ "nh": "September",
23
+ "sh": "February",
24
+ }
25
+
16
26
 
17
27
  class SeaIceAreaBasic(ESMValToolDiagnostic):
18
28
  """
@@ -49,51 +59,40 @@ class SeaIceAreaBasic(ESMValToolDiagnostic):
49
59
  # TODO: Use OSI-450-nh and OSI-450-sh from obs4MIPs once available.
50
60
  )
51
61
  facets = ()
52
- series = (
62
+ series = tuple(
53
63
  SeriesDefinition(
54
- file_pattern="siarea_min/allplots/timeseries_sea_ice_area_nh_*.nc",
55
- sel={"dim0": 0}, # Select the model and not the observations.
56
- dimensions={
57
- "region": "Northern Hemisphere",
58
- "statistic": "September sea ice area",
59
- },
60
- values_name="siconc",
61
- index_name="time",
62
- attributes=[],
63
- ),
64
- SeriesDefinition(
65
- file_pattern="siarea_min/allplots/timeseries_sea_ice_area_sh_*.nc",
66
- sel={"dim0": 0}, # Select the model and not the observations.
67
- dimensions={
68
- "region": "Southern Hemisphere",
69
- "statistic": "February sea ice area",
70
- },
64
+ file_pattern=f"siarea_min/allplots/timeseries_sea_ice_area_{region}_*.nc",
65
+ sel={"dim0": i},
66
+ dimensions=(
67
+ {
68
+ "region": REGIONS[region],
69
+ "statistic": f"{MONTHS[region]} sea ice area",
70
+ }
71
+ | ({} if i == 0 else {"reference_source_id": f"OSI-450-{region}"})
72
+ ),
71
73
  values_name="siconc",
72
74
  index_name="time",
73
75
  attributes=[],
74
- ),
76
+ )
77
+ for region in REGIONS
78
+ for i in range(2)
79
+ ) + tuple(
75
80
  SeriesDefinition(
76
- file_pattern="siarea_seas/allplots/annual_cycle_sea_ice_area_nh_*.nc",
77
- sel={"dim0": 0}, # Select the model and not the observations.
78
- dimensions={
79
- "region": "Northern Hemisphere",
80
- "statistic": "20-year average seasonal cycle of the sea ice area",
81
- },
82
- values_name="siconc",
83
- index_name="month_number",
84
- attributes=[],
85
- ),
86
- SeriesDefinition(
87
- file_pattern="siarea_seas/allplots/annual_cycle_sea_ice_area_sh_*.nc",
88
- sel={"dim0": 0}, # Select the model and not the observations.
89
- dimensions={
90
- "region": "Southern Hemisphere",
91
- "statistic": "20-year average seasonal cycle of the sea ice area,",
92
- },
81
+ file_pattern=f"siarea_seas/allplots/annual_cycle_sea_ice_area_{region}_*.nc",
82
+ sel={"dim0": i},
83
+ dimensions=(
84
+ {
85
+ "region": REGIONS[region],
86
+ "statistic": "20-year average seasonal cycle of the sea ice area",
87
+ }
88
+ | ({} if i == 0 else {"reference_source_id": f"OSI-450-{region}"})
89
+ ),
93
90
  values_name="siconc",
94
91
  index_name="month_number",
95
92
  attributes=[],
96
- ),
93
+ )
94
+ for region in REGIONS
95
+ for i in range(2)
97
96
  )
98
97
 
99
98
  @staticmethod
@@ -14,7 +14,7 @@ from climate_ref_core.diagnostics import DataRequirement
14
14
  from climate_ref_core.metric_values.typing import SeriesDefinition
15
15
  from climate_ref_core.pycmec.metric import CMECMetric, MetricCV
16
16
  from climate_ref_core.pycmec.output import CMECOutput
17
- from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
17
+ from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic, fillvalues_to_nan
18
18
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
19
19
  from climate_ref_esmvaltool.types import MetricBundleArgs, OutputBundleArgs, Recipe
20
20
 
@@ -117,7 +117,7 @@ class TransientClimateResponse(ESMValToolDiagnostic):
117
117
  ) -> tuple[CMECMetric, CMECOutput]:
118
118
  """Format the result."""
119
119
  tcr_ds = xarray.open_dataset(result_dir / "work" / "tcr" / "calculate" / "tcr.nc")
120
- tcr = float(tcr_ds["tcr"].values[0])
120
+ tcr = float(fillvalues_to_nan(tcr_ds["tcr"].values)[0])
121
121
 
122
122
  # Update the diagnostic bundle arguments with the computed diagnostics.
123
123
  metric_args[MetricCV.DIMENSIONS.value] = {
@@ -13,7 +13,7 @@ from climate_ref_core.datasets import ExecutionDatasetCollection, FacetFilter, S
13
13
  from climate_ref_core.diagnostics import DataRequirement
14
14
  from climate_ref_core.pycmec.metric import CMECMetric, MetricCV
15
15
  from climate_ref_core.pycmec.output import CMECOutput
16
- from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
16
+ from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic, fillvalues_to_nan
17
17
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
18
18
  from climate_ref_esmvaltool.types import MetricBundleArgs, OutputBundleArgs, Recipe
19
19
 
@@ -123,7 +123,7 @@ class TransientClimateResponseEmissions(ESMValToolDiagnostic):
123
123
  ) -> tuple[CMECMetric, CMECOutput]:
124
124
  """Format the result."""
125
125
  tcre_ds = xarray.open_dataset(result_dir / "work" / "tcre" / "calculate_tcre" / "tcre.nc")
126
- tcre = float(tcre_ds["tcre"].values[0])
126
+ tcre = float(fillvalues_to_nan(tcre_ds["tcre"].values)[0])
127
127
 
128
128
  # Update the diagnostic bundle arguments with the computed diagnostics.
129
129
  metric_args[MetricCV.DIMENSIONS.value] = {
@@ -14,7 +14,7 @@ from climate_ref_core.diagnostics import DataRequirement
14
14
  from climate_ref_core.metric_values.typing import SeriesDefinition
15
15
  from climate_ref_core.pycmec.metric import CMECMetric, MetricCV
16
16
  from climate_ref_core.pycmec.output import CMECOutput
17
- from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
17
+ from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic, fillvalues_to_nan
18
18
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
19
19
  from climate_ref_esmvaltool.types import MetricBundleArgs, OutputBundleArgs, Recipe
20
20
 
@@ -104,7 +104,7 @@ class ZeroEmissionCommitment(ESMValToolDiagnostic):
104
104
  ) -> tuple[CMECMetric, CMECOutput]:
105
105
  """Format the result."""
106
106
  zec_ds = xarray.open_dataset(result_dir / "work" / "zec" / "zec" / "zec_50.nc")
107
- zec = float(zec_ds["zec"].values[0])
107
+ zec = float(fillvalues_to_nan(zec_ds["zec"].values)[0])
108
108
 
109
109
  # Update the diagnostic bundle arguments with the computed diagnostics.
110
110
  metric_args[MetricCV.DIMENSIONS.value] = {