From 2da4576b37fe830241b02703b2ed40177af5354b Mon Sep 17 00:00:00 2001 From: RosalynHatcher Date: Wed, 14 Feb 2018 16:35:48 +0000 Subject: [PATCH 1/4] Update example_6.2.nc to add in time var data. Update corresponding .check file. --- test_files/example_6.2.check | 3 +-- test_files/example_6.2.nc.gz | Bin 374 -> 426 bytes 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test_files/example_6.2.check b/test_files/example_6.2.check index 0e99c57..bc173c5 100644 --- a/test_files/example_6.2.check +++ b/test_files/example_6.2.check @@ -14,7 +14,6 @@ Checking variable: n_heat_transport ------------------ Checking variable: time ------------------ -ERROR: (5): co-ordinate variable not monotonic ------------------ Checking variable: lat @@ -24,6 +23,6 @@ Checking variable: lat Checking variable: geo_region ------------------ -ERRORS detected: 1 +ERRORS detected: 0 WARNINGS given: 0 INFORMATION messages: 0 diff --git a/test_files/example_6.2.nc.gz b/test_files/example_6.2.nc.gz index 3b3606c84dc76e18d949568733541a8bd494eef9..8e963717f5aa74a22e7086d2499b0ae3528245a8 100644 GIT binary patch literal 426 zcmV;b0agAViwFn_WrSJ)17&z&ZE$R5Up6i>E^cE0?NmEX!!Q_j`cj{$0}Bhv1&SIL zRHi;qL1JTofg!kwNi9;lvfUyi29CfHdI%1{Q8)re;CK9|s;aOvkm%EA`}>^#;Utrb zsYl$^1XL^xUohZ9KslwxYSnB!OYPYDYuc!kiEQ5ippEOTi4W#Guiu3HJDr;M!rVALB4%+ ze2?=G=QEtgL&uM}KL2$5g!%$?iTd)(u|i$@jt%-&502N)KLU9KZ{i4Y9m9BhMF=%1 UgE^cE0?NmWd!!QtZnl_XsMLlrgg7Sf& zMhH;3wLyg+aEwf1rIErK*)9l)NALiIcmhx30UTI64vM01?}0=kue`I~*`v#hutfTnk!f<_? zV+QGlQI#q|K|XMylekugW8(t5H`+p}WbM=Y&TVb`8RsG|fhzW!Yf>1^7l^9&r*!hJ`eGMDZg7YaV!GM@6$3yZmG6;pPp&X3c;96 zO)p4Ov!46#t*4g*3%51zzxwNS;%0c}=gSu%|JWT|zt;oX)pZ#n@YXL;>k94F4Ixxj U6n`&S{n--o14ljmn7{-803rpnKL7v# From e20342a58fdd5b21b77b6fde5b1dc8f9b5afedcf Mon Sep 17 00:00:00 2001 From: RosalynHatcher Date: Mon, 19 Feb 2018 16:24:50 +0000 Subject: [PATCH 2/4] Additional CF-1.7 checks for section 4.3.3. Update standard_name table version in test .check files --- src/cfchecker/cfchecks.py | 192 ++++++++++++++++++--- test_files/CF_1_0_OK.check | 2 +- test_files/CF_1_7.check | 2 +- test_files/CRM018_test1.check | 2 +- test_files/CRM021_test1.check | 2 +- test_files/CRM024_test1.check | 2 +- test_files/CRM026_test2.check | 2 +- test_files/CRM027_test1.check | 2 +- test_files/CRM027_test2.check | 2 +- test_files/CRM028_test1.check | 2 +- test_files/CRM032_test1.check | 2 +- test_files/CRM033_test1.check | 2 +- test_files/CRM035.check | 2 +- test_files/CRM037.check | 2 +- test_files/CRM038.check | 2 +- test_files/CRM041.check | 2 +- test_files/GregRappa.check | 2 +- test_files/Trac020_test1.check | 2 +- test_files/Trac020_test2.check | 2 +- test_files/Trac022.check | 2 +- test_files/Trac049_test1.check | 2 +- test_files/Trac049_test2.check | 2 +- test_files/UpgradeVn.pl | 2 +- test_files/badc_units.check | 2 +- test_files/cell_measures.check | 2 +- test_files/cell_methods.check | 2 +- test_files/complex.check | 2 +- test_files/example_6.2.check | 2 +- test_files/flag_tests.check | 2 +- test_files/formula_terms.check | 4 +- test_files/hfogo_O1_labelVariable_KT.check | 2 +- 31 files changed, 198 insertions(+), 56 deletions(-) diff --git a/src/cfchecker/cfchecks.py b/src/cfchecker/cfchecks.py index 15567c0..489388e 100644 --- a/src/cfchecker/cfchecks.py +++ b/src/cfchecker/cfchecks.py @@ -40,6 +40,7 @@ from ordereddict import OrderedDict else: from collections import OrderedDict + from collections import defaultdict import re, string, types, numpy @@ -630,6 +631,7 @@ def _checker(self): if self.version >= vn1_7: # Additional conformance checks from CF-1.7 onwards self.chkActualRange(var) + self.chkComputedStandardName(var) if var in coordVars: self.chkMultiDimCoord(var, axes) @@ -1119,8 +1121,11 @@ def setUpFormulas(self): self.alias['atmosphere_hybrid_sigma_pressure_coordinate']='hybrid_sigma_pressure' self.alias['hybrid_sigma_pressure']='hybrid_sigma_pressure' self.alias['atmosphere_hybrid_height_coordinate']='atmosphere_hybrid_height_coordinate' + self.alias['atmosphere_sleve_coordinate']='atmosphere_sleve_coordinate' self.alias['ocean_sigma_coordinate']='ocean_sigma_coordinate' self.alias['ocean_s_coordinate']='ocean_s_coordinate' + self.alias['ocean_s_coordinate_g1']='ocean_s_coordinate_g1' + self.alias['ocean_s_coordinate_g2']='ocean_s_coordinate_g2' self.alias['ocean_sigma_z_coordinate']='ocean_sigma_z_coordinate' self.alias['ocean_double_sigma_coordinate']='ocean_double_sigma_coordinate' @@ -1128,22 +1133,87 @@ def setUpFormulas(self): self.formulas['atmosphere_ln_pressure_coordinate']=['p(k)=p0*exp(-lev(k))'] self.formulas['sigma']=['p(n,k,j,i)=ptop+sigma(k)*(ps(n,j,i)-ptop)'] - self.formulas['hybrid_sigma_pressure']=['p(n,k,j,i)=a(k)*p0+b(k)*ps(n,j,i)' - ,'p(n,k,j,i)=ap(k)+b(k)*ps(n,j,i)'] + self.formulas['hybrid_sigma_pressure']=['p(n,k,j,i)=a(k)*p0+b(k)*ps(n,j,i)', + 'p(n,k,j,i)=ap(k)+b(k)*ps(n,j,i)'] self.formulas['atmosphere_hybrid_height_coordinate']=['z(n,k,j,i)=a(k)+b(k)*orog(n,j,i)'] + self.formulas['atmosphere_sleve_coordinate']=['z(n,k,j,i) = a(k)*ztop + b1(k)*zsurf1(n,j,i) + b2(k)*zsurf2(n,j,i)'] + self.formulas['ocean_sigma_coordinate']=['z(n,k,j,i)=eta(n,j,i)+sigma(k)*(depth(j,i)+eta(n,j,i))'] - self.formulas['ocean_s_coordinate']=['z(n,k,j,i)=eta(n,j,i)*(1+s(k))+depth_c*s(k)+(depth(j,i)-depth_c)*C(k)' - ,'C(k)=(1-b)*sinh(a*s(k))/sinh(a)+b*[tanh(a*(s(k)+0.5))/(2*tanh(0.5*a))-0.5]'] + self.formulas['ocean_s_coordinate']=['z(n,k,j,i)=eta(n,j,i)*(1+s(k))+depth_c*s(k)+(depth(j,i)-depth_c)*C(k)', + 'C(k)=(1-b)*sinh(a*s(k))/sinh(a)+b*[tanh(a*(s(k)+0.5))/(2*tanh(0.5*a))-0.5]'] + + self.formulas['ocean_s_coordinate_g1']=['z(n,k,j,i) = S(k,j,i) + eta(n,j,i) * (1 + S(k,j,i) / depth(j,i))', + 'z(n,k,j,i) = S(k,j,i) + eta(n,j,i) * (1 + S(k,j,i) / depth(j,i))'] + + self.formulas['ocean_s_coordinate_g2']=['z(n,k,j,i) = eta(n,j,i) + (eta(n,j,i) + depth(j,i)) * S(k,j,i)', + 'S(k,j,i) = (depth_c * s(k) + depth(j,i) * C(k)) / (depth_c + depth(j,i))'] + + self.formulas['ocean_sigma_z_coordinate']=['z(n,k,j,i)=eta(n,j,i)+sigma(k)*(min(depth_c,depth(j,i))+eta(n,j,i))', + 'z(n,k,j,i)=zlev(k)'] + + self.formulas['ocean_double_sigma_coordinate']=['z(k,j,i)=sigma(k)*f(j,i)', + 'z(k,j,i)=f(j,i)+(sigma(k)-1)*(depth(j,i)-f(j,i))', + 'f(j,i)=0.5*(z1+z2)+0.5*(z1-z2)*tanh(2*a/(z1-z2)*(depth(j,i)-href))'] + + # Set up nested dictionary of: + # 1) valid standard_names for variables named by the formula_terms attribute + # 2) computed_standard_names (csn) for the variable specifying the formula_terms attribute + + self.ft_var_stdnames=defaultdict(dict) + + self.ft_var_stdnames['atmosphere_ln_pressure_coordinate'] = {'p0': ['reference_air_pressure_for_atmosphere_vertical_coordinate'], + 'csn': ['air_pressure']} + + self.ft_var_stdnames['sigma'] = {'ptop': ['air_pressure_at_top_of_atmosphere_model'], + 'ps': ['surface_air_pressure'], + 'csn': ['air_pressure']} + + self.ft_var_stdnames['hybrid_sigma_pressure'] = {'p0': ['reference_air_pressure_for_atmosphere_vertical_coordinate'], + 'ps': ['surface_air_pressure'], + 'csn': ['air_pressure']} - self.formulas['ocean_sigma_z_coordinate']=['z(n,k,j,i)=eta(n,j,i)+sigma(k)*(min(depth_c,depth(j,i))+eta(n,j,i))' - ,'z(n,k,j,i)=zlev(k)'] + self.ft_var_stdnames['atmosphere_hybrid_height_coordinate'] = {'orog': ['surface_altitude', 'surface_height_above_geopotential_datum'], + 'a': ['atmosphere_hybrid_height_coordinate'], + 'csn': ['altitude', 'height_above_geopotential_datum']} + + self.ft_var_stdnames['atmosphere_sleve_coordinate'] = {'ztop': ['altitude_at_top_of_atmosphere_model', 'height_above_geopotential_datum_at_top_of_atmosphere_model'], + 'csn': ['altitude', 'height_above_geopotential_datum']} + + self.ft_var_stdnames['ocean_sigma_coordinate'] = {'eta': ['set'], 'depth': ['set'], 'csn': ['set']} + self.ft_var_stdnames['ocean_s_coordinate'] = {'eta': ['set'], 'depth': ['set'], 'csn': ['set']} + self.ft_var_stdnames['ocean_s_coordinate_g1'] = {'eta': ['set'], 'depth': ['set'], 'csn': ['set']} + self.ft_var_stdnames['ocean_s_coordinate_g2'] = {'eta': ['set'], 'depth': ['set'], 'csn': ['set']} + self.ft_var_stdnames['ocean_sigma_z_coordinate'] = {'eta': ['set'], 'depth': ['set'], 'zlev': ['set'], 'csn': ['set']} + self.ft_var_stdnames['ocean_double_sigma_coordinate'] = {'depth': ['set'], 'csn': ['set']} + + self.ft_stdname_sets=defaultdict(dict) + self.ft_stdname_sets[0] = {'zlev': ['altitude'], + 'eta': ['sea_surface_height_above_geoid'], + 'depth': ['sea_floor_depth_below_geoid'], + 'csn': ['altitude']} + + self.ft_stdname_sets[1] = {'zlev': ['height_above_geopotential_datum'], + 'eta': ['sea_surface_height_above_geopotential_datum'], + 'depth': ['sea_floor_depth_below_geopotential_datum'], + 'csn': ['height_above_geopotential_datum']} + + self.ft_stdname_sets[2] ={'zlev': ['height_above_reference_ellipsoid'], + 'eta': ['sea_surface_height_above_reference_ellipsoid'], + 'depth': ['sea_floor_depth_below_reference_ellipsoid'], + 'csn': ['height_above_reference_ellipsoid']} + + self.ft_stdname_sets[3] = {'zlev': ['height_above_mean_sea_level'], + 'eta': ['sea_surface_height_above_mean_ sea_level'], + 'depth': ['sea_floor_depth_below_mean_ sea_level'], + 'csn': ['height_above_mean_sea_level']} + + + + - self.formulas['ocean_double_sigma_coordinate']=['z(k,j,i)=sigma(k)*f(j,i)' - ,'z(k,j,i)=f(j,i)+(sigma(k)-1)*(depth(j,i)-f(j,i))' - ,'f(j,i)=0.5*(z1+z2)+0.5*(z1-z2)*tanh(2*a/(z1-z2)*(depth(j,i)-href))'] #---------------------------------------- @@ -1754,7 +1824,7 @@ def chkCellMeasures(self,varName): #---------------------------------- def chkFormulaTerms(self,varName,allCoordVars): #---------------------------------- - """Checks on formula_terms attribute (CF Section 4.3.2): + """Checks on formula_terms attribute (CF Section 4.3.3): formula_terms = var: term var: term ... 1) No standard_name present 2) No formula defined for std_name @@ -1764,47 +1834,108 @@ def chkFormulaTerms(self,varName,allCoordVars): if hasattr(var, 'formula_terms'): + if self.version >= vn1_7: + # CF conventions document reorganised - section no. has changed + scode = "4.3.3" + else: + scode = "4.3.2" + if varName not in allCoordVars: - self._add_error("formula_terms attribute only allowed on coordinate variables", varName, code="4.3.2") + self._add_error("formula_terms attribute only allowed on coordinate variables", varName, code=scode) # Get standard_name to determine which formula is to be used if not hasattr(var, 'standard_name'): - self._add_error("Cannot get formula definition as no standard_name", varName, code="4.3.2") + self._add_error("Cannot get formula definition as no standard_name", varName, code=scode) # No sense in carrying on as can't validate formula_terms without valid standard name return - (stdName,modifier) = self.getStdName(var) if not self.alias.has_key(stdName): - self._add_error("No formula defined for standard name: %s" % stdName, varName, code="4.3.2") + self._add_error("No formula defined for standard name: %s" % stdName, varName, code=scode) # No formula available so can't validate formula_terms return index=self.alias[stdName] + if self.version >= vn1_7: + # Check computed_standard_name is valid + setname=None + if hasattr(var, 'computed_standard_name'): + csn = var.computed_standard_name + if self.ft_var_stdnames[index]['csn'][0] == 'set': + # Check which set + for key in self.ft_stdname_sets.keys(): + if csn in self.ft_stdname_sets[key]['csn']: + # Found + setname = key + + elif csn not in self.ft_var_stdnames[index]['csn']: + self._add_error("Invalid computed_standard_name: %s" % csn, varName, code=scode) + formulaTerms=var.formula_terms if not re.search("^([a-zA-Z0-9_]+: +[a-zA-Z0-9_]+( +)?)*$",formulaTerms): - self._add_error("Invalid formula_terms syntax", varName, code="4.3.2") + self._add_error("Invalid formula_terms syntax", varName, code=scode) else: # Need to validate the term & var - split=string.split(formulaTerms) - for x in split[:]: - if not re.search("^[a-zA-Z0-9_]+:$", x): - # Variable - should be declared in netCDF file - if x not in self.f.variables.keys(): - self._add_error("%s is not declared as a variable" % x, varName, code="4.3.2") - else: + iter_obj=iter(formulaTerms.split()) + while True: + try: + term = iter_obj.next() + term=re.sub(':','',term) + + ftvar = iter_obj.next() + # Term - Should be present in formula - x=re.sub(':','',x) found='false' for formula in self.formulas[index]: - if re.search(x,formula): + if re.search(term,formula): found='true' break if found == 'false': - self._add_error("term %s not present in formula" % x, varName, code="4.3.2") + self._add_error("Formula term %s not present in formula for %s" % (term, stdName), varName, code=scode) + + # Variable - should be declared in netCDF file + if ftvar not in self.f.variables.keys(): + self._add_error("%s is not declared as a variable" % ftvar, varName, code=scode) + elif ftvar == varName: + # var is the variable specifying the formula_terms attribute + pass + else: + if self.version >= vn1_7: + # Check that standard_name of formula term is consistent with that + # of the coordinate variable + if hasattr(self.f.variables[ftvar], 'standard_name'): +# print "RSH: index -%s, ftvar - %s, term - %s" % (index, ftvar, term) +# print "RSH ftvar.standard_name:", self.f.variables[ftvar].standard_name + try: + valid_stdnames=self.ft_var_stdnames[index][term] + except KeyError: + # No standard_name specified for this formula_term + continue + + if valid_stdnames[0] == 'set': + if setname is None: + for key in self.ft_stdname_sets.keys(): + if self.f.variables[ftvar].standard_name in self.ft_stdname_sets[key][term]: + # Found + if not setname: + setname = key + elif setname != key: + # standard_names of formula_terms vars are inconsistent + self._add_error("Standard names of formula_terms variables are inconsistent/invalid", varName, code=scode) + break + else: + if not self.f.variables[ftvar].standard_name in self.ft_stdname_sets[setname][term]: + self._add_error("Standard names of formula_terms variables are inconsistent/invalid", varName, code=scode) + + elif self.f.variables[ftvar].standard_name not in valid_stdnames: + self._add_error("Standard name of variable %s inconsistent with that of %s" % (ftvar, varName), + varName, code=scode) + + except StopIteration: + break #---------------------------------------- def chkUnits(self,varName,allCoordVars): @@ -1945,6 +2076,17 @@ def chkValidMinMaxRange(self, varName): if hasattr(var, 'valid_min') or hasattr(var, 'valid_max'): self._add_error("Illegal use of valid_range and valid_min/valid_max", varName, code="2.5.1") + + #------------------------------------------ + def chkComputedStandardName(self, varName): + #------------------------------------------ + """Check if var computed_standard_name attribute that it also has formula_terms attribute""" + var= self.f.variables[varName] + + if hasattr(var, 'computed_standard_name') and not hasattr(var, 'formula_terms'): + self._add_error("computed_standard_name attribute is only allowed on a coordinate variable which has a formula_terms attribute", + varName, code="4.3.3") + #--------------------------------- def chkActualRange(self, varName): #--------------------------------- diff --git a/test_files/CF_1_0_OK.check b/test_files/CF_1_0_OK.check index 7839882..38d16c0 100644 --- a/test_files/CF_1_0_OK.check +++ b/test_files/CF_1_0_OK.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CF_1_0_OK.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/CF_1_7.check b/test_files/CF_1_7.check index 8c035aa..22487d5 100644 --- a/test_files/CF_1_7.check +++ b/test_files/CF_1_7.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CF_1_7.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.7 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Area Type Table Version 6 (22 February 2017) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/CRM018_test1.check b/test_files/CRM018_test1.check index ccbbf78..119c7e6 100644 --- a/test_files/CRM018_test1.check +++ b/test_files/CRM018_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM018_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (2.6.1): No 'Conventions' attribute present diff --git a/test_files/CRM021_test1.check b/test_files/CRM021_test1.check index 8de2fff..51e9540 100644 --- a/test_files/CRM021_test1.check +++ b/test_files/CRM021_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM021_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/CRM024_test1.check b/test_files/CRM024_test1.check index fe5f620..cd00e4b 100644 --- a/test_files/CRM024_test1.check +++ b/test_files/CRM024_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM024_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) ERROR: (2.6.1): This netCDF file does not appear to contain CF Convention data. diff --git a/test_files/CRM026_test2.check b/test_files/CRM026_test2.check index aed6fb0..244c87b 100644 --- a/test_files/CRM026_test2.check +++ b/test_files/CRM026_test2.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM026_test2.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/CRM027_test1.check b/test_files/CRM027_test1.check index f466ed1..be35a96 100644 --- a/test_files/CRM027_test1.check +++ b/test_files/CRM027_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM027_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (7.1): Data for variable time lies outside cell boundaries diff --git a/test_files/CRM027_test2.check b/test_files/CRM027_test2.check index 0eb3538..5bbfde1 100644 --- a/test_files/CRM027_test2.check +++ b/test_files/CRM027_test2.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM027_test2.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (7.1): Data for variable time lies outside cell boundaries diff --git a/test_files/CRM028_test1.check b/test_files/CRM028_test1.check index c5761f2..898a9fa 100644 --- a/test_files/CRM028_test1.check +++ b/test_files/CRM028_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM028_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (2.6.1): No 'Conventions' attribute present diff --git a/test_files/CRM032_test1.check b/test_files/CRM032_test1.check index 64edd8d..34ed9dd 100644 --- a/test_files/CRM032_test1.check +++ b/test_files/CRM032_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM032_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) ERROR: (2.6.1): This netCDF file does not appear to contain CF Convention data. diff --git a/test_files/CRM033_test1.check b/test_files/CRM033_test1.check index 62db726..92c0e4b 100644 --- a/test_files/CRM033_test1.check +++ b/test_files/CRM033_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM033_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (2.6.1): No 'Conventions' attribute present diff --git a/test_files/CRM035.check b/test_files/CRM035.check index d4cf19d..306aa92 100644 --- a/test_files/CRM035.check +++ b/test_files/CRM035.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM035.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (2.6.1): No 'Conventions' attribute present diff --git a/test_files/CRM037.check b/test_files/CRM037.check index 8e4be14..a81ad3b 100644 --- a/test_files/CRM037.check +++ b/test_files/CRM037.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM037.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/CRM038.check b/test_files/CRM038.check index 7490709..c1a0067 100644 --- a/test_files/CRM038.check +++ b/test_files/CRM038.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM038.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/CRM041.check b/test_files/CRM041.check index fe81d58..1d5b30c 100644 --- a/test_files/CRM041.check +++ b/test_files/CRM041.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: CRM041.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/GregRappa.check b/test_files/GregRappa.check index e24721e..c5aa0b8 100644 --- a/test_files/GregRappa.check +++ b/test_files/GregRappa.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: GregRappa.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/Trac020_test1.check b/test_files/Trac020_test1.check index 1dbd2a5..c95c7c5 100644 --- a/test_files/Trac020_test1.check +++ b/test_files/Trac020_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: Trac020_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/Trac020_test2.check b/test_files/Trac020_test2.check index a714305..a6376a1 100644 --- a/test_files/Trac020_test2.check +++ b/test_files/Trac020_test2.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: Trac020_test2.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/Trac022.check b/test_files/Trac022.check index 7c3ebd2..3c2917e 100644 --- a/test_files/Trac022.check +++ b/test_files/Trac022.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: Trac022.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (2.6.1): No 'Conventions' attribute present diff --git a/test_files/Trac049_test1.check b/test_files/Trac049_test1.check index 7fcf656..4edda56 100644 --- a/test_files/Trac049_test1.check +++ b/test_files/Trac049_test1.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: Trac049_test1.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.4 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Area Type Table Version 6 (22 February 2017) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/Trac049_test2.check b/test_files/Trac049_test2.check index 68c3682..3ec07f4 100644 --- a/test_files/Trac049_test2.check +++ b/test_files/Trac049_test2.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: Trac049_test2.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.4 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Area Type Table Version 6 (22 February 2017) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/UpgradeVn.pl b/test_files/UpgradeVn.pl index c0d1f3f..27cd8e7 100755 --- a/test_files/UpgradeVn.pl +++ b/test_files/UpgradeVn.pl @@ -14,7 +14,7 @@ # $standardNameVN as appropriate. #-------------------------------------------------------------------------- $checkerVN="3.0.6-dev"; -$standardNameVN="48 (2017-11-28T15:32:48Z)"; +$standardNameVN="49 (2018-02-13T08:44:33Z)"; $TEST_FILES_DIR="/home/ros/puma2/git-projects/cf-checker/test_files"; chdir $TEST_FILES_DIR or die "Failed to cd to $TEST_FILES_DIR: $!\n"; diff --git a/test_files/badc_units.check b/test_files/badc_units.check index 6940511..6f4037c 100644 --- a/test_files/badc_units.check +++ b/test_files/badc_units.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: badc_units.nc ===================== Using CF Checker Version 3.0.6-dev -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) WARNING (7.1): Data for variable time lies outside cell boundaries diff --git a/test_files/cell_measures.check b/test_files/cell_measures.check index c641643..e10f9a4 100644 --- a/test_files/cell_measures.check +++ b/test_files/cell_measures.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: cell_measures.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) ERROR: (7.1): Incorrect dimensions for boundary variable: lon_vertices diff --git a/test_files/cell_methods.check b/test_files/cell_methods.check index 3093fc8..78f2de5 100644 --- a/test_files/cell_methods.check +++ b/test_files/cell_methods.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: cell_methods.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) ERROR: (7.1): Incorrect dimensions for boundary variable: lon_vertices diff --git a/test_files/complex.check b/test_files/complex.check index 4bb353a..122d922 100644 --- a/test_files/complex.check +++ b/test_files/complex.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: complex.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (2.6.1): No 'Conventions' attribute present diff --git a/test_files/example_6.2.check b/test_files/example_6.2.check index bc173c5..0cc7df7 100644 --- a/test_files/example_6.2.check +++ b/test_files/example_6.2.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: example_6.2.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.7 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Area Type Table Version 6 (22 February 2017) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/flag_tests.check b/test_files/flag_tests.check index 6849c19..6b841ab 100644 --- a/test_files/flag_tests.check +++ b/test_files/flag_tests.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: flag_tests.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.3 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) diff --git a/test_files/formula_terms.check b/test_files/formula_terms.check index 5897c42..eaa56f9 100644 --- a/test_files/formula_terms.check +++ b/test_files/formula_terms.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: formula_terms.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (7.1): Data for variable lat lies outside cell boundaries @@ -68,7 +68,7 @@ ERROR: (5): co-ordinate variable not monotonic Checking variable: sigma3 ------------------ ERROR: (4.3.2): fred is not declared as a variable -ERROR: (4.3.2): term ps2 not present in formula +ERROR: (4.3.2): Formula term ps2 not present in formula for atmosphere_sigma_coordinate ERROR: (5): co-ordinate variable not monotonic ------------------ diff --git a/test_files/hfogo_O1_labelVariable_KT.check b/test_files/hfogo_O1_labelVariable_KT.check index 31d75d5..c5155ad 100644 --- a/test_files/hfogo_O1_labelVariable_KT.check +++ b/test_files/hfogo_O1_labelVariable_KT.check @@ -2,7 +2,7 @@ CHECKING NetCDF FILE: hfogo_O1_labelVariable_KT.nc ===================== Using CF Checker Version 3.0.6-dev Checking against CF Version CF-1.0 -Using Standard Name Table Version 48 (2017-11-28T15:32:48Z) +Using Standard Name Table Version 49 (2018-02-13T08:44:33Z) Using Standardized Region Name Table Version 2 (12 June 2013) From 12a3849aac1c6cf59f043677f5556bc2e5dcbb11 Mon Sep 17 00:00:00 2001 From: RosalynHatcher Date: Mon, 26 Feb 2018 12:48:48 +0000 Subject: [PATCH 3/4] Addition checks for grid_mapping variables for CF-1.7 Ticket #31 --- src/cfchecker/cfchecks.py | 161 ++++++++++++++++++++++++++++++++-- test_files/CF_1_7.check | 71 ++++++++++++++- test_files/CF_1_7.nc.gz | Bin 2807 -> 1545 bytes test_files/example_5.10.check | 51 +++++++++++ test_files/example_5.10.nc.gz | Bin 0 -> 800575 bytes test_files/tests.sh | 5 +- 6 files changed, 273 insertions(+), 15 deletions(-) create mode 100644 test_files/example_5.10.check create mode 100644 test_files/example_5.10.nc.gz diff --git a/src/cfchecker/cfchecks.py b/src/cfchecker/cfchecks.py index 489388e..515f964 100644 --- a/src/cfchecker/cfchecks.py +++ b/src/cfchecker/cfchecks.py @@ -351,6 +351,7 @@ def checker(self, file): # Set up dictionary of all valid attributes, their type and use self.setUpAttributeList() + self.validGridMappingAttributes() # Set up dictionary of standard_names and their assoc. units parser = make_parser() @@ -1065,19 +1066,120 @@ def getCoordinateDataVars(self): #------------------------------------------ if hasattr(self.f.variables[var], 'grid_mapping'): grid_mapping = self.f.variables[var].grid_mapping - # Check syntax of grid_mapping attribute: a string whose value is a single variable name. - if not re.search("^[a-zA-Z0-9_]*$",grid_mapping): - self._add_error("%s - Invalid syntax for 'grid_mapping' attribute" % var, var, code="5.6") - else: - if grid_mapping in variables: - gridMappingVars.append(grid_mapping) + + (grid_mapping_vars, coord_vars) = self.chkGridMappingAttribute(var, grid_mapping) + + for gmv in grid_mapping_vars: + if gmv in variables: + gridMappingVars.append(gmv) else: - self._add_error("grid_mapping attribute referencing non-existent variable %s" % grid_mapping, - var) + self._add_error("grid_mapping attribute referencing non-existent variable %s" % gmv, + var, code="5.6") + + for cv in coord_vars: + # cv must be the name of a coordinate variable or auxiliary coordinate variable + if cv not in self.f.variables[var].dimensions and cv not in coordinates: + self._add_error("%s must be the name of a coordinate variable or auxiliary coordinate variable of %s" % (cv,var), + var, code="5.6") + if cv not in allVariables: + self._add_error("grid_mapping attribute referencing non-existent coordinate variable %s" % cv, + var, code="5.6") return (coordVars, auxCoordVars, boundaryVars, climatologyVars, gridMappingVars) + #------------------ + def subst(self, s): + #------------------ + "substitute tokens for WORD and SEP (space or end of string)" + return s.replace('WORD', r'[A-Za-z0-9_]+').replace('SEP', r'(\s+|$)') + + #-------------------------------------------------------- + def chkGridMappingAttribute(self, varName, grid_mapping): + #-------------------------------------------------------- + """Validate syntax of grid_mapping attribute""" + + grid_mapping_vars=[] + coord_vars=[] + + if self.version < vn1_7: + # Syntax: a string whose value is a single variable name + pat_sole = self.subst('(?PWORD)$') + m = re.match(pat_sole, grid_mapping) + + else: + # Syntax: a string whose value is a single variable name or of the form: + # grid_mapping_var: coord_var [coord_var ...] [grid_mapping_var: coord_var [coord_var ...]] + + pat_coord = self.subst('(?PWORD)SEP') + pat_coord_list = '({})+'.format(pat_coord) + + pat_mapping = self.subst('(?PWORD):SEP(?P{})'.format(pat_coord_list)) + pat_mapping_list = '({})+'.format(pat_mapping) + + pat_all = self.subst('((?PWORD)|(?P{}))$'.format(pat_mapping_list)) + + m = re.match(pat_all, grid_mapping) + + if not m: + self._add_error("%s - Invalid syntax for 'grid_mapping' attribute" % varName, varName, code="5.6") + return ([], []) + + # Parse grid_mapping attribute to obtain a list of grid_mapping_vars and a list of coord_vars + sole_mapping = m.group('sole_mapping') + if sole_mapping: + # Contains only a single variable name + grid_mapping_vars.append(sole_mapping) + + else: + # Complex form, split into lists of grid_mapping_vars and coord_vars + mapping_list = m.group('mapping_list') + for mapping in re.finditer(pat_mapping, mapping_list): + mapping_name = mapping.group('mapping_name') + coord_list = mapping.group('coord_list') + + grid_mapping_vars.append(mapping_name) + for coord in re.finditer(pat_coord, coord_list): + coord_vars.append(coord.group('coord')) + + return (map(str,grid_mapping_vars), map(str,coord_vars)) + + #------------------------------------ + def validGridMappingAttributes(self): + #------------------------------------ + """Setup dictionary of valid grid mapping attributes and their types""" + + self.grid_mapping_attrs=dict([('azimuth_of_central_line', 'N'), + ('crs_wkt', 'S'), + ('earth_radius', 'N'), + ('false_easting', 'N'), + ('false_northing', 'N'), + ('geographic_crs_name', 'S'), + ('geoid_name', 'S'), + ('geopotential_datum_name', 'S'), + ('grid_mapping_name', 'S'), + ('grid_north_pole_latitude', 'N'), + ('grid_north_pole_longitude', 'N'), + ('horizontal_datum_name', 'S'), + ('inverse_flattening', 'N'), + ('latitude_of_projection_origin', 'N'), + ('longitude_of_central_meridian', 'N'), + ('longitude_of_prime_meridian', 'N'), + ('longitude_of_projection_origin', 'N'), + ('north_pole_grid_longitude', 'N'), + ('perspective_point_height', 'N'), + ('prime_meridian_name', 'S'), + ('projected_crs_name', 'S'), + ('reference_ellipsoid_name', 'S'), + ('scale_factor_at_central_meridian', 'N'), + ('scale_factor_at_projection_origin', 'N'), + ('semi_major_axis', 'N'), + ('semi_minor_axis', 'N'), + ('standard_parallel', 'N'), + ('straight_vertical_longitude_from_pole', 'N'), + ('towgs84', 'N')]) + return + #------------------------------------- def chkGridMappingVar(self, varName): #------------------------------------- @@ -1097,6 +1199,10 @@ def chkGridMappingVar(self, varName): if self.version >= vn1_4: # Extra grid_mapping_names at vn1.4 validNames[len(validNames):] = ['lambert_cylindrical_equal_area','mercator','orthographic'] + + if self.version >= vn1_7: + # Extra grid_mapping_names at vn1.7 + validNames[len(validNames):] = ['geostationary', 'oblique_mercator', 'sinusoidal'] if var.grid_mapping_name not in validNames: self._add_error("Invalid grid_mapping_name: %s" % var.grid_mapping_name, @@ -1109,6 +1215,45 @@ def chkGridMappingVar(self, varName): self._add_warn("A grid mapping variable should have 0 dimensions", varName, code="5.6") + for attribute in map(str, var.ncattrs()): + + # Check type of attribute matches that specified in Appendix F: Table 1 + attr_type = type(var.getncattr(attribute)) + + if isinstance(var.getncattr(attribute), basestring): + attr_type='S' + + elif (numpy.issubdtype(attr_type, numpy.int) or + numpy.issubdtype(attr_type, numpy.float) or + attr_type == numpy.ndarray): + attr_type='N' + + else: + self._add_info("Unknown Type for attribute: %s %s" % (attribute, attr_type)) + continue + + if (attribute in self.grid_mapping_attrs.keys() and + attr_type != self.grid_mapping_attrs[attribute]): + self._add_error("Attribute %s of incorrect data type (Appendix F)" % attribute, + varName, code="5.6") + + if self.version >= vn1_7: + if hasattr(var, 'crs_wkt'): + msg = "CF checker currently does not verify the syntax of the crs_wkt attribute " \ + "which must conform to the CRS WKT specification" + self._add_info(msg, varName, code="5.6") + + # If any of these attributes are present then they all must be + l=['reference_ellipsoid_name', 'prime_meridian_name', 'horizontal_datum_name', 'geographic_crs_name'] + if any(hasattr(var, x) for x in l) and not all(hasattr(var, x) for x in l): + msg = "reference_ellipsoid_name, prime_meridian_name, horizontal_datum_name " \ + "and geographic_crs_name must all be definied if any one is defined" + self._add_error(msg, varName, code="5.6") + + if hasattr(var, 'projected_crs_name') and not hasattr(var, 'geographic_crs_name'): + self._add_error("projected_crs_name is defined therefore geographic_crs_name must be also", + varName, code="5.6") + #------------------------ def setUpFormulas(self): #------------------------ diff --git a/test_files/CF_1_7.check b/test_files/CF_1_7.check index 22487d5..9a096de 100644 --- a/test_files/CF_1_7.check +++ b/test_files/CF_1_7.check @@ -11,7 +11,6 @@ ERROR: (2.6.3): Variable external_var2 named as an external variable must not be ------------------ Checking variable: lat ------------------ -ERROR: (5): co-ordinate variable not monotonic ------------------ Checking variable: lon @@ -65,6 +64,70 @@ Checking variable: arv4 ERROR: (2.5.1): actual_range attribute must be of same type as variable arv4 ERROR: (2.5.1): actual_range values must be greater than or equal to 15.0 (valid_min) -ERRORS detected: 14 -WARNINGS given: 0 -INFORMATION messages: 0 +------------------ +Checking variable: current_speed_qc +------------------ +WARN: (3.3): Use of standard_name modifier status_flag is deprecated + +------------------ +Checking variable: current_speed_qc2 +------------------ +WARN: (3.3): Use of standard_name modifier number_of_observations is deprecated + +------------------ +Checking variable: n_heat_transport +------------------ + +------------------ +Checking variable: geo_region +------------------ +ERROR: (3.3): Invalid region name: dummy_ocean + +------------------ +Checking variable: lev +------------------ +ERROR: (4.3.3): Standard name of variable PS inconsistent with that of lev +ERROR: (4.3.3): PTOP is not declared as a variable +ERROR: (5): co-ordinate variable not monotonic + +------------------ +Checking variable: PS +------------------ + +------------------ +Checking variable: sigma +------------------ +ERROR: (4.3.3): Formula term dummy not present in formula for ocean_sigma_coordinate +ERROR: (4.3.3): Standard names of formula_terms variables are inconsistent/invalid +ERROR: (5): co-ordinate variable not monotonic + +------------------ +Checking variable: var2 +------------------ +INFO: (3.1): No units attribute set. Please consider adding a units attribute for completeness. + +------------------ +Checking variable: var3 +------------------ +INFO: (3.1): No units attribute set. Please consider adding a units attribute for completeness. + +------------------ +Checking variable: sigma2 +------------------ +WARN: (3.1): units attribute should be present +ERROR: (4.3.3): computed_standard_name attribute is only allowed on a coordinate variable which has a formula_terms attribute +ERROR: (5): co-ordinate variable not monotonic + +------------------ +Checking variable: temp +------------------ + +------------------ +Checking variable: crs +------------------ +ERROR: (5.6): Attribute longitude_of_prime_meridian of incorrect data type (Appendix F) +ERROR: (5.6): reference_ellipsoid_name, prime_meridian_name, horizontal_datum_name and geographic_crs_name must all be definied if any one is defined + +ERRORS detected: 24 +WARNINGS given: 3 +INFORMATION messages: 2 diff --git a/test_files/CF_1_7.nc.gz b/test_files/CF_1_7.nc.gz index 4c0a5f105f61c9e56e87364b23b2b98f9c39f1de..3285767b320fd1c4effd6fd3970776b2edd75d6b 100644 GIT binary patch literal 1545 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 $outdir/$file.out 2>&1 - elif [[ $file == "CF_1_7.nc" || $file = "example_6.2.nc" ]] + elif [[ $file == "CF_1_7.nc" || $file == "example_6.2.nc" || $file == "example_5.10.nc" ]] then - # CF-1.7 + # Run checker using the CF version specified in the conventions attribute of the file $cfchecker -s $std_name_table -v auto $file > $outdir/$file.out 2>&1 else # Run the checker on the file From b7ed52f31a4e27a991d8c3b3813473f624e1a948 Mon Sep 17 00:00:00 2001 From: RosalynHatcher Date: Mon, 26 Feb 2018 15:55:30 +0000 Subject: [PATCH 4/4] cell_boundaries additional checks. Ticket #32 --- src/cfchecker/cfchecks.py | 37 +++++++++++++++++++++++------------ test_files/stdName_test.check | 6 ++++-- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/cfchecker/cfchecks.py b/src/cfchecker/cfchecks.py index 515f964..b8a06f4 100644 --- a/src/cfchecker/cfchecks.py +++ b/src/cfchecker/cfchecks.py @@ -976,14 +976,19 @@ def getCoordinateDataVars(self): else: self._add_error("Incorrect number of dimensions for boundary variable: %s" % bounds, bounds, code="7.1") - if hasattr(self.f.variables[bounds], 'units'): - if self.f.variables[bounds].units != self.f.variables[var].units: - self._add_error("Boundary var %s has inconsistent units to %s" % (bounds, var), - bounds, code="7.1") - if hasattr(self.f.variables[bounds], 'standard_name') and hasattr(self.f.variables[var], 'standard_name'): - if self.f.variables[bounds].standard_name != self.f.variables[var].standard_name: - self._add_error("Boundary var %s has inconsistent std_name to %s" % (bounds, var), - bounds, code="7.1") + l = ['units', 'standard_name'] + if self.version >= vn1_7: + l[len(l):] = ['axis', 'positive', 'calendar', 'leap_month', 'leap_year', 'month_lengths'] + for x in l: + if hasattr(self.f.variables[bounds], x): + if self.version >= vn1_7: + self._add_warn("Boundary var %s should not have attribute %s" % (bounds, x), + bounds, code="7.1") + if (hasattr(self.f.variables[var], x) + and self.f.variables[bounds].getncattr(x) != self.f.variables[var].getncattr(x)): + self._add_error("Boundary var %s has inconsistent %s to %s" % (bounds, x, var), + bounds, code="7.1") + else: self._add_error("bounds attribute referencing non-existent variable %s" % bounds, bounds, code="7.1") @@ -1438,7 +1443,6 @@ def chkGlobalAttributes(self): # External variables if self.version >= vn1_7 and hasattr(self.f, 'external_variables'): external_vars = self.f.external_variables - if not self.parseBlankSeparatedList(external_vars) : self._add_error("external_variables attribute must be a blank separated list of variable names", code="2.6.3") @@ -1935,9 +1939,18 @@ def chkCellMeasures(self,varName): variable=splitIter.next() if variable not in self.f.variables: - self._add_warn("cell_measures refers to variable %s that doesn't exist in this netCDF file. " % variable + - "This is strictly an error if the cell_measures variable is not included in the dataset.", - varName, code="7.2") + if self.version >= vn1_7: + # Variable must exist in the file or be named by the external_variables attribute + msg = "cell_measures variable %s must either exist in this netCDF file " \ + "or be named by the external_variables attribute" % variable + if not hasattr(self.f, 'external_variables'): + self._add_error(msg, varName, code="7.2") + elif variable not in string.split(self.f.external_variables): + self._add_error(msg, varName, code="7.2") + else: + self._add_warn("cell_measures refers to variable %s that doesn't exist in this netCDF file. " % variable + + "This is strictly an error if the cell_measures variable is not included in the dataset.", + varName, code="7.2") else: # Valid variable name in cell_measures so carry on with tests. diff --git a/test_files/stdName_test.check b/test_files/stdName_test.check index a5d1965..b286550 100644 --- a/test_files/stdName_test.check +++ b/test_files/stdName_test.check @@ -7,7 +7,9 @@ Using Area Type Table Version 6 (22 February 2017) Using Standardized Region Name Table Version 2 (12 June 2013) WARN: (2.6.1): Inconsistency - This netCDF file appears to contain CF-1.0 data, but you've requested a validity check against CF-1.7 -ERROR: (7.1): Boundary var time_bnds has inconsistent std_name to time +WARN: (7.1): Boundary var time_bnds should not have attribute units +WARN: (7.1): Boundary var time_bnds should not have attribute standard_name +ERROR: (7.1): Boundary var time_bnds has inconsistent standard_name to time WARN: (7.1): Data for variable time lies outside cell boundaries ------------------ @@ -39,5 +41,5 @@ Checking variable: time_bnds ------------------ ERRORS detected: 3 -WARNINGS given: 4 +WARNINGS given: 6 INFORMATION messages: 0