Skip to content

Commit

Permalink
Merge pull request #1003 from benjwadams/add_time_coordinate_calendar…
Browse files Browse the repository at this point in the history
…_check

[CF 1.9] Ensure time coordinate variables have a calendar attribute s…
  • Loading branch information
benjwadams authored Apr 21, 2023
2 parents eb4593c + 13b9c34 commit a745fc7
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 1 deletion.
2 changes: 1 addition & 1 deletion compliance_checker/cf/cf_1_6.py
Original file line number Diff line number Diff line change
Expand Up @@ -2221,7 +2221,7 @@ def check_grid_coordinates(self, ds):

alt = (
"{} has no coordinate associated with a variable identified as true latitude/longitude; "
+ "its coordinate variable should also share a subset of {}'s dimensions"
"its coordinate variable should also share a subset of {}'s dimensions"
)

# Make sure we can find latitude and its dimensions are a subset
Expand Down
26 changes: 26 additions & 0 deletions compliance_checker/cf/cf_1_9.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from netCDF4 import Dataset

from compliance_checker import cfutil
from compliance_checker.base import BaseCheck, TestCtx
from compliance_checker.cf.cf_1_8 import CF1_8Check
from compliance_checker.cf.util import VariableReferenceError, reference_attr_variables
Expand All @@ -13,6 +14,31 @@ def __init__(self, options=None):
super(CF1_9Check, self).__init__(options)
self.section_titles.update({"5.8": "§5.8 Domain Variables"})

def check_calendar(self, ds):
# IMPLEMENTATION CONFORMANCE 4.4.1 RECOMMENDED CF 1.9
super(CF1_9Check, self).check_calendar.__doc__
prev_return = super(CF1_9Check, self).check_calendar(ds)
time_var_candidate_name = cfutil.get_time_variable(ds)
time_var_name = (
time_var_candidate_name
if time_var_candidate_name in self._find_coord_vars(ds)
else None
)
# most datasets should have a time coordinate variable
test_ctx = self.get_test_ctx(
BaseCheck.HIGH, self.section_titles["4.4"], time_var_name
)
if time_var_name is None:
return prev_return

test_ctx.assert_true(
hasattr(ds.variables[time_var_name], "calendar"),
f'Time coordinate variable "{time_var_name}" '
"should have a calendar attribute",
)
prev_return.append(test_ctx.to_result())
return prev_return

def check_domain_variables(self, ds: Dataset):
# Domain variables should have coordinates attribute, but should have
# scalar dimensions
Expand Down
24 changes: 24 additions & 0 deletions compliance_checker/tests/test_cf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2997,6 +2997,30 @@ class TestCF1_9(BaseTestCase):
def setUp(self):
self.cf = CF1_9Check()

def test_time_variable_has_calendar(self):
# TEST CONFORMANCE 4.4.1 RECOMMENDED CF 1.9
dataset = MockTimeSeries()
results = self.cf.check_calendar(dataset)
assert (
results[0].msgs[0] == 'Time coordinate variable "time" should have a '
"calendar attribute"
)
# FIXME: NetCDF files shouldn't normally be modified so we can usually
# depend on cached results. Here we need to recreate the checker
# instance in order to not have previous results included pass condition
self.cf = CF1_9Check()
dataset.variables["time"].calendar = "standard"
results = self.cf.check_calendar(dataset)
# no time coordinate present, i.e. there is no time variable name with
# the same name as the time dimension name.
self.cf = CF1_9Check()
dataset = MockTimeSeries()
dataset.variables["time2"] = dataset.variables["time"]
del dataset.variables["time"]
results = self.cf.check_calendar(dataset)
# results array should be empty as no time coordinate variable detected
assert not results

def test_domain(self):
dataset = MockTimeSeries()
domain_var = dataset.createVariable("domain", "c", ())
Expand Down

0 comments on commit a745fc7

Please sign in to comment.