Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions checks/time_checks/time_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# resolves them to use_midpoint=False without needing a Pt suffix.
AVERAGE_CORRECTION_FREQ = [
"day", "mon", "monPt", "yr", "yrPt", "1hrCM", "sem",
"1hr", "3hr", "6hr", "dec",
]


Expand Down
40 changes: 39 additions & 1 deletion plugins/cmip7/cmip7.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ def _load_toml(path: str) -> dict:
return toml.load(f)


def _is_flag_variable(ds, var_name):
"""Return True iff ``var_name`` carries CF flag semantics
(``flag_values`` or ``flag_meanings``). CMIP7 region-selector
variables like ``basin`` and ``siline`` are integer flag variables
per CF §7.5 and CMIP7 data descriptor ``type: integer``. The default
geophysical_variable.toml rules assume continuous float fields
(float ``_FillValue = 1e20`` and float type check) and cannot
describe the flag case.
"""
try:
attrs = ds.variables[var_name].ncattrs()
except Exception:
return False
return "flag_values" in attrs or "flag_meanings" in attrs


class Cmip7ProjectCheck(WCRPBaseCheck):
_cc_spec = "wcrp_cmip7"
_cc_spec_version = "1.0"
Expand Down Expand Up @@ -254,6 +270,19 @@ def _get_geo_var(
ctx = TestCtx(severity, "Geophysical Variable Detection")

if len(geo_vars) == 0:
# CF detection returns zero candidates for files whose data
# variable carries flag_meanings (compliance-checker's
# is_geophysical heuristic treats those as QC flags). CMIP7
# region-selector fx files (basin, siline, ...) are flag-valued
# by spec but ARE the geophysical variable of the file. Fall
# back to the global variable_id attribute, which CMIP7 defines
# as the single geophysical variable of the file, when the
# named variable exists in the dataset.
vid = getattr(ds, "variable_id", None)
vid = str(vid) if vid else None
if vid and vid in ds.variables:
self._geo_var_cache = vid
return vid, res
ctx.add_failure("No geophysical variable detected in the file.")
res.append(ctx.to_result())
return None, res
Expand Down Expand Up @@ -519,14 +548,21 @@ def check_Geophysical_Variable(self, ds):
return res

vcfg = self.config.variable
# CF flag-valued variables (basin, siline, similar CMIP7 region
# selectors) are integer by construction. The default TOML rules
# (float type, _FillValue = 1e20, missing_value = 1e20) do not
# fit them. Gate the float type check and the two fill/missing
# attribute rules on ``is_flag``; everything else still applies.
# See #59.
is_flag = _is_flag_variable(ds, geo)

# existence
if vcfg.existence:
sev = self.get_severity(vcfg.existence.severity)
res.extend(check_variable_existence(ds, geo, sev))

# type
if vcfg.type:
if vcfg.type and not is_flag:
sev = self.get_severity(vcfg.type.severity)
dt = (vcfg.type.data_type or "").lower()
allowed = ["f"] if dt in {"float", "double", "real"} else None
Expand Down Expand Up @@ -559,6 +595,8 @@ def check_Geophysical_Variable(self, ds):
for attr_key, rule in vcfg.attributes.items():
sev = self.get_severity(rule.severity)
name_in_file = rule.attribute_name or attr_key
if is_flag and name_in_file in ("_FillValue", "missing_value"):
continue
res.extend(
check_attribute_suite(
ds=ds,
Expand Down