climate-ref-ilamb 0.6.5__py3-none-any.whl → 0.6.6__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.
@@ -25,7 +25,7 @@ mrsos-WangMao:
25
25
  mrsol: ilamb/mrsol/WangMao/mrsol_olc.nc
26
26
  alternate_vars:
27
27
  - mrsos
28
- transform:
28
+ transforms:
29
29
  - select_depth:
30
30
  value: 0
31
31
  - soil_moisture_to_vol_fraction
@@ -5,7 +5,7 @@ thetao-WOA2023-surface:
5
5
  # TODO: Update to use the obs4REF equiv
6
6
  thetao: ilamb/WOA/thetao_mon_WOA_A5B4_gn_200501-201412.nc
7
7
  variable_cmap: Reds
8
- transform:
8
+ transforms:
9
9
  - select_depth:
10
10
  value: 0
11
11
  alternate_vars:
@@ -15,7 +15,7 @@ so-WOA2023-surface:
15
15
  sources:
16
16
  # TODO: Update to use the obs4REF equiv
17
17
  so: ilamb/WOA/so_mon_WOA_A5B4_gn_200501-201412.nc
18
- transform:
18
+ transforms:
19
19
  - select_depth:
20
20
  value: 0
21
21
  variable_cmap: YlGn
@@ -27,7 +27,7 @@ amoc-RAPID:
27
27
  - timeseries
28
28
  related_vars:
29
29
  - msftmz
30
- transform:
30
+ transforms:
31
31
  - msftmz_to_rapid
32
32
  sources:
33
33
  # TODO: Update to use the obs4REF equiv
@@ -39,7 +39,7 @@ ohc-NOAA:
39
39
  related_vars:
40
40
  - thetao
41
41
  - volcello
42
- transform:
42
+ transforms:
43
43
  - select_depth:
44
44
  min: 0
45
45
  max: 2000
@@ -1,14 +1,14 @@
1
1
  from pathlib import Path
2
2
  from typing import Any
3
3
 
4
- import ilamb3 # type: ignore
5
- import ilamb3.regions as ilr # type: ignore
6
- import matplotlib.pyplot as plt
4
+ import ilamb3
5
+ import ilamb3.regions as ilr
7
6
  import pandas as pd
8
7
  import pooch
8
+ import xarray as xr
9
9
  from ilamb3 import run
10
10
 
11
- from climate_ref_core.constraints import AddSupplementaryDataset
11
+ from climate_ref_core.constraints import AddSupplementaryDataset, RequireFacets
12
12
  from climate_ref_core.dataset_registry import dataset_registry_manager
13
13
  from climate_ref_core.datasets import FacetFilter, SourceDatasetType
14
14
  from climate_ref_core.diagnostics import (
@@ -17,8 +17,9 @@ from climate_ref_core.diagnostics import (
17
17
  ExecutionDefinition,
18
18
  ExecutionResult,
19
19
  )
20
+ from climate_ref_core.metric_values.typing import SeriesMetricValue
20
21
  from climate_ref_core.pycmec.metric import CMECMetric
21
- from climate_ref_core.pycmec.output import CMECOutput
22
+ from climate_ref_core.pycmec.output import CMECOutput, OutputCV
22
23
  from climate_ref_ilamb.datasets import (
23
24
  registry_to_collection,
24
25
  )
@@ -101,16 +102,7 @@ def _build_cmec_bundle(df: pd.DataFrame) -> dict[str, Any]:
101
102
  # reference_df = df[df["source"] == "Reference"]
102
103
  model_df = df[df["source"] != "Reference"]
103
104
 
104
- # Source is formatted as "ACCESS-ESM1-5-r1i1p1f1-gn"
105
- # This assumes that the member_id and grid_label are always the last two parts of the source string
106
- # and don't contain '-'
107
- extracted_source = model_df.source.str.extract(r"([\w-]+)-([\w\d]+)-([\w\d]+)")
108
- model_df.loc[:, "source_id"] = extracted_source[0]
109
- model_df.loc[:, "member_id"] = extracted_source[1]
110
- model_df.loc[:, "grid_label"] = extracted_source[2]
111
-
112
- # Strip out units from the name
113
- # These are available in the attributes
105
+ # Strip out units from the name (available in the attributes)
114
106
  extracted_source = model_df.name.str.extract(r"(.*)\s\[.*\]")
115
107
  model_df.loc[:, "name"] = extracted_source[0]
116
108
 
@@ -149,20 +141,11 @@ def _build_cmec_bundle(df: pd.DataFrame) -> dict[str, Any]:
149
141
  return bundle
150
142
 
151
143
 
152
- def _form_bundles(df: pd.DataFrame) -> tuple[CMECMetric, CMECOutput]:
153
- """
154
- Create the output bundles (really a lift to make Ruff happy with the size of run()).
155
- """
156
- metric_bundle = _build_cmec_bundle(df)
157
- output_bundle = CMECOutput.create_template()
158
- return CMECMetric.model_validate(metric_bundle), CMECOutput.model_validate(output_bundle)
159
-
160
-
161
144
  def _set_ilamb3_options(registry: pooch.Pooch, registry_file: str) -> None:
162
145
  """
163
146
  Set options for ILAMB based on which registry file is being used.
164
147
  """
165
- ilamb3.conf.reset()
148
+ ilamb3.conf.reset() # type: ignore
166
149
  ilamb_regions = ilr.Regions()
167
150
  if registry_file == "ilamb":
168
151
  ilamb_regions.add_netcdf(registry.fetch("ilamb/regions/GlobalLand.nc"))
@@ -213,29 +196,52 @@ class ILAMBStandard(Diagnostic):
213
196
  facets={
214
197
  "variable_id": (
215
198
  self.variable_id,
216
- *ilamb_kwargs.get("relationships", {}).keys(),
217
199
  *ilamb_kwargs.get("alternate_vars", []),
218
200
  *ilamb_kwargs.get("related_vars", []),
201
+ *ilamb_kwargs.get("relationships", {}).keys(),
219
202
  )
220
203
  }
221
204
  ),
