diff --git a/changelog/547.fix.md b/changelog/547.fix.md new file mode 100644 index 000000000..93cc1cf9b --- /dev/null +++ b/changelog/547.fix.md @@ -0,0 +1,2 @@ +Migrated to use a different post-processed JSON file for mapping CMIP6 compound names to CMIP7. +This fixes some errors when dealing with tasmax/tasmin and the removes the out_name attribute which was correctly included in [#530](https://github.com/Climate-REF/climate-ref/pull/530). diff --git a/packages/climate-ref-core/src/climate_ref_core/cmip6_to_cmip7.py b/packages/climate-ref-core/src/climate_ref_core/cmip6_to_cmip7.py index fe4363ef5..b6ac0274c 100644 --- a/packages/climate-ref-core/src/climate_ref_core/cmip6_to_cmip7.py +++ b/packages/climate-ref-core/src/climate_ref_core/cmip6_to_cmip7.py @@ -4,8 +4,8 @@ This module provides utilities to convert CMIP6 xarray datasets to CMIP7 format, following the CMIP7 Global Attributes V1.0 specification (DOI: 10.5281/zenodo.17250297). -Variable branding, realm, and out_name mappings are sourced from the CMIP7 Data Request -(DReq v1.2.2.3) via a bundled JSON subset. Regenerate with:: +Variable branding mappings are sourced from the CMIP7 Data Request (DReq v1.2.2.3) via a bundled JSON subset. +Regenerate with: python scripts/extract-data-request-mappings.py @@ -19,7 +19,7 @@ import json import re import uuid -from dataclasses import dataclass, field +from dataclasses import dataclass from datetime import datetime, timezone from importlib import resources from typing import TYPE_CHECKING, Any @@ -32,12 +32,39 @@ import xarray as xr +def suppress_bounds_coordinates(ds: xr.Dataset) -> xr.Dataset: + """ + Prevent xarray from adding spurious ``coordinates`` attributes to bounds variables. + + When a dataset contains scalar coordinates (e.g. ``height``), + xarray's CF encoder adds a ``coordinates`` attribute to *every* variable, + including bounds variables like ``time_bnds``. + Call this on a dataset before :meth:`xarray.Dataset.to_netcdf`. + + Parameters + ---------- + ds + The dataset about to be written. Modified in place. + + Returns + ------- + xr.Dataset + The same dataset, for chaining. + """ + for name in ds: + if str(name).endswith("_bnds"): + # Setting encoding["coordinates"] to None tells xarray's CF encoder + # to skip auto-adding a coordinates attribute for this variable. + ds[name].encoding["coordinates"] = None + return ds + + @attrs.frozen class DReqVariableMapping: """ A single CMIP6-to-CMIP7 variable mapping from the Data Request. - Each instance represents one row in the DReq Variables table, + Each instance represents one row in the DReq Variables JSON file, capturing the CMIP6 compound name, its CMIP7 equivalent, and the branding/realm metadata needed for format conversion. """ @@ -47,7 +74,6 @@ class DReqVariableMapping: cmip6_compound_name: str cmip7_compound_name: str branded_variable: str - out_name: str branding_suffix: str temporal_label: str vertical_label: str @@ -55,6 +81,7 @@ class DReqVariableMapping: area_label: str realm: str region: str + frequency: str def to_dict(self) -> dict[str, str]: """Serialise to a plain dict (for JSON output).""" @@ -82,7 +109,7 @@ def _load_dreq_mappings() -> dict[str, DReqVariableMapping]: return {key: DReqVariableMapping.from_dict(entry) for key, entry in variables.items()} -_DREQ_VARIABLES: dict[str, DReqVariableMapping] = _load_dreq_mappings() +_DREQ_VARIABLES: dict[str, DReqVariableMapping] = {} # CMIP6-only attributes that should be removed when converting to CMIP7 @@ -137,11 +164,16 @@ def get_dreq_entry(table_id: str, variable_id: str) -> DReqVariableMapping: KeyError If the compound name is not found in the Data Request mappings. """ - compound = f"{table_id}.{variable_id}" - entry = _DREQ_VARIABLES.get(compound) + cmip6_compound = f"{table_id}.{variable_id}" + + # Lazy load the DReq mappings on first access + if len(_DREQ_VARIABLES) == 0: + _DREQ_VARIABLES.update(_load_dreq_mappings()) + + entry = _DREQ_VARIABLES.get(cmip6_compound) if entry is None: raise KeyError( - f"Variable '{compound}' not found in Data Request mappings. " + f"Variable '{cmip6_compound}' not found in Data Request mappings. " f"Add it to INCLUDED_VARIABLES in scripts/extract-data-request-mappings.py and regenerate." ) return entry @@ -263,6 +295,37 @@ def get_realm(table_id: str, variable_id: str) -> str: return entry.realm +def parse_variant_label(variant_label: str) -> dict[str, int]: + """ + Parse a CMIP6 variant label into its component indexes. + + Parameters + ---------- + variant_label + Variant label string (e.g., "r1i1p1f1", "r3i2p4f5") + + Returns + ------- + dict + Dictionary with keys ``realization_index``, ``initialization_index``, + ``physics_index``, ``forcing_index`` mapped to their integer values. + + Raises + ------ + ValueError + If the variant label does not match the expected pattern. + """ + match = re.match(r"r(\d+)i(\d+)p(\d+)f(\d+)", variant_label) + if not match: + raise ValueError(f"Cannot parse variant label: {variant_label!r}") + return { + "realization_index": int(match.group(1)), + "initialization_index": int(match.group(2)), + "physics_index": int(match.group(3)), + "forcing_index": int(match.group(4)), + } + + def convert_variant_index(value: int | str, prefix: str) -> str: """ Convert CMIP6 numeric variant index to CMIP7 string format. @@ -273,7 +336,7 @@ def convert_variant_index(value: int | str, prefix: str) -> str: Parameters ---------- value - The index value (int or str) + The index value (int, numpy integer, or str) prefix The prefix to use ("r", "i", "p", or "f") @@ -282,9 +345,7 @@ def convert_variant_index(value: int | str, prefix: str) -> str: str The CMIP7 format index (e.g., "r1", "i1", "p1", "f1") """ - if isinstance(value, int): - return f"{prefix}{value}" - elif isinstance(value, str): + if isinstance(value, str): # Already has prefix if value.startswith(prefix): return value @@ -294,7 +355,10 @@ def convert_variant_index(value: int | str, prefix: str) -> str: except ValueError: return f"{prefix}{value}" - return f"{prefix}1" # type: ignore + try: + return f"{prefix}{int(value)}" + except (TypeError, ValueError): + return f"{prefix}1" @dataclass @@ -314,36 +378,9 @@ class CMIP7Metadata: product: str = "model-output" license_id: str = "CC-BY-4.0" - # Label attributes (derived from branding_suffix) - temporal_label: str = "tavg" - vertical_label: str = "u" - horizontal_label: str = "hxy" - area_label: str = "u" - - # Derived attributes - branding_suffix: str = field(init=False) - - def __post_init__(self) -> None: - self.branding_suffix = ( - f"{self.temporal_label}-{self.vertical_label}-{self.horizontal_label}-{self.area_label}" - ) - - @classmethod - def from_branding(cls, branding: BrandingSuffix, **kwargs: Any) -> CMIP7Metadata: - """Create metadata from a BrandingSuffix.""" - return cls( - temporal_label=branding.temporal_label, - vertical_label=branding.vertical_label, - horizontal_label=branding.horizontal_label, - area_label=branding.area_label, - **kwargs, - ) - def convert_cmip6_to_cmip7_attrs( cmip6_attrs: dict[str, Any], - variable_id: str | None = None, - branding: BrandingSuffix | None = None, ) -> dict[str, Any]: """ Convert CMIP6 global attributes to CMIP7 format. @@ -354,10 +391,6 @@ def convert_cmip6_to_cmip7_attrs( ---------- cmip6_attrs Dictionary of CMIP6 global attributes. Must contain ``table_id``. - variable_id - Variable ID for determining branding suffix. If not provided, read from attrs. - branding - Optional explicit branding suffix Returns ------- @@ -367,76 +400,78 @@ def convert_cmip6_to_cmip7_attrs( # Start with a copy of existing attributes attrs = dict(cmip6_attrs) - # Determine variable_id if not provided - if variable_id is None: - variable_id = attrs.get("variable_id", "unknown") + # Determine table_id and variable_id for branding lookup + variable_id = attrs["variable_id"] + table_id = attrs["table_id"] - table_id: str = attrs["table_id"] - - # Get branding suffix and out_name from DReq + # Look up the information from the data request for this variable dreq_entry = get_dreq_entry(table_id, variable_id) - if branding is None: - branding = BrandingSuffix( - temporal_label=dreq_entry.temporal_label, - vertical_label=dreq_entry.vertical_label, - horizontal_label=dreq_entry.horizontal_label, - area_label=dreq_entry.area_label, - ) - # Create CMIP7 metadata - cmip7_meta = CMIP7Metadata.from_branding(branding) + # Static CMIP7 metadata + cmip7_meta = CMIP7Metadata() + + # The variable id CMIP7 may differ from CMIP6 (tasmax -> tas) + attrs["variable_id"] = dreq_entry.variable_id # Update mip_era attrs["mip_era"] = cmip7_meta.mip_era attrs["parent_mip_era"] = attrs.get("parent_mip_era", "CMIP6") # New/updated CMIP7 attributes - attrs["variable_id"] = dreq_entry.branded_variable.split("_")[0] attrs["region"] = dreq_entry.region - attrs["drs_specs"] = cmip7_meta.drs_specs - attrs["data_specs_version"] = cmip7_meta.data_specs_version - attrs["product"] = cmip7_meta.product - attrs["license_id"] = cmip7_meta.license_id + attrs["realm"] = dreq_entry.realm + attrs["frequency"] = dreq_entry.frequency - # Add tracking_id with CMIP7 handle prefix - attrs["tracking_id"] = f"hdl:21.14107/{uuid.uuid4()}" - - # Add creation_date in ISO format - attrs["creation_date"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + attrs["drs_specs"] = CMIP7Metadata.drs_specs + attrs["data_specs_version"] = CMIP7Metadata.data_specs_version + attrs["product"] = CMIP7Metadata.product + attrs["license_id"] = CMIP7Metadata.license_id # Add label attributes - attrs["temporal_label"] = cmip7_meta.temporal_label - attrs["vertical_label"] = cmip7_meta.vertical_label - attrs["horizontal_label"] = cmip7_meta.horizontal_label - attrs["area_label"] = cmip7_meta.area_label - attrs["branding_suffix"] = cmip7_meta.branding_suffix + attrs["temporal_label"] = dreq_entry.temporal_label + attrs["vertical_label"] = dreq_entry.vertical_label + attrs["horizontal_label"] = dreq_entry.horizontal_label + attrs["area_label"] = dreq_entry.area_label + attrs["branding_suffix"] = dreq_entry.branding_suffix # Add branded_variable (required in CMIP7) - attrs["branded_variable"] = f"{dreq_entry.out_name}_{cmip7_meta.branding_suffix}" - - # Convert variant indices from CMIP6 integer to CMIP7 string format - if "realization_index" in attrs: - attrs["realization_index"] = convert_variant_index(attrs["realization_index"], "r") - if "initialization_index" in attrs: - attrs["initialization_index"] = convert_variant_index(attrs["initialization_index"], "i") - if "physics_index" in attrs: - attrs["physics_index"] = convert_variant_index(attrs["physics_index"], "p") - if "forcing_index" in attrs: - attrs["forcing_index"] = convert_variant_index(attrs["forcing_index"], "f") + attrs["branded_variable"] = dreq_entry.branded_variable + + # Convert variant indices from CMIP6 integer to CMIP7 string format. + # If individual indexes are missing, parse them from variant_label + parsed_indices: dict[str, int] = {} + variant_label = attrs.get("variant_label") + if variant_label: + try: + parsed_indices = parse_variant_label(variant_label) + except ValueError: + pass + + for index_name, prefix in [ + ("realization_index", "r"), + ("initialization_index", "i"), + ("physics_index", "p"), + ("forcing_index", "f"), + ]: + raw = attrs.get(index_name, parsed_indices.get(index_name, 1)) + attrs[index_name] = convert_variant_index(raw, prefix) # Rebuild variant_label from converted indices - r = attrs.get("realization_index", "r1") - i = attrs.get("initialization_index", "i1") - p = attrs.get("physics_index", "p1") - f = attrs.get("forcing_index", "f1") - attrs["variant_label"] = f"{r}{i}{p}{f}" - - # Set realm and frequency from DReq - attrs["realm"] = get_realm(table_id, variable_id) - if "frequency" not in attrs: - attrs["frequency"] = get_frequency_from_table(table_id) + attrs["variant_label"] = ( + f"{attrs['realization_index']}" + f"{attrs['initialization_index']}" + f"{attrs['physics_index']}" + f"{attrs['forcing_index']}" + ) + # Store legacy CMIP6 compound name for reference - attrs["cmip6_compound_name"] = f"{table_id}.{variable_id}" + attrs["cmip6_compound_name"] = dreq_entry.cmip6_compound_name + + # Add tracking_id with CMIP7 handle prefix + attrs["tracking_id"] = f"hdl:21.14107/{uuid.uuid4()}" + + # Add creation_date in ISO format + attrs["creation_date"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") # Update Conventions (CF version only, per CMIP7 spec) attrs["Conventions"] = "CF-1.12" @@ -491,11 +526,10 @@ def convert_cmip6_dataset( # Use the CMIP7 variable name dreq_entry = get_dreq_entry(table_id, variable_id) - ds = ds.rename({data_vars[0]: dreq_entry.out_name}) + ds = ds.rename({variable_id: dreq_entry.variable_id}) # Convert global attributes - branding = get_branding_suffix(table_id, variable_id) - ds.attrs = convert_cmip6_to_cmip7_attrs(ds.attrs, variable_id=variable_id, branding=branding) + ds.attrs = convert_cmip6_to_cmip7_attrs(ds.attrs) return ds @@ -577,18 +611,18 @@ def create_cmip7_filename( >>> create_cmip7_filename(attrs, "190001-190912") 'tas_tavg-h2m-hxy-u_mon_glb_g13s_CanESM6-MR_historical_r2i1p1f1_190001-190912.nc' """ - components = [ - attrs.get("variable_id", ""), - attrs.get("branding_suffix", ""), - attrs.get("frequency", "mon"), - attrs.get("region", "glb"), - attrs.get("grid_label", "gn"), - attrs.get("source_id", ""), - attrs.get("experiment_id", ""), - attrs.get("variant_label", ""), + filename_order = [ + "variable_id", + "branding_suffix", + "frequency", + "region", + "grid_label", + "source_id", + "experiment_id", + "variant_label", ] - filename = "_".join(str(c) for c in components) + filename = "_".join(str(attrs[c]) for c in filename_order) # Add time range if provided (omit for fixed/time-independent variables) if time_range: @@ -639,19 +673,21 @@ def create_cmip7_path(attrs: dict[str, Any], version: str | None = None) -> str: """ version_str = version or attrs.get("version", "v1") - components = [ - attrs.get("drs_specs", "MIP-DRS7"), - attrs.get("mip_era", "CMIP7"), - attrs.get("activity_id", "CMIP"), - attrs.get("institution_id", ""), - attrs.get("source_id", ""), - attrs.get("experiment_id", ""), - attrs.get("variant_label", ""), - attrs.get("region", "glb"), - attrs.get("frequency", "mon"), - attrs.get("variable_id", ""), - attrs.get("branding_suffix", ""), - attrs.get("grid_label", "gn"), - version_str, + drs_order = [ + "drs_specs", + "mip_era", + "activity_id", + "institution_id", + "source_id", + "experiment_id", + "variant_label", + "region", + "frequency", + "variable_id", + "branding_suffix", + "grid_label", ] + + components = [attrs[key] for key in drs_order] + components.append(version_str) # version goes at the end return "/".join(str(c) for c in components) diff --git a/packages/climate-ref-core/src/climate_ref_core/data/cmip6_cmip7_variable_map.json b/packages/climate-ref-core/src/climate_ref_core/data/cmip6_cmip7_variable_map.json index 7429fd765..16125a504 100644 --- a/packages/climate-ref-core/src/climate_ref_core/data/cmip6_cmip7_variable_map.json +++ b/packages/climate-ref-core/src/climate_ref_core/data/cmip6_cmip7_variable_map.json @@ -1,6 +1,6 @@ { "_metadata": { - "source": "https://github.com/CMIP-Data-Request/CMIP7_DReq_Content/raw/refs/tags/v1.2.2.3/airtable_export/dreq_release_export.json", + "source": "https://raw.githubusercontent.com/CMIP-Data-Request/CMIP7_DReq_Software/refs/heads/main/scripts/examples/variables_v1.2.2.3_cmip6names.json", "data_request_version": "v1.2.2.3", "description": "CMIP6-to-CMIP7 variable mappings extracted from the CMIP7 Data Request. Filtered to mon/fx tables and variables used by REF providers. Keyed by CMIP6 compound name (table_id.variable_id). Regenerate with: uv run python scripts/extract-data-request-mappings.py " }, @@ -11,14 +11,14 @@ "cmip6_compound_name": "AERmon.ua", "cmip7_compound_name": "atmos.ua.tavg-al-hxy-u.mon.glb", "branded_variable": "ua_tavg-al-hxy-u", - "out_name": "ua", "branding_suffix": "tavg-al-hxy-u", "temporal_label": "tavg", "vertical_label": "al", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "AERmon.va": { "table_id": "AERmon", @@ -26,14 +26,14 @@ "cmip6_compound_name": "AERmon.va", "cmip7_compound_name": "atmos.va.tavg-al-hxy-u.mon.glb", "branded_variable": "va_tavg-al-hxy-u", - "out_name": "va", "branding_suffix": "tavg-al-hxy-u", "temporal_label": "tavg", "vertical_label": "al", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "AERmon.zg": { "table_id": "AERmon", @@ -41,14 +41,14 @@ "cmip6_compound_name": "AERmon.zg", "cmip7_compound_name": "atmos.zg.tavg-al-hxy-u.mon.glb", "branded_variable": "zg_tavg-al-hxy-u", - "out_name": "zg", "branding_suffix": "tavg-al-hxy-u", "temporal_label": "tavg", "vertical_label": "al", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "AERmonZ.ta": { "table_id": "AERmonZ", @@ -56,14 +56,14 @@ "cmip6_compound_name": "AERmonZ.ta", "cmip7_compound_name": "atmos.ta.tavg-p39-hy-air.mon.glb", "branded_variable": "ta_tavg-p39-hy-air", - "out_name": "ta", "branding_suffix": "tavg-p39-hy-air", "temporal_label": "tavg", "vertical_label": "p39", "horizontal_label": "hy", "area_label": "air", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "AERmonZ.ua": { "table_id": "AERmonZ", @@ -71,14 +71,14 @@ "cmip6_compound_name": "AERmonZ.ua", "cmip7_compound_name": "atmos.ua.tavg-p39-hy-air.mon.glb", "branded_variable": "ua_tavg-p39-hy-air", - "out_name": "ua", "branding_suffix": "tavg-p39-hy-air", "temporal_label": "tavg", "vertical_label": "p39", "horizontal_label": "hy", "area_label": "air", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "AERmonZ.va": { "table_id": "AERmonZ", @@ -86,14 +86,14 @@ "cmip6_compound_name": "AERmonZ.va", "cmip7_compound_name": "atmos.va.tavg-p39-hy-air.mon.glb", "branded_variable": "va_tavg-p39-hy-air", - "out_name": "va", "branding_suffix": "tavg-p39-hy-air", "temporal_label": "tavg", "vertical_label": "p39", "horizontal_label": "hy", "area_label": "air", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "AERmonZ.zg": { "table_id": "AERmonZ", @@ -101,14 +101,14 @@ "cmip6_compound_name": "AERmonZ.zg", "cmip7_compound_name": "atmos.zg.tavg-p39-hy-air.mon.glb", "branded_variable": "zg_tavg-p39-hy-air", - "out_name": "zg", "branding_suffix": "tavg-p39-hy-air", "temporal_label": "tavg", "vertical_label": "p39", "horizontal_label": "hy", "area_label": "air", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Amon.cli": { "table_id": "Amon", @@ -116,14 +116,29 @@ "cmip6_compound_name": "Amon.cli", "cmip7_compound_name": "atmos.cli.tavg-al-hxy-u.mon.glb", "branded_variable": "cli_tavg-al-hxy-u", - "out_name": "cli", "branding_suffix": "tavg-al-hxy-u", "temporal_label": "tavg", "vertical_label": "al", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.cliSouth30": { + "table_id": "Amon", + "variable_id": "cli", + "cmip6_compound_name": "Amon.cliSouth30", + "cmip7_compound_name": "atmos.cli.tavg-al-hxy-u.mon.30S-90S", + "branded_variable": "cli_tavg-al-hxy-u", + "branding_suffix": "tavg-al-hxy-u", + "temporal_label": "tavg", + "vertical_label": "al", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.clivi": { "table_id": "Amon", @@ -131,14 +146,29 @@ "cmip6_compound_name": "Amon.clivi", "cmip7_compound_name": "atmos.clivi.tavg-u-hxy-u.mon.glb", "branded_variable": "clivi_tavg-u-hxy-u", - "out_name": "clivi", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.cliviSouth30": { + "table_id": "Amon", + "variable_id": "clivi", + "cmip6_compound_name": "Amon.cliviSouth30", + "cmip7_compound_name": "atmos.clivi.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "clivi_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.clt": { "table_id": "Amon", @@ -146,14 +176,29 @@ "cmip6_compound_name": "Amon.clt", "cmip7_compound_name": "atmos.clt.tavg-u-hxy-u.mon.glb", "branded_variable": "clt_tavg-u-hxy-u", - "out_name": "clt", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.cltSouth30": { + "table_id": "Amon", + "variable_id": "clt", + "cmip6_compound_name": "Amon.cltSouth30", + "cmip7_compound_name": "atmos.clt.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "clt_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.clwvi": { "table_id": "Amon", @@ -161,14 +206,29 @@ "cmip6_compound_name": "Amon.clwvi", "cmip7_compound_name": "atmos.clwvi.tavg-u-hxy-u.mon.glb", "branded_variable": "clwvi_tavg-u-hxy-u", - "out_name": "clwvi", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.clwviSouth30": { + "table_id": "Amon", + "variable_id": "clwvi", + "cmip6_compound_name": "Amon.clwviSouth30", + "cmip7_compound_name": "atmos.clwvi.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "clwvi_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.fco2antt": { "table_id": "Amon", @@ -176,14 +236,14 @@ "cmip6_compound_name": "Amon.fco2antt", "cmip7_compound_name": "atmos.fco2antt.tavg-u-hxy-u.mon.glb", "branded_variable": "fco2antt_tavg-u-hxy-u", - "out_name": "fco2antt", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Amon.hfls": { "table_id": "Amon", @@ -191,14 +251,29 @@ "cmip6_compound_name": "Amon.hfls", "cmip7_compound_name": "atmos.hfls.tavg-u-hxy-u.mon.glb", "branded_variable": "hfls_tavg-u-hxy-u", - "out_name": "hfls", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.hflsSouth30": { + "table_id": "Amon", + "variable_id": "hfls", + "cmip6_compound_name": "Amon.hflsSouth30", + "cmip7_compound_name": "atmos.hfls.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "hfls_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.hfss": { "table_id": "Amon", @@ -206,14 +281,29 @@ "cmip6_compound_name": "Amon.hfss", "cmip7_compound_name": "atmos.hfss.tavg-u-hxy-u.mon.glb", "branded_variable": "hfss_tavg-u-hxy-u", - "out_name": "hfss", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.hfssSouth30": { + "table_id": "Amon", + "variable_id": "hfss", + "cmip6_compound_name": "Amon.hfssSouth30", + "cmip7_compound_name": "atmos.hfss.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "hfss_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.hurs": { "table_id": "Amon", @@ -221,14 +311,29 @@ "cmip6_compound_name": "Amon.hurs", "cmip7_compound_name": "atmos.hurs.tavg-h2m-hxy-u.mon.glb", "branded_variable": "hurs_tavg-h2m-hxy-u", - "out_name": "hurs", "branding_suffix": "tavg-h2m-hxy-u", "temporal_label": "tavg", "vertical_label": "h2m", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.hursSouth30": { + "table_id": "Amon", + "variable_id": "hurs", + "cmip6_compound_name": "Amon.hursSouth30", + "cmip7_compound_name": "atmos.hurs.tavg-h2m-hxy-u.mon.30S-90S", + "branded_variable": "hurs_tavg-h2m-hxy-u", + "branding_suffix": "tavg-h2m-hxy-u", + "temporal_label": "tavg", + "vertical_label": "h2m", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.hus": { "table_id": "Amon", @@ -236,14 +341,29 @@ "cmip6_compound_name": "Amon.hus", "cmip7_compound_name": "atmos.hus.tavg-p19-hxy-u.mon.glb", "branded_variable": "hus_tavg-p19-hxy-u", - "out_name": "hus", "branding_suffix": "tavg-p19-hxy-u", "temporal_label": "tavg", "vertical_label": "p19", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.husSouth30": { + "table_id": "Amon", + "variable_id": "hus", + "cmip6_compound_name": "Amon.husSouth30", + "cmip7_compound_name": "atmos.hus.tavg-p19-hxy-u.mon.30S-90S", + "branded_variable": "hus_tavg-p19-hxy-u", + "branding_suffix": "tavg-p19-hxy-u", + "temporal_label": "tavg", + "vertical_label": "p19", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.pr": { "table_id": "Amon", @@ -251,14 +371,29 @@ "cmip6_compound_name": "Amon.pr", "cmip7_compound_name": "atmos.pr.tavg-u-hxy-u.mon.glb", "branded_variable": "pr_tavg-u-hxy-u", - "out_name": "pr", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.prSouth30": { + "table_id": "Amon", + "variable_id": "pr", + "cmip6_compound_name": "Amon.prSouth30", + "cmip7_compound_name": "atmos.pr.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "pr_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.psl": { "table_id": "Amon", @@ -266,14 +401,29 @@ "cmip6_compound_name": "Amon.psl", "cmip7_compound_name": "atmos.psl.tavg-u-hxy-u.mon.glb", "branded_variable": "psl_tavg-u-hxy-u", - "out_name": "psl", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.pslSouth30": { + "table_id": "Amon", + "variable_id": "psl", + "cmip6_compound_name": "Amon.pslSouth30", + "cmip7_compound_name": "atmos.psl.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "psl_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rlds": { "table_id": "Amon", @@ -281,14 +431,29 @@ "cmip6_compound_name": "Amon.rlds", "cmip7_compound_name": "atmos.rlds.tavg-u-hxy-u.mon.glb", "branded_variable": "rlds_tavg-u-hxy-u", - "out_name": "rlds", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rldsSouth30": { + "table_id": "Amon", + "variable_id": "rlds", + "cmip6_compound_name": "Amon.rldsSouth30", + "cmip7_compound_name": "atmos.rlds.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rlds_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rlus": { "table_id": "Amon", @@ -296,14 +461,29 @@ "cmip6_compound_name": "Amon.rlus", "cmip7_compound_name": "atmos.rlus.tavg-u-hxy-u.mon.glb", "branded_variable": "rlus_tavg-u-hxy-u", - "out_name": "rlus", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rlusSouth30": { + "table_id": "Amon", + "variable_id": "rlus", + "cmip6_compound_name": "Amon.rlusSouth30", + "cmip7_compound_name": "atmos.rlus.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rlus_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rlut": { "table_id": "Amon", @@ -311,14 +491,29 @@ "cmip6_compound_name": "Amon.rlut", "cmip7_compound_name": "atmos.rlut.tavg-u-hxy-u.mon.glb", "branded_variable": "rlut_tavg-u-hxy-u", - "out_name": "rlut", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rlutSouth30": { + "table_id": "Amon", + "variable_id": "rlut", + "cmip6_compound_name": "Amon.rlutSouth30", + "cmip7_compound_name": "atmos.rlut.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rlut_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rlutcs": { "table_id": "Amon", @@ -326,14 +521,29 @@ "cmip6_compound_name": "Amon.rlutcs", "cmip7_compound_name": "atmos.rlutcs.tavg-u-hxy-u.mon.glb", "branded_variable": "rlutcs_tavg-u-hxy-u", - "out_name": "rlutcs", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rlutcsSouth30": { + "table_id": "Amon", + "variable_id": "rlutcs", + "cmip6_compound_name": "Amon.rlutcsSouth30", + "cmip7_compound_name": "atmos.rlutcs.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rlutcs_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rsds": { "table_id": "Amon", @@ -341,14 +551,29 @@ "cmip6_compound_name": "Amon.rsds", "cmip7_compound_name": "atmos.rsds.tavg-u-hxy-u.mon.glb", "branded_variable": "rsds_tavg-u-hxy-u", - "out_name": "rsds", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rsdsSouth30": { + "table_id": "Amon", + "variable_id": "rsds", + "cmip6_compound_name": "Amon.rsdsSouth30", + "cmip7_compound_name": "atmos.rsds.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rsds_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rsdt": { "table_id": "Amon", @@ -356,14 +581,29 @@ "cmip6_compound_name": "Amon.rsdt", "cmip7_compound_name": "atmos.rsdt.tavg-u-hxy-u.mon.glb", "branded_variable": "rsdt_tavg-u-hxy-u", - "out_name": "rsdt", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rsdtSouth30": { + "table_id": "Amon", + "variable_id": "rsdt", + "cmip6_compound_name": "Amon.rsdtSouth30", + "cmip7_compound_name": "atmos.rsdt.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rsdt_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rsus": { "table_id": "Amon", @@ -371,14 +611,29 @@ "cmip6_compound_name": "Amon.rsus", "cmip7_compound_name": "atmos.rsus.tavg-u-hxy-u.mon.glb", "branded_variable": "rsus_tavg-u-hxy-u", - "out_name": "rsus", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rsusSouth30": { + "table_id": "Amon", + "variable_id": "rsus", + "cmip6_compound_name": "Amon.rsusSouth30", + "cmip7_compound_name": "atmos.rsus.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rsus_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rsut": { "table_id": "Amon", @@ -386,14 +641,29 @@ "cmip6_compound_name": "Amon.rsut", "cmip7_compound_name": "atmos.rsut.tavg-u-hxy-u.mon.glb", "branded_variable": "rsut_tavg-u-hxy-u", - "out_name": "rsut", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rsutSouth30": { + "table_id": "Amon", + "variable_id": "rsut", + "cmip6_compound_name": "Amon.rsutSouth30", + "cmip7_compound_name": "atmos.rsut.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rsut_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.rsutcs": { "table_id": "Amon", @@ -401,14 +671,29 @@ "cmip6_compound_name": "Amon.rsutcs", "cmip7_compound_name": "atmos.rsutcs.tavg-u-hxy-u.mon.glb", "branded_variable": "rsutcs_tavg-u-hxy-u", - "out_name": "rsutcs", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.rsutcsSouth30": { + "table_id": "Amon", + "variable_id": "rsutcs", + "cmip6_compound_name": "Amon.rsutcsSouth30", + "cmip7_compound_name": "atmos.rsutcs.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "rsutcs_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.ta": { "table_id": "Amon", @@ -416,14 +701,29 @@ "cmip6_compound_name": "Amon.ta", "cmip7_compound_name": "atmos.ta.tavg-p19-hxy-air.mon.glb", "branded_variable": "ta_tavg-p19-hxy-air", - "out_name": "ta", "branding_suffix": "tavg-p19-hxy-air", "temporal_label": "tavg", "vertical_label": "p19", "horizontal_label": "hxy", "area_label": "air", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.taSouth30": { + "table_id": "Amon", + "variable_id": "ta", + "cmip6_compound_name": "Amon.taSouth30", + "cmip7_compound_name": "atmos.ta.tavg-p19-hxy-air.mon.30S-90S", + "branded_variable": "ta_tavg-p19-hxy-air", + "branding_suffix": "tavg-p19-hxy-air", + "temporal_label": "tavg", + "vertical_label": "p19", + "horizontal_label": "hxy", + "area_label": "air", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.tas": { "table_id": "Amon", @@ -431,44 +731,89 @@ "cmip6_compound_name": "Amon.tas", "cmip7_compound_name": "atmos.tas.tavg-h2m-hxy-u.mon.glb", "branded_variable": "tas_tavg-h2m-hxy-u", - "out_name": "tas", "branding_suffix": "tavg-h2m-hxy-u", "temporal_label": "tavg", "vertical_label": "h2m", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.tasSouth30": { + "table_id": "Amon", + "variable_id": "tas", + "cmip6_compound_name": "Amon.tasSouth30", + "cmip7_compound_name": "atmos.tas.tavg-h2m-hxy-u.mon.30S-90S", + "branded_variable": "tas_tavg-h2m-hxy-u", + "branding_suffix": "tavg-h2m-hxy-u", + "temporal_label": "tavg", + "vertical_label": "h2m", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.tasmax": { "table_id": "Amon", - "variable_id": "tasmax", + "variable_id": "tas", "cmip6_compound_name": "Amon.tasmax", "cmip7_compound_name": "atmos.tas.tmaxavg-h2m-hxy-u.mon.glb", "branded_variable": "tas_tmaxavg-h2m-hxy-u", - "out_name": "tas", "branding_suffix": "tmaxavg-h2m-hxy-u", "temporal_label": "tmaxavg", "vertical_label": "h2m", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.tasmaxSouth30": { + "table_id": "Amon", + "variable_id": "tas", + "cmip6_compound_name": "Amon.tasmaxSouth30", + "cmip7_compound_name": "atmos.tas.tmaxavg-h2m-hxy-u.mon.30S-90S", + "branded_variable": "tas_tmaxavg-h2m-hxy-u", + "branding_suffix": "tmaxavg-h2m-hxy-u", + "temporal_label": "tmaxavg", + "vertical_label": "h2m", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.tasmin": { "table_id": "Amon", - "variable_id": "tasmin", + "variable_id": "tas", "cmip6_compound_name": "Amon.tasmin", "cmip7_compound_name": "atmos.tas.tminavg-h2m-hxy-u.mon.glb", "branded_variable": "tas_tminavg-h2m-hxy-u", - "out_name": "tas", "branding_suffix": "tminavg-h2m-hxy-u", "temporal_label": "tminavg", "vertical_label": "h2m", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.tasminSouth30": { + "table_id": "Amon", + "variable_id": "tas", + "cmip6_compound_name": "Amon.tasminSouth30", + "cmip7_compound_name": "atmos.tas.tminavg-h2m-hxy-u.mon.30S-90S", + "branded_variable": "tas_tminavg-h2m-hxy-u", + "branding_suffix": "tminavg-h2m-hxy-u", + "temporal_label": "tminavg", + "vertical_label": "h2m", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.tauu": { "table_id": "Amon", @@ -476,14 +821,14 @@ "cmip6_compound_name": "Amon.tauu", "cmip7_compound_name": "atmos.tauu.tavg-u-hxy-u.mon.glb", "branded_variable": "tauu_tavg-u-hxy-u", - "out_name": "tauu", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Amon.ts": { "table_id": "Amon", @@ -491,14 +836,29 @@ "cmip6_compound_name": "Amon.ts", "cmip7_compound_name": "atmos.ts.tavg-u-hxy-u.mon.glb", "branded_variable": "ts_tavg-u-hxy-u", - "out_name": "ts", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.tsSouth30": { + "table_id": "Amon", + "variable_id": "ts", + "cmip6_compound_name": "Amon.tsSouth30", + "cmip7_compound_name": "atmos.ts.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "ts_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.ua": { "table_id": "Amon", @@ -506,14 +866,29 @@ "cmip6_compound_name": "Amon.ua", "cmip7_compound_name": "atmos.ua.tavg-p19-hxy-air.mon.glb", "branded_variable": "ua_tavg-p19-hxy-air", - "out_name": "ua", "branding_suffix": "tavg-p19-hxy-air", "temporal_label": "tavg", "vertical_label": "p19", "horizontal_label": "hxy", "area_label": "air", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.uaSouth30": { + "table_id": "Amon", + "variable_id": "ua", + "cmip6_compound_name": "Amon.uaSouth30", + "cmip7_compound_name": "atmos.ua.tavg-p19-hxy-air.mon.30S-90S", + "branded_variable": "ua_tavg-p19-hxy-air", + "branding_suffix": "tavg-p19-hxy-air", + "temporal_label": "tavg", + "vertical_label": "p19", + "horizontal_label": "hxy", + "area_label": "air", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.uas": { "table_id": "Amon", @@ -521,14 +896,29 @@ "cmip6_compound_name": "Amon.uas", "cmip7_compound_name": "atmos.uas.tavg-h10m-hxy-u.mon.glb", "branded_variable": "uas_tavg-h10m-hxy-u", - "out_name": "uas", "branding_suffix": "tavg-h10m-hxy-u", "temporal_label": "tavg", "vertical_label": "h10m", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.uasSouth30": { + "table_id": "Amon", + "variable_id": "uas", + "cmip6_compound_name": "Amon.uasSouth30", + "cmip7_compound_name": "atmos.uas.tavg-h10m-hxy-u.mon.30S-90S", + "branded_variable": "uas_tavg-h10m-hxy-u", + "branding_suffix": "tavg-h10m-hxy-u", + "temporal_label": "tavg", + "vertical_label": "h10m", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.va": { "table_id": "Amon", @@ -536,14 +926,29 @@ "cmip6_compound_name": "Amon.va", "cmip7_compound_name": "atmos.va.tavg-p19-hxy-air.mon.glb", "branded_variable": "va_tavg-p19-hxy-air", - "out_name": "va", "branding_suffix": "tavg-p19-hxy-air", "temporal_label": "tavg", "vertical_label": "p19", "horizontal_label": "hxy", "area_label": "air", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.vaSouth30": { + "table_id": "Amon", + "variable_id": "va", + "cmip6_compound_name": "Amon.vaSouth30", + "cmip7_compound_name": "atmos.va.tavg-p19-hxy-air.mon.30S-90S", + "branded_variable": "va_tavg-p19-hxy-air", + "branding_suffix": "tavg-p19-hxy-air", + "temporal_label": "tavg", + "vertical_label": "p19", + "horizontal_label": "hxy", + "area_label": "air", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.vas": { "table_id": "Amon", @@ -551,14 +956,29 @@ "cmip6_compound_name": "Amon.vas", "cmip7_compound_name": "atmos.vas.tavg-h10m-hxy-u.mon.glb", "branded_variable": "vas_tavg-h10m-hxy-u", - "out_name": "vas", "branding_suffix": "tavg-h10m-hxy-u", "temporal_label": "tavg", "vertical_label": "h10m", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.vasSouth30": { + "table_id": "Amon", + "variable_id": "vas", + "cmip6_compound_name": "Amon.vasSouth30", + "cmip7_compound_name": "atmos.vas.tavg-h10m-hxy-u.mon.30S-90S", + "branded_variable": "vas_tavg-h10m-hxy-u", + "branding_suffix": "tavg-h10m-hxy-u", + "temporal_label": "tavg", + "vertical_label": "h10m", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Amon.zg": { "table_id": "Amon", @@ -566,14 +986,29 @@ "cmip6_compound_name": "Amon.zg", "cmip7_compound_name": "atmos.zg.tavg-p19-hxy-air.mon.glb", "branded_variable": "zg_tavg-p19-hxy-air", - "out_name": "zg", "branding_suffix": "tavg-p19-hxy-air", "temporal_label": "tavg", "vertical_label": "p19", "horizontal_label": "hxy", "area_label": "air", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Amon.zgSouth30": { + "table_id": "Amon", + "variable_id": "zg", + "cmip6_compound_name": "Amon.zgSouth30", + "cmip7_compound_name": "atmos.zg.tavg-p19-hxy-air.mon.30S-90S", + "branded_variable": "zg_tavg-p19-hxy-air", + "branding_suffix": "tavg-p19-hxy-air", + "temporal_label": "tavg", + "vertical_label": "p19", + "horizontal_label": "hxy", + "area_label": "air", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "CFmon.hus": { "table_id": "CFmon", @@ -581,14 +1016,29 @@ "cmip6_compound_name": "CFmon.hus", "cmip7_compound_name": "atmos.hus.tavg-al-hxy-u.mon.glb", "branded_variable": "hus_tavg-al-hxy-u", - "out_name": "hus", "branding_suffix": "tavg-al-hxy-u", "temporal_label": "tavg", "vertical_label": "al", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "CFmon.husSouth30": { + "table_id": "CFmon", + "variable_id": "hus", + "cmip6_compound_name": "CFmon.husSouth30", + "cmip7_compound_name": "atmos.hus.tavg-al-hxy-u.mon.30S-90S", + "branded_variable": "hus_tavg-al-hxy-u", + "branding_suffix": "tavg-al-hxy-u", + "temporal_label": "tavg", + "vertical_label": "al", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "CFmon.ta": { "table_id": "CFmon", @@ -596,14 +1046,29 @@ "cmip6_compound_name": "CFmon.ta", "cmip7_compound_name": "atmos.ta.tavg-al-hxy-u.mon.glb", "branded_variable": "ta_tavg-al-hxy-u", - "out_name": "ta", "branding_suffix": "tavg-al-hxy-u", "temporal_label": "tavg", "vertical_label": "al", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "CFmon.taSouth30": { + "table_id": "CFmon", + "variable_id": "ta", + "cmip6_compound_name": "CFmon.taSouth30", + "cmip7_compound_name": "atmos.ta.tavg-al-hxy-u.mon.30S-90S", + "branded_variable": "ta_tavg-al-hxy-u", + "branding_suffix": "tavg-al-hxy-u", + "temporal_label": "tavg", + "vertical_label": "al", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "30S-90S", + "frequency": "mon" }, "Emon.cSoil": { "table_id": "Emon", @@ -611,14 +1076,134 @@ "cmip6_compound_name": "Emon.cSoil", "cmip7_compound_name": "land.cSoil.tavg-u-hxy-lnd.mon.glb", "branded_variable": "cSoil_tavg-u-hxy-lnd", - "out_name": "cSoil", "branding_suffix": "tavg-u-hxy-lnd", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Emon.cSoilAbove1m": { + "table_id": "Emon", + "variable_id": "cSoil", + "cmip6_compound_name": "Emon.cSoilAbove1m", + "cmip7_compound_name": "land.cSoil.tavg-d100cm-hxy-lnd.mon.glb", + "branded_variable": "cSoil_tavg-d100cm-hxy-lnd", + "branding_suffix": "tavg-d100cm-hxy-lnd", + "temporal_label": "tavg", + "vertical_label": "d100cm", + "horizontal_label": "hxy", + "area_label": "lnd", + "realm": "land", + "region": "glb", + "frequency": "mon" + }, + "Emon.cSoilLevels": { + "table_id": "Emon", + "variable_id": "cSoil", + "cmip6_compound_name": "Emon.cSoilLevels", + "cmip7_compound_name": "land.cSoil.tavg-sl-hxy-lnd.mon.glb", + "branded_variable": "cSoil_tavg-sl-hxy-lnd", + "branding_suffix": "tavg-sl-hxy-lnd", + "temporal_label": "tavg", + "vertical_label": "sl", + "horizontal_label": "hxy", + "area_label": "lnd", + "realm": "land", + "region": "glb", + "frequency": "mon" + }, + "Emon.cVegGrass": { + "table_id": "Emon", + "variable_id": "cVeg", + "cmip6_compound_name": "Emon.cVegGrass", + "cmip7_compound_name": "land.cVeg.tavg-u-hxy-ng.mon.glb", + "branded_variable": "cVeg_tavg-u-hxy-ng", + "branding_suffix": "tavg-u-hxy-ng", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "ng", + "realm": "land", + "region": "glb", + "frequency": "mon" + }, + "Emon.cVegShrub": { + "table_id": "Emon", + "variable_id": "cVeg", + "cmip6_compound_name": "Emon.cVegShrub", + "cmip7_compound_name": "land.cVeg.tavg-u-hxy-shb.mon.glb", + "branded_variable": "cVeg_tavg-u-hxy-shb", + "branding_suffix": "tavg-u-hxy-shb", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "shb", + "realm": "land", + "region": "glb", + "frequency": "mon" + }, + "Emon.cVegTree": { + "table_id": "Emon", + "variable_id": "cVeg", + "cmip6_compound_name": "Emon.cVegTree", + "cmip7_compound_name": "land.cVeg.tavg-u-hxy-tree.mon.glb", + "branded_variable": "cVeg_tavg-u-hxy-tree", + "branding_suffix": "tavg-u-hxy-tree", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "tree", + "realm": "land", + "region": "glb", + "frequency": "mon" + }, + "Emon.gppGrass": { + "table_id": "Emon", + "variable_id": "gpp", + "cmip6_compound_name": "Emon.gppGrass", + "cmip7_compound_name": "land.gpp.tavg-u-hxy-ng.mon.glb", + "branded_variable": "gpp_tavg-u-hxy-ng", + "branding_suffix": "tavg-u-hxy-ng", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "ng", + "realm": "land", + "region": "glb", + "frequency": "mon" + }, + "Emon.gppShrub": { + "table_id": "Emon", + "variable_id": "gpp", + "cmip6_compound_name": "Emon.gppShrub", + "cmip7_compound_name": "land.gpp.tavg-u-hxy-shb.mon.glb", + "branded_variable": "gpp_tavg-u-hxy-shb", + "branding_suffix": "tavg-u-hxy-shb", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "shb", + "realm": "land", + "region": "glb", + "frequency": "mon" + }, + "Emon.gppTree": { + "table_id": "Emon", + "variable_id": "gpp", + "cmip6_compound_name": "Emon.gppTree", + "cmip7_compound_name": "land.gpp.tavg-u-hxy-tree.mon.glb", + "branded_variable": "gpp_tavg-u-hxy-tree", + "branding_suffix": "tavg-u-hxy-tree", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "tree", + "realm": "land", + "region": "glb", + "frequency": "mon" }, "Emon.mrsol": { "table_id": "Emon", @@ -626,14 +1211,29 @@ "cmip6_compound_name": "Emon.mrsol", "cmip7_compound_name": "land.mrsol.tavg-sl-hxy-lnd.mon.glb", "branded_variable": "mrsol_tavg-sl-hxy-lnd", - "out_name": "mrsol", "branding_suffix": "tavg-sl-hxy-lnd", "temporal_label": "tavg", "vertical_label": "sl", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Emon.prhmax": { + "table_id": "Emon", + "variable_id": "pr", + "cmip6_compound_name": "Emon.prhmax", + "cmip7_compound_name": "atmos.pr.tmax-u-hxy-u.mon.glb", + "branded_variable": "pr_tmax-u-hxy-u", + "branding_suffix": "tmax-u-hxy-u", + "temporal_label": "tmax", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "atmos", + "region": "glb", + "frequency": "mon" }, "Emon.rsds": { "table_id": "Emon", @@ -641,29 +1241,134 @@ "cmip6_compound_name": "Emon.rsds", "cmip7_compound_name": "land.rsds.tavg-u-hxy-lnd.mon.glb", "branded_variable": "rsds_tavg-u-hxy-lnd", - "out_name": "rsds", "branding_suffix": "tavg-u-hxy-lnd", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Emon.rsdsoni": { + "table_id": "Emon", + "variable_id": "rsds", + "cmip6_compound_name": "Emon.rsdsoni", + "cmip7_compound_name": "ocean.rsds.tavg-u-hxy-ifs.mon.glb", + "branded_variable": "rsds_tavg-u-hxy-ifs", + "branding_suffix": "tavg-u-hxy-ifs", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "ifs", + "realm": "ocean", + "region": "glb", + "frequency": "mon" + }, + "Emon.rsdss": { + "table_id": "Emon", + "variable_id": "rsds", + "cmip6_compound_name": "Emon.rsdss", + "cmip7_compound_name": "land.rsds.tavg-u-hxy-sn.mon.glb", + "branded_variable": "rsds_tavg-u-hxy-sn", + "branding_suffix": "tavg-u-hxy-sn", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "sn", + "realm": "land ocean", + "region": "glb", + "frequency": "mon" + }, + "Emon.rsus": { + "table_id": "Emon", + "variable_id": "rsus", + "cmip6_compound_name": "Emon.rsus", + "cmip7_compound_name": "land.rsus.tavg-u-hxy-lnd.mon.glb", + "branded_variable": "rsus_tavg-u-hxy-lnd", + "branding_suffix": "tavg-u-hxy-lnd", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "lnd", + "realm": "land", + "region": "glb", + "frequency": "mon" + }, + "Emon.rsusoni": { + "table_id": "Emon", + "variable_id": "rsus", + "cmip6_compound_name": "Emon.rsusoni", + "cmip7_compound_name": "ocean.rsus.tavg-u-hxy-ifs.mon.glb", + "branded_variable": "rsus_tavg-u-hxy-ifs", + "branding_suffix": "tavg-u-hxy-ifs", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "ifs", + "realm": "ocean", + "region": "glb", + "frequency": "mon" + }, + "Emon.rsuss": { + "table_id": "Emon", + "variable_id": "rsus", + "cmip6_compound_name": "Emon.rsuss", + "cmip7_compound_name": "land.rsus.tavg-u-hxy-sn.mon.glb", + "branded_variable": "rsus_tavg-u-hxy-sn", + "branding_suffix": "tavg-u-hxy-sn", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "sn", + "realm": "land ocean", + "region": "glb", + "frequency": "mon" + }, + "Emon.thetaot2000": { + "table_id": "Emon", + "variable_id": "thetao", + "cmip6_compound_name": "Emon.thetaot2000", + "cmip7_compound_name": "ocean.thetao.tavg-d2000m-hxy-sea.mon.glb", + "branded_variable": "thetao_tavg-d2000m-hxy-sea", + "branding_suffix": "tavg-d2000m-hxy-sea", + "temporal_label": "tavg", + "vertical_label": "d2000m", + "horizontal_label": "hxy", + "area_label": "sea", + "realm": "ocean", + "region": "glb", + "frequency": "mon" + }, + "Emon.thetaot300": { + "table_id": "Emon", + "variable_id": "thetao", + "cmip6_compound_name": "Emon.thetaot300", + "cmip7_compound_name": "ocean.thetao.tavg-d300m-hxy-sea.mon.glb", + "branded_variable": "thetao_tavg-d300m-hxy-sea", + "branding_suffix": "tavg-d300m-hxy-sea", + "temporal_label": "tavg", + "vertical_label": "d300m", + "horizontal_label": "hxy", + "area_label": "sea", + "realm": "ocean", + "region": "glb", + "frequency": "mon" }, - "Emon.rsus": { + "Emon.thetaot700": { "table_id": "Emon", - "variable_id": "rsus", - "cmip6_compound_name": "Emon.rsus", - "cmip7_compound_name": "land.rsus.tavg-u-hxy-lnd.mon.glb", - "branded_variable": "rsus_tavg-u-hxy-lnd", - "out_name": "rsus", - "branding_suffix": "tavg-u-hxy-lnd", + "variable_id": "thetao", + "cmip6_compound_name": "Emon.thetaot700", + "cmip7_compound_name": "ocean.thetao.tavg-d700m-hxy-sea.mon.glb", + "branded_variable": "thetao_tavg-d700m-hxy-sea", + "branding_suffix": "tavg-d700m-hxy-sea", "temporal_label": "tavg", - "vertical_label": "u", + "vertical_label": "d700m", "horizontal_label": "hxy", - "area_label": "lnd", - "realm": "land", - "region": "glb" + "area_label": "sea", + "realm": "ocean", + "region": "glb", + "frequency": "mon" }, "Emon.vegFrac": { "table_id": "Emon", @@ -671,14 +1376,14 @@ "cmip6_compound_name": "Emon.vegFrac", "cmip7_compound_name": "land.vegFrac.tavg-u-hxy-u.mon.glb", "branded_variable": "vegFrac_tavg-u-hxy-u", - "out_name": "vegFrac", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "ImonAnt.hfls": { "table_id": "ImonAnt", @@ -686,14 +1391,14 @@ "cmip6_compound_name": "ImonAnt.hfls", "cmip7_compound_name": "atmos.hfls.tavg-u-hxy-is.mon.ata", "branded_variable": "hfls_tavg-u-hxy-is", - "out_name": "hfls", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "ata" + "realm": "atmos land landIce", + "region": "ata", + "frequency": "mon" }, "ImonAnt.hfss": { "table_id": "ImonAnt", @@ -701,14 +1406,14 @@ "cmip6_compound_name": "ImonAnt.hfss", "cmip7_compound_name": "atmos.hfss.tavg-u-hxy-is.mon.ata", "branded_variable": "hfss_tavg-u-hxy-is", - "out_name": "hfss", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "ata" + "realm": "atmos land landIce", + "region": "ata", + "frequency": "mon" }, "ImonAnt.rlds": { "table_id": "ImonAnt", @@ -716,14 +1421,14 @@ "cmip6_compound_name": "ImonAnt.rlds", "cmip7_compound_name": "atmos.rlds.tavg-u-hxy-is.mon.ata", "branded_variable": "rlds_tavg-u-hxy-is", - "out_name": "rlds", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "ata" + "realm": "atmos land landIce", + "region": "ata", + "frequency": "mon" }, "ImonAnt.rlus": { "table_id": "ImonAnt", @@ -731,14 +1436,14 @@ "cmip6_compound_name": "ImonAnt.rlus", "cmip7_compound_name": "atmos.rlus.tavg-u-hxy-is.mon.ata", "branded_variable": "rlus_tavg-u-hxy-is", - "out_name": "rlus", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "ata" + "realm": "atmos land landIce", + "region": "ata", + "frequency": "mon" }, "ImonAnt.rsds": { "table_id": "ImonAnt", @@ -746,14 +1451,14 @@ "cmip6_compound_name": "ImonAnt.rsds", "cmip7_compound_name": "atmos.rsds.tavg-u-hxy-is.mon.ata", "branded_variable": "rsds_tavg-u-hxy-is", - "out_name": "rsds", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "ata" + "realm": "atmos land", + "region": "ata", + "frequency": "mon" }, "ImonAnt.rsus": { "table_id": "ImonAnt", @@ -761,14 +1466,14 @@ "cmip6_compound_name": "ImonAnt.rsus", "cmip7_compound_name": "atmos.rsus.tavg-u-hxy-is.mon.ata", "branded_variable": "rsus_tavg-u-hxy-is", - "out_name": "rsus", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "ata" + "realm": "atmos land landIce", + "region": "ata", + "frequency": "mon" }, "ImonAnt.snc": { "table_id": "ImonAnt", @@ -776,14 +1481,14 @@ "cmip6_compound_name": "ImonAnt.snc", "cmip7_compound_name": "landIce.snc.tavg-u-hxy-is.mon.ata", "branded_variable": "snc_tavg-u-hxy-is", - "out_name": "snc", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "landIce", - "region": "ata" + "realm": "landIce land", + "region": "ata", + "frequency": "mon" }, "ImonAnt.tas": { "table_id": "ImonAnt", @@ -791,14 +1496,14 @@ "cmip6_compound_name": "ImonAnt.tas", "cmip7_compound_name": "atmos.tas.tavg-h2m-hm-is.mon.ata", "branded_variable": "tas_tavg-h2m-hm-is", - "out_name": "tas", "branding_suffix": "tavg-h2m-hm-is", "temporal_label": "tavg", "vertical_label": "h2m", "horizontal_label": "hm", "area_label": "is", - "realm": "atmos", - "region": "ata" + "realm": "atmos land landIce", + "region": "ata", + "frequency": "mon" }, "ImonAnt.ts": { "table_id": "ImonAnt", @@ -806,14 +1511,14 @@ "cmip6_compound_name": "ImonAnt.ts", "cmip7_compound_name": "atmos.ts.tavg-u-hxy-is.mon.ata", "branded_variable": "ts_tavg-u-hxy-is", - "out_name": "ts", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "ata" + "realm": "atmos land landIce", + "region": "ata", + "frequency": "mon" }, "ImonGre.hfls": { "table_id": "ImonGre", @@ -821,14 +1526,14 @@ "cmip6_compound_name": "ImonGre.hfls", "cmip7_compound_name": "atmos.hfls.tavg-u-hxy-is.mon.grl", "branded_variable": "hfls_tavg-u-hxy-is", - "out_name": "hfls", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "grl" + "realm": "atmos land landIce", + "region": "grl", + "frequency": "mon" }, "ImonGre.hfss": { "table_id": "ImonGre", @@ -836,14 +1541,14 @@ "cmip6_compound_name": "ImonGre.hfss", "cmip7_compound_name": "atmos.hfss.tavg-u-hxy-is.mon.grl", "branded_variable": "hfss_tavg-u-hxy-is", - "out_name": "hfss", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "grl" + "realm": "atmos land landIce", + "region": "grl", + "frequency": "mon" }, "ImonGre.rlds": { "table_id": "ImonGre", @@ -851,14 +1556,14 @@ "cmip6_compound_name": "ImonGre.rlds", "cmip7_compound_name": "atmos.rlds.tavg-u-hxy-is.mon.grl", "branded_variable": "rlds_tavg-u-hxy-is", - "out_name": "rlds", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "grl" + "realm": "atmos land landIce", + "region": "grl", + "frequency": "mon" }, "ImonGre.rlus": { "table_id": "ImonGre", @@ -866,14 +1571,14 @@ "cmip6_compound_name": "ImonGre.rlus", "cmip7_compound_name": "atmos.rlus.tavg-u-hxy-is.mon.grl", "branded_variable": "rlus_tavg-u-hxy-is", - "out_name": "rlus", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "grl" + "realm": "atmos land landIce", + "region": "grl", + "frequency": "mon" }, "ImonGre.rsds": { "table_id": "ImonGre", @@ -881,14 +1586,14 @@ "cmip6_compound_name": "ImonGre.rsds", "cmip7_compound_name": "atmos.rsds.tavg-u-hxy-is.mon.grl", "branded_variable": "rsds_tavg-u-hxy-is", - "out_name": "rsds", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "grl" + "realm": "atmos land", + "region": "grl", + "frequency": "mon" }, "ImonGre.rsus": { "table_id": "ImonGre", @@ -896,14 +1601,14 @@ "cmip6_compound_name": "ImonGre.rsus", "cmip7_compound_name": "atmos.rsus.tavg-u-hxy-is.mon.grl", "branded_variable": "rsus_tavg-u-hxy-is", - "out_name": "rsus", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "grl" + "realm": "atmos land landIce", + "region": "grl", + "frequency": "mon" }, "ImonGre.snc": { "table_id": "ImonGre", @@ -911,14 +1616,14 @@ "cmip6_compound_name": "ImonGre.snc", "cmip7_compound_name": "landIce.snc.tavg-u-hxy-is.mon.grl", "branded_variable": "snc_tavg-u-hxy-is", - "out_name": "snc", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "landIce", - "region": "grl" + "realm": "landIce land", + "region": "grl", + "frequency": "mon" }, "ImonGre.tas": { "table_id": "ImonGre", @@ -926,14 +1631,14 @@ "cmip6_compound_name": "ImonGre.tas", "cmip7_compound_name": "atmos.tas.tavg-h2m-hm-is.mon.grl", "branded_variable": "tas_tavg-h2m-hm-is", - "out_name": "tas", "branding_suffix": "tavg-h2m-hm-is", "temporal_label": "tavg", "vertical_label": "h2m", "horizontal_label": "hm", "area_label": "is", - "realm": "atmos", - "region": "grl" + "realm": "atmos land landIce", + "region": "grl", + "frequency": "mon" }, "ImonGre.ts": { "table_id": "ImonGre", @@ -941,14 +1646,119 @@ "cmip6_compound_name": "ImonGre.ts", "cmip7_compound_name": "atmos.ts.tavg-u-hxy-is.mon.grl", "branded_variable": "ts_tavg-u-hxy-is", - "out_name": "ts", "branding_suffix": "tavg-u-hxy-is", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "is", - "realm": "atmos", - "region": "grl" + "realm": "atmos land landIce", + "region": "grl", + "frequency": "mon" + }, + "LImon.hflsIs": { + "table_id": "LImon", + "variable_id": "hfls", + "cmip6_compound_name": "LImon.hflsIs", + "cmip7_compound_name": "landIce.hfls.tavg-u-hxy-is.mon.glb", + "branded_variable": "hfls_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce", + "region": "glb", + "frequency": "mon" + }, + "LImon.hfssIs": { + "table_id": "LImon", + "variable_id": "hfss", + "cmip6_compound_name": "LImon.hfssIs", + "cmip7_compound_name": "landIce.hfss.tavg-u-hxy-is.mon.glb", + "branded_variable": "hfss_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce", + "region": "glb", + "frequency": "mon" + }, + "LImon.mrroIs": { + "table_id": "LImon", + "variable_id": "mrro", + "cmip6_compound_name": "LImon.mrroIs", + "cmip7_compound_name": "landIce.mrro.tavg-u-hxy-is.mon.glb", + "branded_variable": "mrro_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce", + "region": "glb", + "frequency": "mon" + }, + "LImon.rldsIs": { + "table_id": "LImon", + "variable_id": "rlds", + "cmip6_compound_name": "LImon.rldsIs", + "cmip7_compound_name": "landIce.rlds.tavg-u-hxy-is.mon.glb", + "branded_variable": "rlds_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce", + "region": "glb", + "frequency": "mon" + }, + "LImon.rlusIs": { + "table_id": "LImon", + "variable_id": "rlus", + "cmip6_compound_name": "LImon.rlusIs", + "cmip7_compound_name": "landIce.rlus.tavg-u-hxy-is.mon.glb", + "branded_variable": "rlus_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce", + "region": "glb", + "frequency": "mon" + }, + "LImon.rsdsIs": { + "table_id": "LImon", + "variable_id": "rsds", + "cmip6_compound_name": "LImon.rsdsIs", + "cmip7_compound_name": "landIce.rsds.tavg-u-hxy-is.mon.glb", + "branded_variable": "rsds_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce land", + "region": "glb", + "frequency": "mon" + }, + "LImon.rsusIs": { + "table_id": "LImon", + "variable_id": "rsus", + "cmip6_compound_name": "LImon.rsusIs", + "cmip7_compound_name": "landIce.rsus.tavg-u-hxy-is.mon.glb", + "branded_variable": "rsus_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce land", + "region": "glb", + "frequency": "mon" }, "LImon.snc": { "table_id": "LImon", @@ -956,14 +1766,59 @@ "cmip6_compound_name": "LImon.snc", "cmip7_compound_name": "landIce.snc.tavg-u-hxy-lnd.mon.glb", "branded_variable": "snc_tavg-u-hxy-lnd", - "out_name": "snc", "branding_suffix": "tavg-u-hxy-lnd", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "lnd", + "realm": "landIce land", + "region": "glb", + "frequency": "mon" + }, + "LImon.sncIs": { + "table_id": "LImon", + "variable_id": "snc", + "cmip6_compound_name": "LImon.sncIs", + "cmip7_compound_name": "landIce.snc.tavg-u-hxy-is.mon.glb", + "branded_variable": "snc_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce land", + "region": "glb", + "frequency": "mon" + }, + "LImon.tasIs": { + "table_id": "LImon", + "variable_id": "tas", + "cmip6_compound_name": "LImon.tasIs", + "cmip7_compound_name": "landIce.tas.tavg-h2m-hxy-is.mon.glb", + "branded_variable": "tas_tavg-h2m-hxy-is", + "branding_suffix": "tavg-h2m-hxy-is", + "temporal_label": "tavg", + "vertical_label": "h2m", + "horizontal_label": "hxy", + "area_label": "is", + "realm": "landIce", + "region": "glb", + "frequency": "mon" + }, + "LImon.tsIs": { + "table_id": "LImon", + "variable_id": "ts", + "cmip6_compound_name": "LImon.tsIs", + "cmip7_compound_name": "landIce.ts.tavg-u-hxy-is.mon.glb", + "branded_variable": "ts_tavg-u-hxy-is", + "branding_suffix": "tavg-u-hxy-is", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "is", "realm": "landIce", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Lmon.burntFractionAll": { "table_id": "Lmon", @@ -971,14 +1826,14 @@ "cmip6_compound_name": "Lmon.burntFractionAll", "cmip7_compound_name": "land.burntFractionAll.tavg-u-hxy-u.mon.glb", "branded_variable": "burntFractionAll_tavg-u-hxy-u", - "out_name": "burntFractionAll", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Lmon.cVeg": { "table_id": "Lmon", @@ -986,14 +1841,14 @@ "cmip6_compound_name": "Lmon.cVeg", "cmip7_compound_name": "land.cVeg.tavg-u-hxy-lnd.mon.glb", "branded_variable": "cVeg_tavg-u-hxy-lnd", - "out_name": "cVeg", "branding_suffix": "tavg-u-hxy-lnd", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Lmon.gpp": { "table_id": "Lmon", @@ -1001,14 +1856,14 @@ "cmip6_compound_name": "Lmon.gpp", "cmip7_compound_name": "land.gpp.tavg-u-hxy-lnd.mon.glb", "branded_variable": "gpp_tavg-u-hxy-lnd", - "out_name": "gpp", "branding_suffix": "tavg-u-hxy-lnd", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Lmon.lai": { "table_id": "Lmon", @@ -1016,14 +1871,14 @@ "cmip6_compound_name": "Lmon.lai", "cmip7_compound_name": "land.lai.tavg-u-hxy-lnd.mon.glb", "branded_variable": "lai_tavg-u-hxy-lnd", - "out_name": "lai", "branding_suffix": "tavg-u-hxy-lnd", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Lmon.mrro": { "table_id": "Lmon", @@ -1031,29 +1886,29 @@ "cmip6_compound_name": "Lmon.mrro", "cmip7_compound_name": "land.mrro.tavg-u-hxy-lnd.mon.glb", "branded_variable": "mrro_tavg-u-hxy-lnd", - "out_name": "mrro", "branding_suffix": "tavg-u-hxy-lnd", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Lmon.mrsos": { "table_id": "Lmon", - "variable_id": "mrsos", + "variable_id": "mrsol", "cmip6_compound_name": "Lmon.mrsos", "cmip7_compound_name": "land.mrsol.tavg-d10cm-hxy-lnd.mon.glb", "branded_variable": "mrsol_tavg-d10cm-hxy-lnd", - "out_name": "mrsol", "branding_suffix": "tavg-d10cm-hxy-lnd", "temporal_label": "tavg", "vertical_label": "d10cm", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Lmon.nbp": { "table_id": "Lmon", @@ -1061,14 +1916,14 @@ "cmip6_compound_name": "Lmon.nbp", "cmip7_compound_name": "land.nbp.tavg-u-hxy-lnd.mon.glb", "branded_variable": "nbp_tavg-u-hxy-lnd", - "out_name": "nbp", "branding_suffix": "tavg-u-hxy-lnd", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "lnd", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Lmon.treeFrac": { "table_id": "Lmon", @@ -1076,14 +1931,14 @@ "cmip6_compound_name": "Lmon.treeFrac", "cmip7_compound_name": "land.treeFrac.tavg-u-hxy-u.mon.glb", "branded_variable": "treeFrac_tavg-u-hxy-u", - "out_name": "treeFrac", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "land", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "Ofx.areacello": { "table_id": "Ofx", @@ -1091,14 +1946,14 @@ "cmip6_compound_name": "Ofx.areacello", "cmip7_compound_name": "ocean.areacello.ti-u-hxy-u.fx.glb", "branded_variable": "areacello_ti-u-hxy-u", - "out_name": "areacello", "branding_suffix": "ti-u-hxy-u", "temporal_label": "ti", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "ocean", - "region": "glb" + "region": "glb", + "frequency": "fx" }, "Ofx.sftof": { "table_id": "Ofx", @@ -1106,14 +1961,14 @@ "cmip6_compound_name": "Ofx.sftof", "cmip7_compound_name": "ocean.sftof.ti-u-hxy-u.fx.glb", "branded_variable": "sftof_ti-u-hxy-u", - "out_name": "sftof", "branding_suffix": "ti-u-hxy-u", "temporal_label": "ti", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "ocean", - "region": "glb" + "region": "glb", + "frequency": "fx" }, "Ofx.volcello": { "table_id": "Ofx", @@ -1121,44 +1976,59 @@ "cmip6_compound_name": "Ofx.volcello", "cmip7_compound_name": "ocean.volcello.ti-ol-hxy-sea.fx.glb", "branded_variable": "volcello_ti-ol-hxy-sea", - "out_name": "volcello", "branding_suffix": "ti-ol-hxy-sea", "temporal_label": "ti", "vertical_label": "ol", "horizontal_label": "hxy", "area_label": "sea", "realm": "ocean", - "region": "glb" + "region": "glb", + "frequency": "fx" }, - "Omon.msftmz": { + "Omon.so": { "table_id": "Omon", - "variable_id": "msftmz", - "cmip6_compound_name": "Omon.msftmz", - "cmip7_compound_name": "ocean.msftm.tavg-ol-hyb-sea.mon.glb", - "branded_variable": "msftm_tavg-ol-hyb-sea", - "out_name": "msftm", - "branding_suffix": "tavg-ol-hyb-sea", + "variable_id": "so", + "cmip6_compound_name": "Omon.so", + "cmip7_compound_name": "ocean.so.tavg-ol-hxy-sea.mon.glb", + "branded_variable": "so_tavg-ol-hxy-sea", + "branding_suffix": "tavg-ol-hxy-sea", "temporal_label": "tavg", "vertical_label": "ol", - "horizontal_label": "hyb", + "horizontal_label": "hxy", "area_label": "sea", "realm": "ocean", - "region": "glb" + "region": "glb", + "frequency": "mon" }, - "Omon.so": { + "Omon.soSouth30": { "table_id": "Omon", "variable_id": "so", - "cmip6_compound_name": "Omon.so", - "cmip7_compound_name": "ocean.so.tavg-ol-hxy-sea.mon.glb", + "cmip6_compound_name": "Omon.soSouth30", + "cmip7_compound_name": "ocean.so.tavg-ol-hxy-sea.mon.30S-90S", "branded_variable": "so_tavg-ol-hxy-sea", - "out_name": "so", "branding_suffix": "tavg-ol-hxy-sea", "temporal_label": "tavg", "vertical_label": "ol", "horizontal_label": "hxy", "area_label": "sea", "realm": "ocean", - "region": "glb" + "region": "30S-90S", + "frequency": "mon" + }, + "Omon.soga": { + "table_id": "Omon", + "variable_id": "so", + "cmip6_compound_name": "Omon.soga", + "cmip7_compound_name": "ocean.so.tavg-ol-hm-sea.mon.glb", + "branded_variable": "so_tavg-ol-hm-sea", + "branding_suffix": "tavg-ol-hm-sea", + "temporal_label": "tavg", + "vertical_label": "ol", + "horizontal_label": "hm", + "area_label": "sea", + "realm": "ocean", + "region": "glb", + "frequency": "mon" }, "Omon.sos": { "table_id": "Omon", @@ -1166,14 +2036,44 @@ "cmip6_compound_name": "Omon.sos", "cmip7_compound_name": "ocean.sos.tavg-u-hxy-sea.mon.glb", "branded_variable": "sos_tavg-u-hxy-sea", - "out_name": "sos", "branding_suffix": "tavg-u-hxy-sea", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "sea", "realm": "ocean", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Omon.sosga": { + "table_id": "Omon", + "variable_id": "sos", + "cmip6_compound_name": "Omon.sosga", + "cmip7_compound_name": "ocean.sos.tavg-u-hm-sea.mon.glb", + "branded_variable": "sos_tavg-u-hm-sea", + "branding_suffix": "tavg-u-hm-sea", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hm", + "area_label": "sea", + "realm": "ocean", + "region": "glb", + "frequency": "mon" + }, + "Omon.sosgaSouth30": { + "table_id": "Omon", + "variable_id": "sos", + "cmip6_compound_name": "Omon.sosgaSouth30", + "cmip7_compound_name": "ocean.sos.tavg-u-hm-sea.mon.30S-90S", + "branded_variable": "sos_tavg-u-hm-sea", + "branding_suffix": "tavg-u-hm-sea", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hm", + "area_label": "sea", + "realm": "ocean", + "region": "30S-90S", + "frequency": "mon" }, "Omon.thetao": { "table_id": "Omon", @@ -1181,14 +2081,44 @@ "cmip6_compound_name": "Omon.thetao", "cmip7_compound_name": "ocean.thetao.tavg-ol-hxy-sea.mon.glb", "branded_variable": "thetao_tavg-ol-hxy-sea", - "out_name": "thetao", "branding_suffix": "tavg-ol-hxy-sea", "temporal_label": "tavg", "vertical_label": "ol", "horizontal_label": "hxy", "area_label": "sea", "realm": "ocean", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Omon.thetaoSouth30": { + "table_id": "Omon", + "variable_id": "thetao", + "cmip6_compound_name": "Omon.thetaoSouth30", + "cmip7_compound_name": "ocean.thetao.tavg-ol-hxy-sea.mon.30S-90S", + "branded_variable": "thetao_tavg-ol-hxy-sea", + "branding_suffix": "tavg-ol-hxy-sea", + "temporal_label": "tavg", + "vertical_label": "ol", + "horizontal_label": "hxy", + "area_label": "sea", + "realm": "ocean", + "region": "30S-90S", + "frequency": "mon" + }, + "Omon.thetaoga": { + "table_id": "Omon", + "variable_id": "thetao", + "cmip6_compound_name": "Omon.thetaoga", + "cmip7_compound_name": "ocean.thetao.tavg-ol-hm-sea.mon.glb", + "branded_variable": "thetao_tavg-ol-hm-sea", + "branding_suffix": "tavg-ol-hm-sea", + "temporal_label": "tavg", + "vertical_label": "ol", + "horizontal_label": "hm", + "area_label": "sea", + "realm": "ocean", + "region": "glb", + "frequency": "mon" }, "Omon.tos": { "table_id": "Omon", @@ -1196,14 +2126,44 @@ "cmip6_compound_name": "Omon.tos", "cmip7_compound_name": "ocean.tos.tavg-u-hxy-sea.mon.glb", "branded_variable": "tos_tavg-u-hxy-sea", - "out_name": "tos", "branding_suffix": "tavg-u-hxy-sea", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "sea", "realm": "ocean", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "Omon.tosSouth30": { + "table_id": "Omon", + "variable_id": "tos", + "cmip6_compound_name": "Omon.tosSouth30", + "cmip7_compound_name": "ocean.tos.tavg-u-hxy-sea.mon.30S-90S", + "branded_variable": "tos_tavg-u-hxy-sea", + "branding_suffix": "tavg-u-hxy-sea", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "sea", + "realm": "ocean", + "region": "30S-90S", + "frequency": "mon" + }, + "Omon.tosga": { + "table_id": "Omon", + "variable_id": "tos", + "cmip6_compound_name": "Omon.tosga", + "cmip7_compound_name": "ocean.tos.tavg-u-hm-sea.mon.glb", + "branded_variable": "tos_tavg-u-hm-sea", + "branding_suffix": "tavg-u-hm-sea", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hm", + "area_label": "sea", + "realm": "ocean", + "region": "glb", + "frequency": "mon" }, "Omon.volcello": { "table_id": "Omon", @@ -1211,14 +2171,14 @@ "cmip6_compound_name": "Omon.volcello", "cmip7_compound_name": "ocean.volcello.tavg-ol-hxy-sea.mon.glb", "branded_variable": "volcello_tavg-ol-hxy-sea", - "out_name": "volcello", "branding_suffix": "tavg-ol-hxy-sea", "temporal_label": "tavg", "vertical_label": "ol", "horizontal_label": "hxy", "area_label": "sea", "realm": "ocean", - "region": "glb" + "region": "glb", + "frequency": "mon" }, "SImon.siconc": { "table_id": "SImon", @@ -1226,14 +2186,134 @@ "cmip6_compound_name": "SImon.siconc", "cmip7_compound_name": "seaIce.siconc.tavg-u-hxy-u.mon.glb", "branded_variable": "siconc_tavg-u-hxy-u", - "out_name": "siconc", "branding_suffix": "tavg-u-hxy-u", "temporal_label": "tavg", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "seaIce", - "region": "glb" + "region": "glb", + "frequency": "mon" + }, + "SImon.siconcSouth30": { + "table_id": "SImon", + "variable_id": "siconc", + "cmip6_compound_name": "SImon.siconcSouth30", + "cmip7_compound_name": "seaIce.siconc.tavg-u-hxy-u.mon.30S-90S", + "branded_variable": "siconc_tavg-u-hxy-u", + "branding_suffix": "tavg-u-hxy-u", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "u", + "realm": "seaIce", + "region": "30S-90S", + "frequency": "mon" + }, + "SImon.sifllwdtop": { + "table_id": "SImon", + "variable_id": "rlds", + "cmip6_compound_name": "SImon.sifllwdtop", + "cmip7_compound_name": "seaIce.rlds.tavg-u-hxy-si.mon.glb", + "branded_variable": "rlds_tavg-u-hxy-si", + "branding_suffix": "tavg-u-hxy-si", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "si", + "realm": "seaIce", + "region": "glb", + "frequency": "mon" + }, + "SImon.sifllwutop": { + "table_id": "SImon", + "variable_id": "rlus", + "cmip6_compound_name": "SImon.sifllwutop", + "cmip7_compound_name": "seaIce.rlus.tavg-u-hxy-si.mon.glb", + "branded_variable": "rlus_tavg-u-hxy-si", + "branding_suffix": "tavg-u-hxy-si", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "si", + "realm": "seaIce", + "region": "glb", + "frequency": "mon" + }, + "SImon.siflswdtop": { + "table_id": "SImon", + "variable_id": "rsds", + "cmip6_compound_name": "SImon.siflswdtop", + "cmip7_compound_name": "seaIce.rsds.tavg-u-hxy-si.mon.glb", + "branded_variable": "rsds_tavg-u-hxy-si", + "branding_suffix": "tavg-u-hxy-si", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "si", + "realm": "seaIce", + "region": "glb", + "frequency": "mon" + }, + "SImon.siflswutop": { + "table_id": "SImon", + "variable_id": "rsus", + "cmip6_compound_name": "SImon.siflswutop", + "cmip7_compound_name": "seaIce.rsus.tavg-u-hxy-si.mon.glb", + "branded_variable": "rsus_tavg-u-hxy-si", + "branding_suffix": "tavg-u-hxy-si", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "si", + "realm": "seaIce", + "region": "glb", + "frequency": "mon" + }, + "SImon.sisnconc": { + "table_id": "SImon", + "variable_id": "snc", + "cmip6_compound_name": "SImon.sisnconc", + "cmip7_compound_name": "seaIce.snc.tavg-u-hxy-si.mon.glb", + "branded_variable": "snc_tavg-u-hxy-si", + "branding_suffix": "tavg-u-hxy-si", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "si", + "realm": "seaIce", + "region": "glb", + "frequency": "mon" + }, + "SImon.sitemptop": { + "table_id": "SImon", + "variable_id": "ts", + "cmip6_compound_name": "SImon.sitemptop", + "cmip7_compound_name": "seaIce.ts.tavg-u-hxy-si.mon.glb", + "branded_variable": "ts_tavg-u-hxy-si", + "branding_suffix": "tavg-u-hxy-si", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "si", + "realm": "seaIce", + "region": "glb", + "frequency": "mon" + }, + "SImon.sitemptopSouth30": { + "table_id": "SImon", + "variable_id": "ts", + "cmip6_compound_name": "SImon.sitemptopSouth30", + "cmip7_compound_name": "seaIce.ts.tavg-u-hxy-si.mon.30S-90S", + "branded_variable": "ts_tavg-u-hxy-si", + "branding_suffix": "tavg-u-hxy-si", + "temporal_label": "tavg", + "vertical_label": "u", + "horizontal_label": "hxy", + "area_label": "si", + "realm": "seaIce", + "region": "30S-90S", + "frequency": "mon" }, "fx.areacella": { "table_id": "fx", @@ -1241,14 +2321,14 @@ "cmip6_compound_name": "fx.areacella", "cmip7_compound_name": "atmos.areacella.ti-u-hxy-u.fx.glb", "branded_variable": "areacella_ti-u-hxy-u", - "out_name": "areacella", "branding_suffix": "ti-u-hxy-u", "temporal_label": "ti", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", - "realm": "atmos", - "region": "glb" + "realm": "atmos land", + "region": "glb", + "frequency": "fx" }, "fx.sftlf": { "table_id": "fx", @@ -1256,14 +2336,14 @@ "cmip6_compound_name": "fx.sftlf", "cmip7_compound_name": "atmos.sftlf.ti-u-hxy-u.fx.glb", "branded_variable": "sftlf_ti-u-hxy-u", - "out_name": "sftlf", "branding_suffix": "ti-u-hxy-u", "temporal_label": "ti", "vertical_label": "u", "horizontal_label": "hxy", "area_label": "u", "realm": "atmos", - "region": "glb" + "region": "glb", + "frequency": "fx" } } } diff --git a/packages/climate-ref-core/src/climate_ref_core/esgf/cmip7.py b/packages/climate-ref-core/src/climate_ref_core/esgf/cmip7.py index 684ae10fa..dc3a8c569 100644 --- a/packages/climate-ref-core/src/climate_ref_core/esgf/cmip7.py +++ b/packages/climate-ref-core/src/climate_ref_core/esgf/cmip7.py @@ -18,8 +18,11 @@ from climate_ref_core.cmip6_to_cmip7 import ( convert_cmip6_dataset, create_cmip7_filename, + create_cmip7_path, format_cmip7_time_range, get_dreq_entry, + get_frequency_from_table, + suppress_bounds_coordinates, ) from climate_ref_core.esgf.cmip6 import CMIP6Request @@ -28,7 +31,7 @@ def _get_cmip7_cache_dir() -> Path: """Get the cache directory for converted CMIP7 files.""" # Use platform-appropriate cache directory for climate-ref # This avoids polluting the intake-esgf cache with converted files - cache_dir = Path(platformdirs.user_cache_dir("climate-ref")) / "cmip7-converted" + cache_dir = Path(platformdirs.user_cache_dir("climate_ref")) / "cmip7-converted" cache_dir.mkdir(parents=True, exist_ok=True) return cache_dir @@ -51,25 +54,21 @@ def _convert_file_to_cmip7(cmip6_path: Path, cmip7_facets: dict[str, Any]) -> Pa """ cache_dir = _get_cmip7_cache_dir() - # Build CMIP7 DRS path - # CMIP7 DRS: {activity_id}/{institution_id}/{source_id}/{experiment_id}/ - # {variant_label}/{frequency}/{variable_id}/{grid_label}/{version} - # Ensure all facet values are strings (some may be integers from metadata) # CMIP6 activity_id can contain multiple activities separated by spaces # (e.g. "C4MIP CDRMIP"). Use only the first activity for the DRS path. activity_id = str(cmip7_facets.get("activity_id", "CMIP")).split()[0] version = str(cmip7_facets.get("version", "v1")) - drs_path = cache_dir / Path( - activity_id, - str(cmip7_facets.get("institution_id", "unknown")), - str(cmip7_facets.get("source_id", "unknown")), - str(cmip7_facets.get("experiment_id", "historical")), - str(cmip7_facets.get("variant_label", "r1i1p1f1")), - str(cmip7_facets.get("frequency", "mon")), - str(cmip7_facets.get("variable_id", "tas")), - str(cmip7_facets.get("grid_label", "gn")), - version, - ) + + # Build CMIP7 DRS path using the standard MIP-DRS7 path builder. + # Provide defaults for fields that may not be in facets. + path_facets = { + "drs_specs": "MIP-DRS7", + "mip_era": "CMIP7", + "institution_id": "unknown", + **cmip7_facets, + "activity_id": activity_id, + } + drs_path = cache_dir / create_cmip7_path(path_facets, version) drs_path.mkdir(parents=True, exist_ok=True) @@ -94,6 +93,8 @@ def _convert_file_to_cmip7(cmip6_path: Path, cmip7_facets: dict[str, Any]) -> Pa ds_cmip7.attrs["activity_id"] = activity_id try: + logger.info(f"Writing translated CMIP7 file: {output_file}") + suppress_bounds_coordinates(ds_cmip7) ds_cmip7.to_netcdf(output_file) except PermissionError: # If we can't write but file exists (race condition or permission issue), use it @@ -172,9 +173,6 @@ def __init__( self.remove_ensembles = remove_ensembles self.time_span = time_span - # Store CMIP7 facets - self._cmip7_facets = dict(facets) - # Create corresponding CMIP6 facets self._cmip6_facets = self._convert_to_cmip6_facets(facets) @@ -191,7 +189,7 @@ def _convert_to_cmip6_facets(self, cmip7_facets: dict[str, Any]) -> dict[str, An return cmip6_facets def _convert_to_cmip7_metadata(self, cmip6_row: dict[str, Any]) -> dict[str, Any]: - """Convert CMIP6 metadata to CMIP7 format. + """Convert a subset of CMIP6 metadata to CMIP7 format. This is the single location for DReq enrichment: it updates ``variable_id`` and adds ``region``, ``branding_suffix``, and @@ -213,23 +211,16 @@ def _convert_to_cmip7_metadata(self, cmip6_row: dict[str, Any]) -> dict[str, Any # Map table_id to frequency if not present if "frequency" not in cmip7_row and "table_id" in cmip7_row: - table_to_freq = { - "Amon": "mon", - "day": "day", - "fx": "fx", - "Oyr": "yr", - "Omon": "mon", - } - cmip7_row["frequency"] = table_to_freq.get(cmip7_row["table_id"], "mon") - - # Enrich with DReq metadata (single canonical location) + cmip7_row["frequency"] = get_frequency_from_table(cmip7_row["table_id"]) + + # Enrich with DReq metadata table_id = cmip7_row.get("table_id") variable_id = cmip7_row.get("variable_id") if table_id and variable_id: try: entry = get_dreq_entry(table_id, variable_id) cmip7_row["region"] = entry.region - cmip7_row["variable_id"] = entry.branded_variable.split("_")[0] + cmip7_row["variable_id"] = entry.variable_id cmip7_row["branding_suffix"] = entry.branding_suffix cmip7_row["branded_variable"] = entry.branded_variable except KeyError: @@ -263,6 +254,8 @@ def fetch_datasets(self) -> pd.DataFrame: return cmip6_df # Convert each file and update metadata + # The returned DataFrame does not need to have the complete set of CMIP7 metadata + # Datasets will be re-read during DB ingestion during the tests converted_rows = [] for _, row in cmip6_df.iterrows(): row_dict: dict[str, Any] = {str(k): v for k, v in row.to_dict().items()} diff --git a/packages/climate-ref-core/tests/unit/esgf/test_cmip7.py b/packages/climate-ref-core/tests/unit/esgf/test_cmip7.py index c5e136b22..609921f70 100644 --- a/packages/climate-ref-core/tests/unit/esgf/test_cmip7.py +++ b/packages/climate-ref-core/tests/unit/esgf/test_cmip7.py @@ -215,7 +215,7 @@ def test_returns_path(self): def test_path_contains_climate_ref(self): """Test that path contains climate-ref identifier.""" result = _get_cmip7_cache_dir() - assert "climate-ref" in str(result) + assert "climate_ref" in str(result) assert "cmip7-converted" in str(result) @@ -254,14 +254,20 @@ def test_uses_cached_file(self, mock_cache_dir, mock_convert, mock_open, mock_ti "branding_suffix": "tavg-h2m-hxy-u", "region": "glb", } + # MIP-DRS7 path: drs_specs/mip_era/activity/institution/source/experiment/ + # variant/region/frequency/variable/branding/grid/version drs_path = cache_dir / Path( + "MIP-DRS7", + "CMIP7", "CMIP", "CSIRO", "ACCESS-ESM1-5", "historical", "r1i1p1f1", + "glb", "mon", "tas", + "tavg-h2m-hxy-u", "gn", "v1", ) @@ -347,7 +353,7 @@ def test_handles_integer_facet_values( mock_converted_ds = MagicMock() mock_convert.return_value = mock_converted_ds - # Use integer values for some facets; include table_id for DReq enrichment + # Use integer values for some facets; include all required fields cmip7_facets = { "activity_id": "CMIP", "institution_id": 123, # Integer value @@ -359,6 +365,8 @@ def test_handles_integer_facet_values( "table_id": "Amon", "grid_label": "gn", "version": 1, # Integer value + "branding_suffix": "tavg-h2m-hxy-u", + "region": "glb", } input_file = tmp_path / "test_input.nc" @@ -409,13 +417,17 @@ def test_handles_permission_error_with_existing_file( # Pre-create the output directory and file (simulating race condition) cmip7_fn = "tas_tavg-h2m-hxy-u_mon_glb_gn_ACCESS-ESM1-5_historical_r1i1p1f1.nc" drs_path = cache_dir / Path( + "MIP-DRS7", + "CMIP7", "CMIP", "CSIRO", "ACCESS-ESM1-5", "historical", "r1i1p1f1", + "glb", "mon", "tas", + "tavg-h2m-hxy-u", "gn", "v1", ) @@ -472,10 +484,8 @@ def test_handles_permission_error_without_existing_file( @patch("climate_ref_core.esgf.cmip7.xr.open_dataset") @patch("climate_ref_core.esgf.cmip7.convert_cmip6_dataset") @patch("climate_ref_core.esgf.cmip7._get_cmip7_cache_dir") - def test_uses_default_facet_values( - self, mock_cache_dir, mock_convert, mock_open, mock_time_range, tmp_path - ): - """Test that default facet values are used when facets are missing.""" + def test_raises_if_missing(self, mock_cache_dir, mock_convert, mock_open, mock_time_range, tmp_path): + """Test that empty facets raises KeyError from create_cmip7_path.""" cache_dir = tmp_path / "cache" cache_dir.mkdir() mock_cache_dir.return_value = cache_dir @@ -486,19 +496,14 @@ def test_uses_default_facet_values( mock_converted_ds = MagicMock() mock_convert.return_value = mock_converted_ds - # Empty facets - should use all defaults (no DReq enrichment without table_id/variable_id) + # Empty facets - create_cmip7_path will fail on missing required keys cmip7_facets: dict[str, str] = {} input_file = tmp_path / "test_input.nc" input_file.touch() - result = _convert_file_to_cmip7(input_file, cmip7_facets) - - # Check default DRS path structure - assert "CMIP" in str(result) # default activity_id - assert "unknown" in str(result) # default institution_id/source_id - assert "historical" in str(result) # default experiment_id - assert "r1i1p1f1" in str(result) # default variant_label + with pytest.raises(KeyError): + _convert_file_to_cmip7(input_file, cmip7_facets) class TestCMIP7RequestMetadataEdgeCases: diff --git a/packages/climate-ref-core/tests/unit/test_cmip6_to_cmip7.py b/packages/climate-ref-core/tests/unit/test_cmip6_to_cmip7.py index 13ed9cf16..6e824f63a 100644 --- a/packages/climate-ref-core/tests/unit/test_cmip6_to_cmip7.py +++ b/packages/climate-ref-core/tests/unit/test_cmip6_to_cmip7.py @@ -2,12 +2,12 @@ import attrs import cftime +import netCDF4 import numpy as np import pytest import xarray as xr from climate_ref_core.cmip6_to_cmip7 import ( - _DREQ_VARIABLES, CMIP6_ONLY_ATTRIBUTES, BrandingSuffix, DReqVariableMapping, @@ -19,30 +19,29 @@ format_cmip7_time_range, get_branding_suffix, get_cmip7_compound_name, + get_dreq_entry, get_frequency_from_table, get_realm, + parse_variant_label, + suppress_bounds_coordinates, ) class TestDReqDataLoading: """Test that DReq data is loaded correctly at import time.""" - def test_dreq_variables_loaded(self): - assert len(_DREQ_VARIABLES) > 30, f"Expected >30 DReq variables, got {len(_DREQ_VARIABLES)}" - def test_dreq_entry_is_dreq_variable_mapping(self): - entry = _DREQ_VARIABLES.get("Amon.tas") + entry = get_dreq_entry(table_id="Amon", variable_id="tas") assert entry is not None, "Amon.tas should be in DReq" assert isinstance(entry, DReqVariableMapping) def test_dreq_entry_has_required_fields(self): - entry = _DREQ_VARIABLES["Amon.tas"] + entry = get_dreq_entry(table_id="Amon", variable_id="tas") assert entry.table_id == "Amon" assert entry.variable_id == "tas" assert entry.cmip6_compound_name == "Amon.tas" assert entry.cmip7_compound_name != "" assert entry.branded_variable != "" - assert entry.out_name == "tas" assert entry.branding_suffix != "" assert entry.temporal_label != "" assert entry.vertical_label != "" @@ -63,7 +62,6 @@ def sample_mapping(self) -> DReqVariableMapping: cmip6_compound_name="Amon.tas", cmip7_compound_name="atmos.tas.tavg-h2m-hxy-u.mon.glb", branded_variable="tas_tavg-h2m-hxy-u", - out_name="tas", branding_suffix="tavg-h2m-hxy-u", temporal_label="tavg", vertical_label="h2m", @@ -71,6 +69,7 @@ def sample_mapping(self) -> DReqVariableMapping: area_label="u", realm="atmos", region="glb", + frequency="mon", ) def test_to_dict(self, sample_mapping: DReqVariableMapping): @@ -88,7 +87,6 @@ def test_from_dict(self): "cmip6_compound_name": "Omon.tos", "cmip7_compound_name": "ocean.tos.tavg-u-hxy-sea.mon.glb", "branded_variable": "tos_tavg-u-hxy-sea", - "out_name": "tos", "branding_suffix": "tavg-u-hxy-sea", "temporal_label": "tavg", "vertical_label": "u", @@ -96,6 +94,7 @@ def test_from_dict(self): "area_label": "sea", "realm": "ocean", "region": "glb", + "frequency": "mon", } mapping = DReqVariableMapping.from_dict(data) assert mapping.table_id == "Omon" @@ -327,6 +326,61 @@ def test_unknown_raises(self): get_realm("Amon", "totally_unknown_xyz") +class TestParseVariantLabel: + """Test parsing variant labels into component indexes.""" + + @pytest.mark.parametrize( + ("variant_label", "expected"), + [ + pytest.param( + "r1i1p1f1", + {"realization_index": 1, "initialization_index": 1, "physics_index": 1, "forcing_index": 1}, + id="all_ones", + ), + pytest.param( + "r3i2p4f5", + {"realization_index": 3, "initialization_index": 2, "physics_index": 4, "forcing_index": 5}, + id="all_non_one", + ), + pytest.param( + "r1i1p1f2", + {"realization_index": 1, "initialization_index": 1, "physics_index": 1, "forcing_index": 2}, + id="only_forcing_non_one", + ), + pytest.param( + "r10i1p1f1", + {"realization_index": 10, "initialization_index": 1, "physics_index": 1, "forcing_index": 1}, + id="large_realization", + ), + pytest.param( + "r1i1p2f1", + {"realization_index": 1, "initialization_index": 1, "physics_index": 2, "forcing_index": 1}, + id="only_physics_non_one", + ), + pytest.param( + "r6i3p1f2", + {"realization_index": 6, "initialization_index": 3, "physics_index": 1, "forcing_index": 2}, + id="mixed_non_one", + ), + ], + ) + def test_parse_variant_label(self, variant_label, expected): + result = parse_variant_label(variant_label) + assert result == expected + + def test_invalid_variant_label_raises(self): + with pytest.raises(ValueError, match="Cannot parse variant label"): + parse_variant_label("invalid") + + def test_empty_string_raises(self): + with pytest.raises(ValueError, match="Cannot parse variant label"): + parse_variant_label("") + + def test_partial_label_raises(self): + with pytest.raises(ValueError, match="Cannot parse variant label"): + parse_variant_label("r1i1p1") + + class TestConvertVariantIndex: """Test CMIP6 integer index to CMIP7 string format conversion.""" @@ -340,6 +394,8 @@ class TestConvertVariantIndex: pytest.param("1", "r", "r1", id="str_numeric"), pytest.param("r1", "r", "r1", id="str_with_prefix_r"), pytest.param("i2", "i", "i2", id="str_with_prefix_i"), + pytest.param(np.int32(3), "r", "r3", id="numpy_int32"), + pytest.param(np.int64(5), "f", "f5", id="numpy_int64"), ], ) def test_index_conversion(self, value, prefix, expected): @@ -455,6 +511,133 @@ def test_removes_cmip6_only_attributes(self): assert cmip7_attrs["grid_label"] == "gn" assert "variant_label" in cmip7_attrs + @pytest.mark.parametrize( + ("variant_label", "realization", "initialization", "physics", "forcing"), + [ + pytest.param("r1i1p1f2", "r1", "i1", "p1", "f2", id="forcing_2"), + pytest.param("r2i1p1f1", "r2", "i1", "p1", "f1", id="realization_2"), + pytest.param("r3i2p4f5", "r3", "i2", "p4", "f5", id="all_non_one"), + pytest.param("r10i1p1f1", "r10", "i1", "p1", "f1", id="large_realization"), + pytest.param("r1i1p2f1", "r1", "i1", "p2", "f1", id="physics_2"), + pytest.param("r6i3p1f2", "r6", "i3", "p1", "f2", id="mixed"), + ], + ) + def test_variant_label_preserved_when_individual_indexes_missing( + self, variant_label, realization, initialization, physics, forcing + ): + """When individual indexes are absent, variant_label must be parsed, not defaulted to 1.""" + cmip6_attrs = { + "variable_id": "tas", + "table_id": "Amon", + "variant_label": variant_label, + } + cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) + + assert cmip7_attrs["realization_index"] == realization + assert cmip7_attrs["initialization_index"] == initialization + assert cmip7_attrs["physics_index"] == physics + assert cmip7_attrs["forcing_index"] == forcing + assert cmip7_attrs["variant_label"] == f"{realization}{initialization}{physics}{forcing}" + + def test_individual_indexes_override_variant_label(self): + """When both individual indexes and variant_label are present, indexes take priority.""" + cmip6_attrs = { + "variable_id": "tas", + "table_id": "Amon", + "variant_label": "r1i1p1f1", + "realization_index": 3, + "initialization_index": 2, + "physics_index": 4, + "forcing_index": 5, + } + cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) + + assert cmip7_attrs["realization_index"] == "r3" + assert cmip7_attrs["initialization_index"] == "i2" + assert cmip7_attrs["physics_index"] == "p4" + assert cmip7_attrs["forcing_index"] == "f5" + assert cmip7_attrs["variant_label"] == "r3i2p4f5" + + def test_numpy_int_indexes(self): + """netCDF attributes often return numpy integers, not Python ints.""" + cmip6_attrs = { + "variable_id": "tas", + "table_id": "Amon", + "realization_index": np.int64(3), + "initialization_index": np.int32(2), + "physics_index": np.int64(1), + "forcing_index": np.int32(4), + } + cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) + + assert cmip7_attrs["realization_index"] == "r3" + assert cmip7_attrs["initialization_index"] == "i2" + assert cmip7_attrs["physics_index"] == "p1" + assert cmip7_attrs["forcing_index"] == "f4" + assert cmip7_attrs["variant_label"] == "r3i2p1f4" + + def test_no_variant_label_no_indexes_defaults_to_one(self): + """When neither variant_label nor individual indexes are present, default to 1.""" + cmip6_attrs = { + "variable_id": "tas", + "table_id": "Amon", + } + cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) + + assert cmip7_attrs["variant_label"] == "r1i1p1f1" + + def test_realistic_cmip6_attrs_non_default_forcing(self): + """Simulate a real CMIP6 file with forcing_index=2 (e.g., ACCESS-ESM1-5 r1i1p1f2).""" + cmip6_attrs = { + "Conventions": "CF-1.7 CMIP-6.2", + "activity_id": "ScenarioMIP", + "experiment_id": "ssp126", + "forcing_index": 2, + "frequency": "mon", + "grid_label": "gn", + "initialization_index": 1, + "institution_id": "CSIRO", + "mip_era": "CMIP6", + "physics_index": 1, + "realization_index": 1, + "source_id": "ACCESS-ESM1-5", + "table_id": "Amon", + "variable_id": "tas", + "variant_label": "r1i1p1f2", + "member_id": "r1i1p1f2", + } + cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) + + assert cmip7_attrs["forcing_index"] == "f2" + assert cmip7_attrs["variant_label"] == "r1i1p1f2" + assert cmip7_attrs["realization_index"] == "r1" + + def test_realistic_cmip6_attrs_multiple_non_default(self): + """Simulate a real CMIP6 file with multiple non-default indexes (e.g., r6i1p1f2).""" + cmip6_attrs = { + "Conventions": "CF-1.7 CMIP-6.2", + "activity_id": "CMIP", + "experiment_id": "historical", + "forcing_index": 2, + "frequency": "mon", + "grid_label": "gn", + "initialization_index": 1, + "institution_id": "MOHC", + "mip_era": "CMIP6", + "physics_index": 1, + "realization_index": 6, + "source_id": "UKESM1-0-LL", + "table_id": "Amon", + "variable_id": "tas", + "variant_label": "r6i1p1f2", + "member_id": "r6i1p1f2", + } + cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) + + assert cmip7_attrs["forcing_index"] == "f2" + assert cmip7_attrs["realization_index"] == "r6" + assert cmip7_attrs["variant_label"] == "r6i1p1f2" + def test_missing_table_id_raises(self): """table_id is required for conversion.""" with pytest.raises(KeyError): @@ -531,6 +714,55 @@ def test_uses_dreq_for_branding(self): assert ds_cmip7.attrs["vertical_label"] == "p19" assert ds_cmip7.attrs["branding_suffix"] == "tavg-p19-hxy-u" + def test_preserves_non_default_indexes_from_ds_attrs(self): + """Dataset with non-1 indexes must preserve them through conversion.""" + rng = np.random.default_rng(42) + ds = xr.Dataset( + {"tas": (["time", "lat", "lon"], rng.random((3, 3, 4)))}, + coords={"time": np.arange(3), "lat": np.linspace(-90, 90, 3), "lon": np.linspace(0, 360, 4)}, + attrs={ + "variable_id": "tas", + "table_id": "Amon", + "source_id": "UKESM1-0-LL", + "experiment_id": "historical", + "variant_label": "r6i1p1f2", + "realization_index": 6, + "initialization_index": 1, + "physics_index": 1, + "forcing_index": 2, + "institution_id": "MOHC", + "grid_label": "gn", + "Conventions": "CF-1.7 CMIP-6.2", + }, + ) + ds_cmip7 = convert_cmip6_dataset(ds) + + assert ds_cmip7.attrs["realization_index"] == "r6" + assert ds_cmip7.attrs["forcing_index"] == "f2" + assert ds_cmip7.attrs["variant_label"] == "r6i1p1f2" + + def test_variant_label_only_no_individual_indexes_in_ds(self): + """Dataset with only variant_label (no individual indexes) must still get correct variant.""" + rng = np.random.default_rng(42) + ds = xr.Dataset( + {"tas": (["time", "lat", "lon"], rng.random((3, 3, 4)))}, + coords={"time": np.arange(3), "lat": np.linspace(-90, 90, 3), "lon": np.linspace(0, 360, 4)}, + attrs={ + "variable_id": "tas", + "table_id": "Amon", + "source_id": "ACCESS-ESM1-5", + "experiment_id": "ssp126", + "variant_label": "r1i1p1f2", + "institution_id": "CSIRO", + "grid_label": "gn", + "Conventions": "CF-1.7 CMIP-6.2", + }, + ) + ds_cmip7 = convert_cmip6_dataset(ds) + + assert ds_cmip7.attrs["forcing_index"] == "f2" + assert ds_cmip7.attrs["variant_label"] == "r1i1p1f2" + class TestCreateCmip7Filename: """Test CMIP7 filename generation per MIP-DRS7 spec.""" @@ -567,136 +799,43 @@ def test_creates_valid_filename_without_time_range(self): expected = "areacella_ti-u-hxy-u_fx_glb_gn_ACCESS-ESM1-5_historical_r1i1p1f1.nc" assert filename == expected - def test_uses_defaults_for_missing_attributes(self): - attrs = { - "variable_id": "tas", - "branding_suffix": "tavg-h2m-hxy-u", - "source_id": "TestModel", - "experiment_id": "piControl", - "variant_label": "r1i1p1f1", - } - filename = create_cmip7_filename(attrs) - - assert filename == "tas_tavg-h2m-hxy-u_mon_glb_gn_TestModel_piControl_r1i1p1f1.nc" - - def test_falls_back_to_variable_id_without_out_name(self): - """Test that variable_id is used when out_name is absent.""" - attrs = { - "variable_id": "tas", - "branding_suffix": "tavg-h2m-hxy-u", - "frequency": "mon", - "region": "glb", - "grid_label": "gn", - "source_id": "ACCESS-ESM1-5", - "experiment_id": "historical", - "variant_label": "r1i1p1f1", - } - filename = create_cmip7_filename(attrs) - - assert filename == "tas_tavg-h2m-hxy-u_mon_glb_gn_ACCESS-ESM1-5_historical_r1i1p1f1.nc" - - -class TestCreateCmip7FilenameFromConversion: - """Test filename generation from full conversion pipeline (end-to-end).""" - - def test_tasmax_filename_uses_out_name(self): - """End-to-end: tasmax conversion produces filename with out_name=tas. - - CMIP6 tasmax maps to CMIP7 out_name=tas with branding tmaxavg-h2m-hxy-u. - The variable_id attribute stays as 'tasmax' but the filename uses 'tas'. - """ - cmip6_attrs = { - "variable_id": "tasmax", - "table_id": "Amon", - "source_id": "ACCESS-ESM1-5", - "experiment_id": "historical", - "realization_index": 1, - "initialization_index": 1, - "physics_index": 1, - "forcing_index": 1, - } - cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) - - # variable_id is updated - assert cmip7_attrs["variable_id"] == "tas" - # branded_variable uses updated variable_id - assert cmip7_attrs["branded_variable"] == "tas_tmaxavg-h2m-hxy-u" - - # Filename uses variable_id - filename = create_cmip7_filename(cmip7_attrs) - assert filename.startswith("tas_tmaxavg-h2m-hxy-u_") - assert "tasmax" not in filename - - def test_tas_filename_unchanged(self): - """End-to-end: tas conversion where out_name == variable_id.""" - cmip6_attrs = { - "variable_id": "tas", - "table_id": "Amon", - "source_id": "ACCESS-ESM1-5", - "experiment_id": "historical", - "realization_index": 1, - "initialization_index": 1, - "physics_index": 1, - "forcing_index": 1, - } - cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) - - assert cmip7_attrs["variable_id"] == "tas" - assert cmip7_attrs["branded_variable"] == "tas_tavg-h2m-hxy-u" - - filename = create_cmip7_filename(cmip7_attrs) - assert filename.startswith("tas_tavg-h2m-hxy-u_") - - -class TestDReqDrivenAttrsEdgeCases: - """Test DReq-driven attribute/filename logic for edge cases. - Covers variables where out_name != variable_id (e.g. tasmax -> tas) - and where region != 'glb' (e.g. ImonAnt.tas -> region='ata'). - """ - - def test_tasmax_attrs_have_distinct_out_name(self): - """Amon.tasmax: out_name='tas' differs from variable_id='tasmax'.""" +class TestCMIP7AttrsEdgeCases: + @pytest.mark.parametrize( + "cmip6_variable_id,branding_suffix", + [ + ("tasmax", "tmaxavg-h2m-hxy-u"), + ("tasmin", "tminavg-h2m-hxy-u"), + ], + ) + def test_tasmax_to_tas(self, cmip6_variable_id, branding_suffix): cmip6_attrs = { - "variable_id": "tasmax", + "variable_id": cmip6_variable_id, "table_id": "Amon", "source_id": "ACCESS-ESM1-5", "experiment_id": "historical", "variant_label": "r1i1p1f1", "grid_label": "gn", + "activity_id": "CMIP", + "institution_id": "CSIRO", } cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) assert cmip7_attrs["variable_id"] == "tas" - assert cmip7_attrs["branded_variable"] == "tas_tmaxavg-h2m-hxy-u" - assert cmip7_attrs["branding_suffix"] == "tmaxavg-h2m-hxy-u" + assert cmip7_attrs["branded_variable"] == f"tas_{branding_suffix}" + assert cmip7_attrs["branding_suffix"] == branding_suffix - # Filename must use variable_id + # Filename must use the new variable_id filename = create_cmip7_filename(cmip7_attrs) - assert filename.startswith("tas_tmaxavg-h2m-hxy-u_") - assert "tasmax" not in filename + assert filename.startswith(f"tas_{branding_suffix}_") + assert cmip6_variable_id not in filename # Path must use variable_id path = create_cmip7_path(cmip7_attrs) path_parts = path.split("/") # variable_id component is at position 9 in the DRS path assert "tas" in path_parts - assert "tasmax" not in path_parts - - def test_tasmin_attrs_have_distinct_out_name(self): - """Amon.tasmin: out_name='tas' differs from variable_id='tasmin'.""" - cmip6_attrs = { - "variable_id": "tasmin", - "table_id": "Amon", - "source_id": "ACCESS-ESM1-5", - "experiment_id": "historical", - "variant_label": "r1i1p1f1", - "grid_label": "gn", - } - cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) - - assert cmip7_attrs["variable_id"] == "tas" - assert cmip7_attrs["branded_variable"] == "tas_tminavg-h2m-hxy-u" + assert cmip6_variable_id not in path_parts def test_imonant_tas_has_non_glb_region(self): """ImonAnt.tas: region='ata' (Antarctic), not 'glb'.""" @@ -707,6 +846,8 @@ def test_imonant_tas_has_non_glb_region(self): "experiment_id": "historical", "variant_label": "r1i1p1f1", "grid_label": "gn", + "activity_id": "CMIP", + "institution_id": "CSIRO", } cmip7_attrs = convert_cmip6_to_cmip7_attrs(cmip6_attrs) @@ -766,22 +907,6 @@ def test_uses_version_from_attrs(self): assert path.endswith("/v20240101") - def test_uses_defaults_for_missing_attributes(self): - attrs = { - "institution_id": "TestInst", - "source_id": "TestModel", - "experiment_id": "piControl", - "variant_label": "r1i1p1f1", - "variable_id": "pr", - "branding_suffix": "tavg-u-hxy-u", - } - path = create_cmip7_path(attrs) - - assert path.startswith("MIP-DRS7/CMIP7/CMIP/") - assert "/glb/" in path - assert "/mon/" in path - assert path.endswith("/gn/v1") - class TestFormatCmip7TimeRange: """Test time range formatting per MIP-DRS7 spec.""" @@ -827,3 +952,82 @@ def test_single_timestep(self): result = format_cmip7_time_range(ds, "mon") assert result == "200006-200006" + + +class TestSuppressBoundsCoordinates: + """Test that suppress_bounds_coordinates prevents spurious coordinates on bounds variables.""" + + def test_sets_encoding_on_bnds_variables(self): + ds = xr.Dataset( + { + "tas": (["time", "lat", "lon"], np.zeros((3, 2, 4))), + "time_bnds": (["time", "bnds"], np.zeros((3, 2))), + "lat_bnds": (["lat", "bnds"], np.zeros((2, 2))), + "lon_bnds": (["lon", "bnds"], np.zeros((4, 2))), + }, + ) + suppress_bounds_coordinates(ds) + + assert ds["time_bnds"].encoding["coordinates"] is None + assert ds["lat_bnds"].encoding["coordinates"] is None + assert ds["lon_bnds"].encoding["coordinates"] is None + + def test_does_not_modify_non_bounds_variables(self): + ds = xr.Dataset( + { + "tas": (["time"], np.zeros(3)), + "time_bnds": (["time", "bnds"], np.zeros((3, 2))), + }, + ) + suppress_bounds_coordinates(ds) + + assert "coordinates" not in ds["tas"].encoding + + def test_no_op_when_no_bounds(self): + ds = xr.Dataset({"tas": (["time"], np.zeros(3))}) + suppress_bounds_coordinates(ds) + assert "coordinates" not in ds["tas"].encoding + + def test_returns_dataset_for_chaining(self): + ds = xr.Dataset({"tas": (["time"], np.zeros(3))}) + result = suppress_bounds_coordinates(ds) + assert result is ds + + def test_netcdf_roundtrip_no_spurious_coordinates_on_bounds(self, tmp_path): + """Verify that suppress_bounds_coordinates prevents xarray from adding + a ``coordinates`` attribute to bounds variables when a scalar coordinate + (e.g. ``height``) is present in the dataset.""" + time_vals = [cftime.DatetimeNoLeap(1850, m, 16) for m in range(1, 4)] + time_bnds_vals = np.array([[0, 1], [1, 2], [2, 3]], dtype=np.float64) + + ds = xr.Dataset( + { + "tas": (["time", "lat", "lon"], np.ones((3, 2, 4))), + "time_bnds": (["time", "bnds"], time_bnds_vals), + }, + coords={ + "time": time_vals, + "lat": [0.0, 1.0], + "lon": [0.0, 1.0, 2.0, 3.0], + "height": 2.0, + }, + ) + + out_path = tmp_path / "test_bounds.nc" + suppress_bounds_coordinates(ds) + ds.to_netcdf(out_path) + + # Re-read the raw file (no xarray decoding) to inspect attributes + + with netCDF4.Dataset(out_path) as nc: + time_bnds_var = nc.variables["time_bnds"] + bnds_attrs = {a: time_bnds_var.getncattr(a) for a in time_bnds_var.ncattrs()} + assert "coordinates" not in bnds_attrs, ( + f"time_bnds should not have a 'coordinates' attribute, got: {bnds_attrs}" + ) + + # The data variable *should* still reference height + tas_var = nc.variables["tas"] + tas_attrs = {a: tas_var.getncattr(a) for a in tas_var.ncattrs()} + assert "coordinates" in tas_attrs, "tas should have a 'coordinates' attribute" + assert "height" in tas_attrs["coordinates"] diff --git a/packages/climate-ref/src/climate_ref/datasets/cmip7.py b/packages/climate-ref/src/climate_ref/datasets/cmip7.py index 07e2b27b3..0aa892059 100644 --- a/packages/climate-ref/src/climate_ref/datasets/cmip7.py +++ b/packages/climate-ref/src/climate_ref/datasets/cmip7.py @@ -55,7 +55,6 @@ def parse_cmip7_file(file: str, **kwargs: Any) -> dict[str, Any]: "region": getattr(ds, "region", "glb"), "branding_suffix": getattr(ds, "branding_suffix", ""), "branded_variable": getattr(ds, "branded_variable", ""), - "out_name": getattr(ds, "out_name", ""), "version": getattr(ds, "version", ""), # Additional mandatory attributes "mip_era": getattr(ds, "mip_era", "CMIP7"), diff --git a/packages/climate-ref/src/climate_ref/models/dataset.py b/packages/climate-ref/src/climate_ref/models/dataset.py index c0493af24..d8bcd4fa0 100644 --- a/packages/climate-ref/src/climate_ref/models/dataset.py +++ b/packages/climate-ref/src/climate_ref/models/dataset.py @@ -328,9 +328,6 @@ class CMIP7Dataset(Dataset): instance_id: Mapped[str] = mapped_column(index=True) """CMIP7 DRS format unique identifier""" - # TODO: Review whether branded_variable should use the file's branded_variable - # attribute (which uses out_name from the Data Request) instead of variable_id. - # Currently out_name == variable_id for all known CMIP7 variables except for tasmin/tasmax @hybrid_property def branded_variable(self) -> str: """Return branded variable: ``{variable_id}_{branding_suffix}``.""" diff --git a/scripts/create-cmip7-datasets.py b/scripts/create-cmip7-datasets.py index b4255410f..41037e147 100755 --- a/scripts/create-cmip7-datasets.py +++ b/scripts/create-cmip7-datasets.py @@ -16,6 +16,7 @@ create_cmip7_filename, create_cmip7_path, format_cmip7_time_range, + suppress_bounds_coordinates, ) app = typer.Typer() @@ -61,6 +62,7 @@ def _convert_file( # Only write if file doesn't already exist if not cmip7_path.exists(): + suppress_bounds_coordinates(ds_cmip7) ds_cmip7.to_netcdf(cmip7_path) logger.debug(f"Wrote CMIP7 file: {cmip7_path}") else: diff --git a/scripts/extract-data-request-mappings.py b/scripts/extract-data-request-mappings.py index 6098de7d8..79290a894 100644 --- a/scripts/extract-data-request-mappings.py +++ b/scripts/extract-data-request-mappings.py @@ -1,7 +1,7 @@ """ -Extract CMIP6-to-CMIP7 variable mappings from the CMIP7 Data Request (DReq). +Extract CMIP6-to-CMIP7 variable mappings from a variable mapping file exported from the CMIP7 Data Request. -Downloads the DReq release export JSON from GitHub and extracts a subset +Downloads a JSON file from GitHub and extracts a subset containing only the variables used by Climate REF diagnostic providers. The output is written to the climate-ref-core package data directory. @@ -9,12 +9,40 @@ ----- uv run python scripts/extract-data-request-mappings.py -We are advised not to interact with the data request JSON directly, -but in this case it's simpler to filter and extract directly without depending on the DReq software. +The data request variable file contains entries like the following, +keyed by CMIP6 compound name (e.g. "Amon.tas"): + +"Amon.tasmax": { + "frequency": "mon", + "modeling_realm": "atmos", + "standard_name": "air_temperature", + "units": "K", + "cell_methods": "area: mean time: maximum within days time: mean over days", + "cell_measures": "area: areacella", + "long_name": "Daily Maximum Near-Surface Air Temperature", + "comment": "monthly mean of the daily-maximum near-surface air temperature.", + "processing_note": "...", + "dimensions": "longitude latitude time4 height2m", + "out_name": "tas", + "type": "real", + "positive": "", + "spatial_shape": "XY-na", + "temporal_shape": "monthly-mean-daily-stat", + "cmip6_table": "Amon", + "physical_parameter_name": "tasmax", + "variableRootDD": "tas", + "branding_label": "tmaxavg-h2m-hxy-u", + "branded_variable_name": "tas_tmaxavg-h2m-hxy-u", + "region": "glb", + "cmip6_compound_name": "Amon.tasmax", + "cmip7_compound_name": "atmos.tas.tmaxavg-h2m-hxy-u.mon.glb", + "uid": "bab942a8-e5dd-11e5-8482-ac72891c3257" +}, References ---------- -- CMIP7 Data Request Content: https://github.com/CMIP-Data-Request/CMIP7_DReq_Content + - CMIP7 Data Request Software: https://github.com/CMIP-Data-Request/CMIP7_DReq_Software + """ from __future__ import annotations @@ -31,7 +59,7 @@ app = typer.Typer(help="Extract CMIP6-to-CMIP7 variable mappings from the CMIP7 Data Request.") DATA_REQUEST_VERSION = "v1.2.2.3" -DATA_REQUEST_URL = f"https://github.com/CMIP-Data-Request/CMIP7_DReq_Content/raw/refs/tags/{DATA_REQUEST_VERSION}/airtable_export/dreq_release_export.json" +DATA_REQUEST_URL = f"https://raw.githubusercontent.com/CMIP-Data-Request/CMIP7_DReq_Software/refs/heads/main/scripts/examples/variables_{DATA_REQUEST_VERSION}_cmip6names.json" DEFAULT_OUTPUT_PATH = ( pathlib.Path(__file__).resolve().parent.parent @@ -118,10 +146,7 @@ def download_dreq() -> dict: _download_timeout = 60 with urllib.request.urlopen(DATA_REQUEST_URL, timeout=_download_timeout) as resp: # noqa: S310 data = json.loads(resp.read()) - # The top-level key is the version string - version_key = next(iter(data.keys())) - typer.echo(f"Version: {typer.style(version_key, fg=typer.colors.CYAN, bold=True)}") - return data[version_key] + return data def extract_mappings(dreq: dict) -> dict[str, DReqVariableMapping]: @@ -131,30 +156,16 @@ def extract_mappings(dreq: dict) -> dict[str, DReqVariableMapping]: Returns a dict keyed by CMIP6 compound name (e.g. ``"Amon.tas"``). Raises on duplicate compound names. """ - variables = dreq["Variables"]["records"] + variables = dreq["Compound Name"] mappings: dict[str, DReqVariableMapping] = {} for _rec_id, rec in variables.items(): - cmip6_cn = rec.get("CMIP6 Compound Name", "") - if not cmip6_cn or "." not in cmip6_cn: - continue - - branded = rec.get("Branded Variable Name", "") - cmip7_cn = rec.get("CMIP7 Compound Name", "") - region = rec.get("Region", "glb") - - # Parse branded variable name -> out_name + branding_suffix - parts = branded.split("_", 1) - out_name = parts[0] if parts else "" - branding_suffix = parts[1] if len(parts) > 1 else "" + cmip6_cn = rec["cmip6_compound_name"] + branded = rec["branded_variable_name"] + branding_suffix = rec["branding_label"] # Parse and validate branding suffix components (format: temporal-vertical-horizontal-area) - if not branding_suffix: - raise ValueError( - f"Missing branding suffix for record {_rec_id!r} " - f"(CMIP6 Compound Name: {cmip6_cn!r}, Branded Variable Name: {branded!r})" - ) suffix_parts = branding_suffix.split("-") _expected_suffix_parts = 4 if len(suffix_parts) != _expected_suffix_parts or any(not p for p in suffix_parts): @@ -167,27 +178,22 @@ def extract_mappings(dreq: dict) -> dict[str, DReqVariableMapping]: ) temporal_label, vertical_label, horizontal_label, area_label = suffix_parts - # Resolve realm from CMIP7 compound name (first component) - cmip7_parts = cmip7_cn.split(".") - realm = cmip7_parts[0] if cmip7_parts else "" - - # Parse CMIP6 compound name - table_id, variable_id = cmip6_cn.split(".", 1) + table_id, _cmip6_variable_id = cmip6_cn.split(".", 1) entry = DReqVariableMapping( table_id=table_id, - variable_id=variable_id, + variable_id=rec["variableRootDD"], cmip6_compound_name=cmip6_cn, - cmip7_compound_name=cmip7_cn, + cmip7_compound_name=rec["cmip7_compound_name"], branded_variable=branded, - out_name=out_name, branding_suffix=branding_suffix, temporal_label=temporal_label, vertical_label=vertical_label, horizontal_label=horizontal_label, area_label=area_label, - realm=realm, - region=region, + realm=rec["modeling_realm"], + region=rec["region"], + frequency=rec["frequency"], ) # Check if a duplicate has been seen @@ -220,7 +226,7 @@ def print_summary(mappings: dict[str, DReqVariableMapping]) -> None: by_variable_id[v.variable_id].append(cn) all_overlaps = {vid: cns for vid, cns in by_variable_id.items() if len(cns) > 1} - detail_fields = ("out_name", "branding_suffix", "realm", "region") + detail_fields = ("variable_id", "branding_suffix", "realm", "region") # Only show overlaps where at least one field varies across tables varying_overlaps = {}