climate-ref-esmvaltool 0.6.6__py3-none-any.whl → 0.8.0__py3-none-any.whl

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 (22) hide show
  1. climate_ref_esmvaltool/diagnostics/base.py +92 -45
  2. climate_ref_esmvaltool/diagnostics/climate_at_global_warming_levels.py +37 -14
  3. climate_ref_esmvaltool/diagnostics/climate_drivers_for_fire.py +37 -15
  4. climate_ref_esmvaltool/diagnostics/cloud_radiative_effects.py +37 -18
  5. climate_ref_esmvaltool/diagnostics/cloud_scatterplots.py +19 -7
  6. climate_ref_esmvaltool/diagnostics/ecs.py +26 -5
  7. climate_ref_esmvaltool/diagnostics/enso.py +98 -8
  8. climate_ref_esmvaltool/diagnostics/example.py +11 -10
  9. climate_ref_esmvaltool/diagnostics/regional_historical_changes.py +139 -24
  10. climate_ref_esmvaltool/diagnostics/sea_ice_area_basic.py +57 -3
  11. climate_ref_esmvaltool/diagnostics/sea_ice_sensitivity.py +26 -12
  12. climate_ref_esmvaltool/diagnostics/tcr.py +16 -3
  13. climate_ref_esmvaltool/diagnostics/tcre.py +10 -12
  14. climate_ref_esmvaltool/diagnostics/zec.py +17 -3
  15. climate_ref_esmvaltool/recipe.py +9 -5
  16. {climate_ref_esmvaltool-0.6.6.dist-info → climate_ref_esmvaltool-0.8.0.dist-info}/METADATA +2 -1
  17. climate_ref_esmvaltool-0.8.0.dist-info/RECORD +30 -0
  18. {climate_ref_esmvaltool-0.6.6.dist-info → climate_ref_esmvaltool-0.8.0.dist-info}/WHEEL +1 -1
  19. climate_ref_esmvaltool-0.6.6.dist-info/RECORD +0 -30
  20. {climate_ref_esmvaltool-0.6.6.dist-info → climate_ref_esmvaltool-0.8.0.dist-info}/entry_points.txt +0 -0
  21. {climate_ref_esmvaltool-0.6.6.dist-info → climate_ref_esmvaltool-0.8.0.dist-info}/licenses/LICENCE +0 -0
  22. {climate_ref_esmvaltool-0.6.6.dist-info → climate_ref_esmvaltool-0.8.0.dist-info}/licenses/NOTICE +0 -0
@@ -11,6 +11,7 @@ from climate_ref_core.constraints import (
11
11
  )
12
12
  from climate_ref_core.datasets import ExecutionDatasetCollection, FacetFilter, SourceDatasetType
13
13
  from climate_ref_core.diagnostics import DataRequirement
14
+ from climate_ref_core.metric_values.typing import SeriesDefinition
14
15
  from climate_ref_core.pycmec.metric import CMECMetric, MetricCV
15
16
  from climate_ref_core.pycmec.output import CMECOutput
16
17
  from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
@@ -27,32 +28,117 @@ class ENSOBasicClimatology(ESMValToolDiagnostic):
27
28
  slug = "enso-basic-climatology"
28
29
  base_recipe = "ref/recipe_enso_basicclimatology.yml"
29
30
 