222
205
  FacetFilter(facets={"frequency": ("mon",)}),
223
206
  FacetFilter(facets={"experiment_id": ("historical", "land-hist")}),
224
- # Exclude unneeded snc tables
225
207
  FacetFilter(facets={"table_id": ("ImonAnt", "ImonGre")}, keep=False),
226
208
  ),
227
209
  constraints=(
228
- AddSupplementaryDataset.from_defaults("areacella", SourceDatasetType.CMIP6),
229
- AddSupplementaryDataset.from_defaults("sftlf", SourceDatasetType.CMIP6),
230
- )
231
- if registry_file == "ilamb"
232
- else (
233
- AddSupplementaryDataset.from_defaults("areacello", SourceDatasetType.CMIP6),
234
- AddSupplementaryDataset.from_defaults("sftof", SourceDatasetType.CMIP6),
210
+ RequireFacets(
211
+ "variable_id",
212
+ (
213
+ self.variable_id,
214
+ *ilamb_kwargs.get("alternate_vars", []),
215
+ *ilamb_kwargs.get("related_vars", []),
216
+ ),
217
+ operator="any",
218
+ ),
219
+ *(
220
+ [
221
+ RequireFacets(
222
+ "variable_id",
223
+ required_facets=tuple(ilamb_kwargs.get("relationships", {}).keys()),
224
+ )
225
+ ]
226
+ if "relationships" in ilamb_kwargs
227
+ else []
228
+ ),
229
+ *(
230
+ (
231
+ AddSupplementaryDataset.from_defaults("areacella", SourceDatasetType.CMIP6),
232
+ AddSupplementaryDataset.from_defaults("sftlf", SourceDatasetType.CMIP6),
233
+ )
234
+ if registry_file == "ilamb"
235
+ else (
236
+ AddSupplementaryDataset.from_defaults("areacello", SourceDatasetType.CMIP6),
237
+ AddSupplementaryDataset.from_defaults("sftof", SourceDatasetType.CMIP6),
238
+ )
239
+ ),
235
240
  ),
236
- group_by=("experiment_id",),
241
+ group_by=("experiment_id", "source_id", "member_id", "grid_label"),
237
242
  ),
238
243
  )