30
- variables = (
31
- "pr",
32
- "tos",
33
- "tauu",
34
- )
35
31
  data_requirements = (
36
32
  DataRequirement(
37
33
  source_type=SourceDatasetType.CMIP6,
38
34
  filters=(
39
35
  FacetFilter(
40
36
  facets={
41
- "variable_id": variables,
37
+ "variable_id": ("pr", "tauu"),
42
38
  "experiment_id": "historical",
39
+ "table_id": "Amon",
40
+ },
41
+ ),
42
+ FacetFilter(
43
+ facets={
44
+ "variable_id": "tos",
45
+ "experiment_id": "historical",
46
+ "table_id": "Omon",
43
47
  },
44
48
  ),
45
49
  ),
46
50
  group_by=("source_id", "member_id", "grid_label"),
47
51
  constraints=(
48
- RequireFacets("variable_id", variables),
49
52
  RequireContiguousTimerange(group_by=("instance_id",)),
50
53
  RequireOverlappingTimerange(group_by=("instance_id",)),
54
+ RequireFacets(
55
+ "variable_id",
56
+ (
57
+ "pr",
58
+ "tauu",
59
+ "tos",
60
+ ),
61
+ ),
51
62
  ),
52
63
  ),
53
64
  )
54
65
  facets = ()
55
66
 
67
+ series = (
68
+ tuple(
69
+ SeriesDefinition(
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
+ ),
79
+ values_name="tos" if var_name == "sst" else var_name,
80
+ index_name="lon",
81
+ attributes=[],
82
+ )
83
+ for var_name in ("pr", "sst", "tauu")
84
+ for source_id in ("{source_id}", "GPCP-V2.3", "TROPFLUX")
85
+ )
86
+ + tuple(
87
+ SeriesDefinition(
88
+ file_pattern=f"diagnostic_metrics/plot_script/{{source_id}}_eq_{var_name}_seacycle.nc",
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
+ ),
98
+ values_name="tos" if var_name == "sst" else var_name,
99
+ index_name="lon",
100
+ attributes=[],
101
+ )
102
+ for var_name in ("pr", "sst", "tauu")
103
+ for source_id in ("{source_id}", "GPCP-V2.3", "TROPFLUX")
104
+ )
105
+ + tuple(
106
+ SeriesDefinition(
107
+ file_pattern="diagnostic_metrics/plot_script/{source_id}_pr_double.nc",
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
+ ),
116
+ values_name="pr",
117
+ index_name="lat",
118
+ attributes=[],
119
+ )
120
+ for source_id in ("{source_id}", "GPCP-V2.3")
121
+ )
122
+ + tuple(
123
+ SeriesDefinition(
124
+ file_pattern="diagnostic_metrics/plot_script/*_pr_double_seacycle.nc",
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
+ ),
134
+ values_name="pr",
135
+ index_name="lat",
136
+ attributes=[],
137
+ )
138
+ for source_id in ("{source_id}", "GPCP-V2.3")
139
+ )
140
+ )
141
+
56
142
  @staticmethod
57
143
  def update_recipe(
58
144
  recipe: Recipe,
@@ -85,19 +171,23 @@ class ENSOCharacteristics(ESMValToolDiagnostic):
85
171
  facets={
86
172
  "variable_id": "tos",
87
173
  "experiment_id": "historical",
174
+ "table_id": "Omon",
88
175
  },
89
176
  ),
90
177
  ),
91
178
  group_by=("source_id", "member_id", "grid_label"),
92
179
  constraints=(
93
- RequireFacets("variable_id", ("tos",)),
94
180
  RequireContiguousTimerange(group_by=("instance_id",)),
95
181
  RequireOverlappingTimerange(group_by=("instance_id",)),
96
182
  AddSupplementaryDataset.from_defaults("areacello", SourceDatasetType.CMIP6),
183
+ RequireFacets("variable_id", ("tos", "areacello")),
97
184
  ),
98
185
  ),
99
186
  )
100
187
  facets = ("grid_label", "member_id", "source_id", "region", "metric")
188
+ # ENSO pattern and lifecycle are series, but the ESMValTool diagnostic
189
+ # script does not save the values used in the figure.
190
+ series = tuple()
101
191
 
102
192
  @staticmethod
103
193
  def update_recipe(
@@ -17,28 +17,29 @@ class GlobalMeanTimeseries(ESMValToolDiagnostic):
17
17
  name = "Global Mean Timeseries"
18
18
  slug = "global-mean-timeseries"
19
19
  base_recipe = "examples/recipe_python.yml"
20
- series = (
21
- SeriesDefinition(
22
- file_pattern="timeseries/script1/*.nc",
23
- dimensions={},
24
- values_name="tas",
25
- index_name="time",
26
- attributes=[],
27
- ),
28
- )
29
20
 
30
21
  data_requirements = (
31
22
  DataRequirement(
32
23
  source_type=SourceDatasetType.CMIP6,
33
24
  filters=(FacetFilter(facets={"variable_id": ("tas",)}),),
34
- group_by=("instance_id",),
25
+ group_by=("source_id", "experiment_id", "member_id", "table_id", "variable_id", "grid_label"),
35
26
  constraints=(
36
27
  RequireContiguousTimerange(group_by=("instance_id",)),
37
28
  AddSupplementaryDataset.from_defaults("areacella", SourceDatasetType.CMIP6),
38
29
  ),
39
30
  ),
40
31
  )
32
+
41
33
  facets = ()
34
+ series = (
35
+ SeriesDefinition(
36
+ file_pattern="timeseries/script1/*.nc",
37
+ dimensions={"statistic": "tas annual global mean"},
38
+ values_name="tas",
39
+ index_name="time",
40
+ attributes=[],
41
+ ),
42
+ )
42
43
 
43
44
  @staticmethod
44
45
  def update_recipe(
@@ -7,15 +7,16 @@ import xarray
7
7
 
8
8
  from climate_ref_core.constraints import (
9
9
  AddSupplementaryDataset,
10
- RequireContiguousTimerange,
10
+ PartialDateTime,
11
11
  RequireFacets,
12
+ RequireTimerange,
12
13
  )
13
14
  from climate_ref_core.datasets import ExecutionDatasetCollection, FacetFilter, SourceDatasetType
14
15
  from climate_ref_core.diagnostics import DataRequirement
15
16
  from climate_ref_core.metric_values.typing import SeriesDefinition
16
17
  from climate_ref_core.pycmec.metric import CMECMetric, MetricCV
17
18
  from climate_ref_core.pycmec.output import CMECOutput
18
- from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
19
+ from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic, fillvalues_to_nan
19
20
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
20
21
  from climate_ref_esmvaltool.types import MetricBundleArgs, OutputBundleArgs, Recipe
21
22
 
@@ -86,6 +87,15 @@ def normalize_region(region: str) -> str:
86
87
  return region.replace("&", "-and-").replace("/", "-and-")
87
88
 
88
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
+
89
99
  class RegionalHistoricalAnnualCycle(ESMValToolDiagnostic):
90
100
  """
91
101
  Plot regional historical annual cycle of climate variables.
@@ -102,18 +112,6 @@ class RegionalHistoricalAnnualCycle(ESMValToolDiagnostic):
102
112
  "tas",
103
113
  "ua",
104
114
  )
105
- series = tuple(
106
- SeriesDefinition(
107
- file_pattern=f"anncyc-{region}/allplots/*_{var_name}_*.nc",
108
- sel={"dim0": 0}, # Select the model and not the observation.
109
- dimensions={"region": region},
110
- values_name=var_name,
111
- index_name="month_number",
112
- attributes=[],
113
- )
114
- for var_name in variables
115
- for region in REGIONS
116
- )
117
115
 
118
116
  data_requirements = (
119
117
  DataRequirement(
@@ -123,14 +121,18 @@ class RegionalHistoricalAnnualCycle(ESMValToolDiagnostic):
123
121
  facets={
124
122
  "variable_id": variables,
125
123
  "experiment_id": "historical",
126
- "frequency": "mon",
124
+ "table_id": "Amon",
127
125
  },
128
126
  ),
129
127
  ),
130
128
  group_by=("source_id", "member_id", "grid_label"),
131
129
  constraints=(
130
+ RequireTimerange(
131
+ group_by=("instance_id",),
132
+ start=PartialDateTime(1980, 1),
133
+ end=PartialDateTime(2009, 12),
134
+ ),
132
135
  RequireFacets("variable_id", variables),
133
- RequireContiguousTimerange(group_by=("instance_id",)),
134
136
  AddSupplementaryDataset.from_defaults("areacella", SourceDatasetType.CMIP6),
135
137
  ),
136
138
  ),
@@ -149,7 +151,14 @@ class RegionalHistoricalAnnualCycle(ESMValToolDiagnostic):
149
151
  ),
150
152
  ),
151
153
  group_by=("source_id",),
152
- constraints=(RequireContiguousTimerange(group_by=("instance_id",)),),
154
+ constraints=(
155
+ RequireTimerange(
156
+ group_by=("instance_id",),
157
+ start=PartialDateTime(1980, 1),
158
+ end=PartialDateTime(2009, 12),
159
+ ),
160
+ RequireFacets("variable_id", ("psl", "ua")),
161
+ ),
153
162
  # TODO: Add obs4MIPs datasets once available and working:
154
163
  #
155
164
  # obs4MIPs dataset that cannot be ingested (https://github.com/Climate-REF/climate-ref/issues/260):
@@ -160,7 +169,28 @@ class RegionalHistoricalAnnualCycle(ESMValToolDiagnostic):
160
169
  # - HadCRUT5_ground_5.0.1.0-analysis: tas
161
170
  ),
162
171
  )
172
+
163
173
  facets = ()
174
+ series = tuple(
175
+ SeriesDefinition(
176
+ file_pattern=f"anncyc-{region}/allplots/*_{var_name}_*.nc",
177
+ sel={"dim0": 0}, # Select the model and not the observation.
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
+ ),
186
+ values_name=var_name,
187
+ index_name="month_number",
188
+ attributes=[],
189
+ )
190
+ for var_name in variables
191
+ for region in REGIONS
192
+ for i in range(2)
193
+ )
164
194
 
165
195
  @staticmethod
166
196
  def update_recipe(
@@ -211,18 +241,91 @@ class RegionalHistoricalTimeSeries(RegionalHistoricalAnnualCycle):
211
241
  name = "Regional historical mean and anomaly of climate variables"
212
242
  slug = "regional-historical-timeseries"
213
243
  base_recipe = "ref/recipe_ref_timeseries_region.yml"
244
+
245
+ variables = (
246
+ "hus",
247
+ "pr",
248
+ "psl",
249
+ "tas",
250
+ "ua",
251
+ )
252
+
253
+ data_requirements = (
254
+ DataRequirement(
255
+ source_type=SourceDatasetType.CMIP6,
256
+ filters=(
257
+ FacetFilter(
258
+ facets={
259
+ "variable_id": variables,
260
+ "experiment_id": "historical",
261
+ "table_id": "Amon",
262
+ },
263
+ ),
264
+ ),
265
+ group_by=("source_id", "member_id", "grid_label"),
266
+ constraints=(
267
+ RequireTimerange(
268
+ group_by=("instance_id",),
269
+ start=PartialDateTime(1980, 1),
270
+ end=PartialDateTime(2014, 12),
271
+ ),
272
+ RequireFacets("variable_id", variables),
273
+ AddSupplementaryDataset.from_defaults("areacella", SourceDatasetType.CMIP6),
274
+ ),
275
+ ),
276
+ DataRequirement(
277
+ source_type=SourceDatasetType.obs4MIPs,
278
+ filters=(
279
+ FacetFilter(
280
+ facets={
281
+ "variable_id": (
282
+ "psl",
283
+ "ua",
284
+ ),
285
+ "source_id": "ERA-5",
286
+ "frequency": "mon",
287
+ },
288
+ ),
289
+ ),
290
+ group_by=("source_id",),
291
+ constraints=(
292
+ RequireTimerange(
293
+ group_by=("instance_id",),
294
+ start=PartialDateTime(1980, 1),
295
+ end=PartialDateTime(2014, 12),
296
+ ),
297
+ ),
298
+ # TODO: Add obs4MIPs datasets once available and working:
299
+ #
300
+ # obs4MIPs dataset that cannot be ingested (https://github.com/Climate-REF/climate-ref/issues/260):
301
+ # - GPCP-V2.3: pr
302
+ #
303
+ # Not yet available on obs4MIPs:
304
+ # - ERA5: hus
305
+ # - HadCRUT5_ground_5.0.1.0-analysis: tas
306
+ ),
307
+ )
308
+
214
309
  series = tuple(
215
310
  SeriesDefinition(
216
311
  file_pattern=f"{diagnostic}-{region}/allplots/*_{var_name}_*.nc",
217
- sel={"dim0": 0}, # Select the model and not the observation.
218
- dimensions={"region": region},
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
+ ),
219
321
  values_name=var_name,
220
322
  index_name="time",
221
323
  attributes=[],
222
324
  )
223
- for var_name in RegionalHistoricalAnnualCycle.variables
325
+ for var_name in variables
224
326
  for region in REGIONS
225
327
  for diagnostic in ["timeseries_abs", "timeseries"]
328
+ for i in range(2)
226
329
  )
227
330
 
228
331
 
@@ -249,13 +352,17 @@ class RegionalHistoricalTrend(ESMValToolDiagnostic):
249
352
  "ua",
250
353
  ),
251
354
  "experiment_id": "historical",
252
- "frequency": "mon",
355
+ "table_id": "Amon",
253
356
  },
254
357
  ),
255
358
  ),
256
359
  group_by=("source_id", "member_id", "grid_label"),
257
360
  constraints=(
258
- RequireContiguousTimerange(group_by=("instance_id",)),
361
+ RequireTimerange(
362
+ group_by=("instance_id",),
363
+ start=PartialDateTime(1980, 1),
364
+ end=PartialDateTime(2009, 12),
365
+ ),
259
366
  AddSupplementaryDataset.from_defaults("areacella", SourceDatasetType.CMIP6),
260
367
  ),
261
368
  ),
@@ -275,7 +382,13 @@ class RegionalHistoricalTrend(ESMValToolDiagnostic):
275
382
  ),
276
383
  ),
277
384
  group_by=("source_id",),
278
- constraints=(RequireContiguousTimerange(group_by=("instance_id",)),),
385
+ constraints=(
386
+ RequireTimerange(
387
+ group_by=("instance_id",),
388
+ start=PartialDateTime(1980, 1),
389
+ end=PartialDateTime(2009, 12),
390
+ ),
391
+ ),
279
392
  # TODO: Add obs4MIPs datasets once available and working:
280
393
  #
281
394
  # obs4MIPs dataset that cannot be ingested (https://github.com/Climate-REF/climate-ref/issues/260):
@@ -330,7 +443,9 @@ class RegionalHistoricalTrend(ESMValToolDiagnostic):
330
443
  variable_id = next(iter(ds.data_vars.keys()))
331
444
  metric_args[MetricCV.DIMENSIONS.value]["variable_id"][variable_id] = {}
332
445
  metric_args[MetricCV.RESULTS.value][variable_id] = {}
333
- 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
+ ):
334
449
  region = region_value.strip()
335
450
  trend = float(trend_value)
336
451
  if region not in metric_args[MetricCV.DIMENSIONS.value]["region"]:
@@ -2,14 +2,27 @@ import pandas
2
2
 
3
3
  from climate_ref_core.constraints import (
4
4
  AddSupplementaryDataset,
5
- RequireContiguousTimerange,
5
+ PartialDateTime,
6
+ RequireFacets,
7
+ RequireTimerange,
6
8
  )
7
9
  from climate_ref_core.datasets import FacetFilter, SourceDatasetType
8
10
  from climate_ref_core.diagnostics import DataRequirement
11
+ from climate_ref_core.metric_values.typing import SeriesDefinition
9
12
  from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
10
13
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
11
14
  from climate_ref_esmvaltool.types import Recipe
12
15
 
16
+ REGIONS = {
17
+ "nh": "Northern Hemisphere",
18
+ "sh": "Southern Hemisphere",
19
+ }
20
+
21
+ MONTHS = {
22
+ "nh": "September",
23
+ "sh": "February",
24
+ }
25
+
13
26
 
14
27
  class SeaIceAreaBasic(ESMValToolDiagnostic):
15
28
  """
@@ -28,18 +41,59 @@ class SeaIceAreaBasic(ESMValToolDiagnostic):
28
41
  facets={
29
42
  "variable_id": "siconc",
30
43
  "experiment_id": "historical",
44
+ "table_id": "SImon",
31
45
  },
32
46
  ),
33
47
  ),