244
+
239
245
  self.facets = (
240
246
  "experiment_id",
241
247
  "source_id",
@@ -257,12 +263,11 @@ class ILAMBStandard(Diagnostic):
257
263
  """
258
264
  Run the ILAMB standard analysis.
259
265
  """
260
- plt.rcParams.update({"figure.max_open_warning": 0})
261
266
  _set_ilamb3_options(self.registry, self.registry_file)
262
267
  ref_datasets = self.ilamb_data.datasets.set_index(self.ilamb_data.slug_column)
263
- run.run_simple(
264
- ref_datasets,
268
+ run.run_single_block(
265
269
  self.slug,
270
+ ref_datasets,
266
271
  definition.datasets[SourceDatasetType.CMIP6].datasets,
267
272
  definition.output_directory,
268
273
  **self.ilamb_kwargs,
@@ -281,15 +286,91 @@ class ILAMBStandard(Diagnostic):
281
286
  -------
282
287
  An execution result object
283
288
  """
284
- selectors = definition.datasets[SourceDatasetType.CMIP6].selector_dict()
285
289
  _set_ilamb3_options(self.registry, self.registry_file)
286
-
290
+ # In ILAMB, scalars are saved in CSV files in the output directory. To
291
+ # be compatible with the REF system we will need to add the metadata
292
+ # that is associated with the execution group, called the selector.
287
293
  df = _load_csv_and_merge(definition.output_directory)
288
- # Add the selectors to the dataframe
294
+ selectors = definition.datasets[SourceDatasetType.CMIP6].selector_dict()
289
295
  for key, value in selectors.items():
290
296
  df[key] = value
291
- metric_bundle, output_bundle = _form_bundles(df)
297
+ metric_bundle = CMECMetric.model_validate(_build_cmec_bundle(df))
298
+
299
+ # Add each png file plot to the output
300
+ output_bundle = CMECOutput.create_template()
301
+ for plotfile in definition.output_directory.glob("*.png"):
302
+ output_bundle[OutputCV.PLOTS.value][f"{plotfile}"] = {
303
+ OutputCV.FILENAME.value: f"{plotfile}",
304
+ OutputCV.LONG_NAME.value: _caption_from_filename(plotfile),
305
+ OutputCV.DESCRIPTION.value: "",
306
+ }
307
+
308
+ # Add the html page to the output
309
+ index_html = str(definition.to_output_path("index.html"))
310
+ output_bundle[OutputCV.HTML.value][index_html] = {
311
+ OutputCV.FILENAME.value: index_html,
312
+ OutputCV.LONG_NAME.value: "Results page",
313
+ OutputCV.DESCRIPTION.value: "Page displaying scalars and plots from the ILAMB execution.",
314
+ }
315
+ output_bundle[OutputCV.INDEX.value] = index_html
316
+
317
+ # Add series to the output based on the time traces we find in the
318
+ # output files
319
+ series = []
320
+ for ncfile in definition.output_directory.glob("*.nc"):
321
+ ds = xr.open_dataset(ncfile)
322
+ for name, da in ds.items():
323
+ # Only create series for 1d DataArray's with these dimensions
324
+ if not (da.ndim == 1 and set(da.dims).intersection(["time", "month"])):
325
+ continue
326
+ # Convert dimension values
327
+ attrs = {}
328
+ str_name = str(name)
329
+ index_name = str(da.dims[0])
330
+ index = ds[index_name].values.tolist()
331
+ if hasattr(index[0], "isoformat"):
332
+ index = [v.isoformat() for v in index]
333
+ if hasattr(index[0], "calendar"):
334
+ attrs["calendar"] = index[0].calendar
335
+ # Parse out some CVs
336
+ dimensions = {"metric": str_name, "source_id": ncfile.stem}
337
+ if "_" in str_name:
338
+ dimensions["region"] = str_name.split("_")[1]
339
+ series.append(
340
+ SeriesMetricValue(
341
+ dimensions=dimensions,
342
+ values=da.values.tolist(),
343
+ index=index,
344
+ index_name=index_name,
345
+ attributes=attrs,
346
+ )
347
+ )
292
348
 
293
349
  return ExecutionResult.build_from_output_bundle(
294
- definition, cmec_output_bundle=output_bundle, cmec_metric_bundle=metric_bundle
350
+ definition, cmec_output_bundle=output_bundle, cmec_metric_bundle=metric_bundle, series=series
295
351
  )
352
+
353
+
354
+ def _caption_from_filename(filename: Path) -> str:
355
+ source, region, plot = filename.stem.split("_")
356
+ plot_texts = {
357
+ "bias": "bias",
358
+ "biasscore": "bias score",
359
+ "cycle": "annual cycle",
360
+ "cyclescore": "annual cycle score",
361
+ "mean": "period mean",
362
+ "rmse": "RMSE",
363
+ "rmsescore": "RMSE score",
364
+ "shift": "shift in maximum month",
365
+ "tmax": "maxmimum month",
366
+ "trace": "regional mean",
367
+ "taylor": "Taylor diagram",
368
+ }
369
+ if plot not in plot_texts:
370
+ return ""
371
+ caption = f"The {plot_texts.get(plot)}"
372
+ if source != "None":
373
+ caption += f" of {'the reference data' if source == 'Reference' else source}"
374
+ if region.lower() != "none":
375
+ caption += f" over the {ilr.Regions().get_name(region)} region."
376
+ return caption
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: climate-ref-ilamb
3
- Version: 0.6.5
3
+ Version: 0.6.6
4
4
  Summary: ILAMB diagnostic provider for the Rapid Evaluation Framework
5
5
  Author-email: Nathan Collier <nathaniel.collier@gmail.com>, Jared Lewis <jared.lewis@climate-resource.com>
6
6
  License-Expression: Apache-2.0
@@ -19,7 +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: ilamb3>=2025.5.20
22
+ Requires-Dist: ilamb3>=2025.9.9
23
23
  Requires-Dist: scipy<1.16
24
24
  Description-Content-Type: text/markdown
25
25
 
@@ -1,15 +1,15 @@
1
1
  climate_ref_ilamb/__init__.py,sha256=hMEkSjBY3yo-EbdMNOIvMSdGK14G2s5PERmWrBEtzFk,1414
2
2
  climate_ref_ilamb/datasets.py,sha256=MVCt1pxV5dIfYLm6huC0BZWP5stCamYNwXzc7kKW5AI,799
3
3
  climate_ref_ilamb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- climate_ref_ilamb/standard.py,sha256=rMI-GERTn_rg6qkp7jvzFZVAc7AZHkknEtNYFbo-Bak,10556
5
- climate_ref_ilamb/configure/ilamb.yaml,sha256=4lzZhtn4nq4hV0qjXq9mWlN2cqIkhh_ICtYlL4gtW3k,1194
6
- climate_ref_ilamb/configure/iomb.yaml,sha256=PxJAivXKNiYp-HbaYOJgUO8LnL9fmSztQKxLM_jFyvQ,944
4
+ climate_ref_ilamb/standard.py,sha256=oCuYakGtCvTREHa_bTgTeYrClunxMQiX7iloc8s6M88,14029
5
+ climate_ref_ilamb/configure/ilamb.yaml,sha256=keUmj7Oih-AepogB7PTwN56DTb0K0k_x1CkSbbhZjJ0,1195
6
+ climate_ref_ilamb/configure/iomb.yaml,sha256=EFacB193aMxM9-jCHSfq1hOeCjCDYMHbdZVDiBJYLfc,948
7
7
  climate_ref_ilamb/dataset_registry/ilamb.txt,sha256=_zqrq-Sa-0NTjPDFX6nQIeUalEc7tPrKr_CssOBlseg,1030
8
8
  climate_ref_ilamb/dataset_registry/iomb.txt,sha256=b95CUBYEGfeoPyRGx_E267c-2GF-E_lc4XeFkNSOJMo,375
9
9
  climate_ref_ilamb/dataset_registry/test.txt,sha256=gBjUJ6W-crghYqKN0QOFmjyqpMxKK50dU3SYTuIA6jM,206
10
- climate_ref_ilamb-0.6.5.dist-info/METADATA,sha256=OKlmtX3Tp7e5hb_tBdj_qOBl_qbH7PjlTBYQerZyS64,2343
11
- climate_ref_ilamb-0.6.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
- climate_ref_ilamb-0.6.5.dist-info/entry_points.txt,sha256=SnRhJk7KRiGd3jL4OMA2SId5p838T95kGcVrr3wtZAQ,59
13
- climate_ref_ilamb-0.6.5.dist-info/licenses/LICENCE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
14
- climate_ref_ilamb-0.6.5.dist-info/licenses/NOTICE,sha256=4qTlax9aX2-mswYJuVrLqJ9jK1IkN5kSBqfVvYLF3Ws,128
15
- climate_ref_ilamb-0.6.5.dist-info/RECORD,,
10
+ climate_ref_ilamb-0.6.6.dist-info/METADATA,sha256=hv06AZjVhr2w4aPUhKwUAmU5ssHtr9YkSrBWfECx5WA,2342
11
+ climate_ref_ilamb-0.6.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ climate_ref_ilamb-0.6.6.dist-info/entry_points.txt,sha256=SnRhJk7KRiGd3jL4OMA2SId5p838T95kGcVrr3wtZAQ,59
13
+ climate_ref_ilamb-0.6.6.dist-info/licenses/LICENCE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
14
+ climate_ref_ilamb-0.6.6.dist-info/licenses/NOTICE,sha256=4qTlax9aX2-mswYJuVrLqJ9jK1IkN5kSBqfVvYLF3Ws,128
15
+ climate_ref_ilamb-0.6.6.dist-info/RECORD,,