34
- group_by=("instance_id",),
48
+ group_by=("source_id", "member_id", "grid_label"),
35
49
  constraints=(
36
- RequireContiguousTimerange(group_by=("instance_id",)),
50
+ RequireTimerange(
51
+ group_by=("instance_id",),
52
+ start=PartialDateTime(1979, 1),
53
+ end=PartialDateTime(2014, 12),
54
+ ),
37
55
  AddSupplementaryDataset.from_defaults("areacello", SourceDatasetType.CMIP6),
56
+ RequireFacets("variable_id", ("siconc", "areacello")),
38
57
  ),
39
58
  ),
40
59
  # TODO: Use OSI-450-nh and OSI-450-sh from obs4MIPs once available.
41
60
  )
42
61
  facets = ()
62
+ series = tuple(
63
+ SeriesDefinition(
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
+ ),
73
+ values_name="siconc",
74
+ index_name="time",
75
+ attributes=[],
76
+ )
77
+ for region in REGIONS
78
+ for i in range(2)
79
+ ) + tuple(
80
+ SeriesDefinition(
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
+ ),
90
+ values_name="siconc",
91
+ index_name="month_number",
92
+ attributes=[],
93
+ )
94
+ for region in REGIONS
95
+ for i in range(2)
96
+ )
43
97
 
44
98
  @staticmethod
45
99
  def update_recipe(
@@ -5,8 +5,9 @@ import pandas as pd
5
5
 
6
6
  from climate_ref_core.constraints import (
7
7
  AddSupplementaryDataset,
8
- RequireContiguousTimerange,
8
+ PartialDateTime,
9
9
  RequireFacets,
10
+ RequireTimerange,
10
11
  )
11
12
  from climate_ref_core.datasets import ExecutionDatasetCollection, FacetFilter, SourceDatasetType
12
13
  from climate_ref_core.diagnostics import DataRequirement
@@ -26,31 +27,44 @@ class SeaIceSensitivity(ESMValToolDiagnostic):
26
27
  slug = "sea-ice-sensitivity"
27
28
  base_recipe = "recipe_seaice_sensitivity.yml"
28
29
 
29
- variables = (
30
- "siconc",
31
- "tas",
32
- )
33
-
34
30
  data_requirements = (
35
31
  DataRequirement(
36
32
  source_type=SourceDatasetType.CMIP6,
37
33
  filters=(
38
34
  FacetFilter(
39
35
  facets={
40
- "variable_id": variables,
36
+ "variable_id": "siconc",
37
+ "experiment_id": "historical",
38
+ "table_id": "SImon",
39
+ },
40
+ ),
41
+ FacetFilter(
42
+ facets={
43
+ "variable_id": "tas",
41
44
  "experiment_id": "historical",
45
+ "table_id": "Amon",
42
46
  },
43
47
  ),
44
48
  ),
45
49
  group_by=("experiment_id",), # this does nothing, but group_by cannot be empty
46
50
  constraints=(
51
+ RequireTimerange(
52
+ group_by=("instance_id",),
53
+ start=PartialDateTime(1979, 1),
54
+ end=PartialDateTime(2014, 12),
55
+ ),
56
+ RequireFacets(
57
+ "variable_id",
58
+ required_facets=("siconc", "tas"),
59
+ group_by=("source_id", "member_id", "grid_label"),
60
+ ),
47
61
  AddSupplementaryDataset.from_defaults("areacella", SourceDatasetType.CMIP6),
48
62
  AddSupplementaryDataset.from_defaults("areacello", SourceDatasetType.CMIP6),
49
- RequireContiguousTimerange(group_by=("instance_id",)),
50
- RequireFacets("variable_id", variables),
51
- # TODO: Add a constraint to ensure that tas, siconc and areacello
52
- # are available for each model or alternatively filter out
53
- # incomplete models below.
63
+ RequireFacets(
64
+ "variable_id",
65
+ required_facets=("areacello",),
66
+ group_by=("source_id", "grid_label"),
67
+ ),
54
68
  ),
55
69
  ),
56
70
  )
@@ -11,9 +11,10 @@ from climate_ref_core.constraints import (
11
11
  )
12
12
  from climate_ref_core.datasets import ExecutionDatasetCollection, FacetFilter, SourceDatasetType
13
13
  from climate_ref_core.diagnostics import DataRequirement
14
+ from climate_ref_core.metric_values.typing import SeriesDefinition
14
15
  from climate_ref_core.pycmec.metric import CMECMetric, MetricCV
15
16
  from climate_ref_core.pycmec.output import CMECOutput
16
- from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic
17
+ from climate_ref_esmvaltool.diagnostics.base import ESMValToolDiagnostic, fillvalues_to_nan
17
18
  from climate_ref_esmvaltool.recipe import dataframe_to_recipe
18
19
  from climate_ref_esmvaltool.types import MetricBundleArgs, OutputBundleArgs, Recipe
19
20
 
@@ -39,19 +40,31 @@ class TransientClimateResponse(ESMValToolDiagnostic):
39
40
  facets={
40
41
  "variable_id": ("tas",),
41
42
  "experiment_id": experiments,
43
+ "table_id": "Amon",
42
44
  },
43
45
  ),
44
46
  ),
45
47
  group_by=("source_id", "member_id", "grid_label"),
46
48
  constraints=(
47
- RequireFacets("experiment_id", experiments),
48
49
  RequireContiguousTimerange(group_by=("instance_id",)),
49
50
  RequireOverlappingTimerange(group_by=("instance_id",)),
51
+ RequireFacets("experiment_id", experiments),
50
52
  AddSupplementaryDataset.from_defaults("areacella", SourceDatasetType.CMIP6),
51
53
  ),
52
54
  ),
53
55
  )
54
56
  facets = ("grid_label", "member_id", "source_id", "region", "metric")
57
+ series = (
58
+ SeriesDefinition(
59
+ file_pattern="tcr/calculate/{source_id}*.nc",
60
+ dimensions={
61
+ "statistic": "global annual mean tas anomaly relative to linear fit of piControl run",
62
+ },
63
+ values_name="tas_anomaly",
64
+ index_name="time",
65
+ attributes=[],
66
+ ),
67
+ )
55
68
 
56
69
  @staticmethod
57
70
  def update_recipe(
@@ -104,7 +117,7 @@ class TransientClimateResponse(ESMValToolDiagnostic):
104
117
  ) -> tuple[CMECMetric, CMECOutput]:
105
118
  """Format the result."""
106
119
  tcr_ds = xarray.open_dataset(result_dir / "work" / "tcr" / "calculate" / "tcr.nc")
107
- tcr = float(tcr_ds["tcr"].values[0])
120
+ tcr = float(fillvalues_to_nan(tcr_ds["tcr"].values)[0])
108
121
 
109
122
  # Update the diagnostic bundle arguments with the computed diagnostics.
110
123
  metric_args[MetricCV.DIMENSIONS.value] = {