Skip to content

Commit

Permalink
Adds support for forecast reference metadata in CF checks. (#779)
Browse files Browse the repository at this point in the history
Adds support for forecast reference metadata in CF checks.

This commit updates cfutil's feature detection to ignore variables used
to declare forecast metadata. Specifically, variables with standard
names 'forecast_period' and 'forecast_reference_time' are used in
conjunction with 'time' to indicate to dataset consumers the appropriate
model initialization time, forecast hour, and valid time. This is
useful for datasets that aggregate multiple forecast runs, such as FMRC.
  • Loading branch information
lukecampbell authored Mar 25, 2020
1 parent 4c14ad4 commit 307aab6
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 0 deletions.
23 changes: 23 additions & 0 deletions compliance_checker/cfutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ def is_geophysical(ds, variable):
if variable in get_auxiliary_coordinate_variables(ds):
return False

if variable in get_forecast_metadata_variables(ds):
return False

# Is it dimensionless and unitless?
if len(ncvar.shape) == 0 and unitless:
return False
Expand Down Expand Up @@ -296,6 +299,26 @@ def get_auxiliary_coordinate_variables(ds):
return ret_val


def get_forecast_metadata_variables(ds):
'''
Returns a list of variables that represent forecast reference time
metadata.
:param netCDF4.Dataset ds: An open netCDF4 Dataset.
:rtype: list
'''
forecast_metadata_standard_names = {
'forecast_period',
'forecast_reference_time',
}
forecast_metadata_variables = []
for varname in ds.variables:
standard_name = getattr(ds.variables[varname], 'standard_name', None)
if standard_name in forecast_metadata_standard_names:
forecast_metadata_variables.append(varname)
return forecast_metadata_variables


def get_cell_boundary_map(ds):
'''
Returns a dictionary mapping a variable to its boundary variable. The
Expand Down
32 changes: 32 additions & 0 deletions compliance_checker/tests/data/forecast_reference.cdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
netcdf forecast_reference.cdl {
dimensions:
time = 2 ;
lat = 4 ;
lon = 4 ;
variables:
float time(time) ;
time:units = "seconds since 1970-01-01" ;
time:standard_name = "time" ;
time:calendar = "gregorian" ;
time:axis = "T" ;
float lat(lat) ;
lat:units = "degrees_north" ;
lat:standard_name = "latitude" ;
lat:axis = "Y" ;
float lon(lon) ;
lon:units = "degrees_east" ;
lon:standard_name = "longitude" ;
lon:axis = "X" ;
float air_temp(time, lon, lat) ;
air_temp:units = "deg_C" ;
air_temp:standard_name = "air_temperature";
float forecast_reference_time(time) ;
forecast_reference_time:units = "seconds since 1970-01-01" ;
forecast_reference_time:standard_name = "forecast_reference_time" ;
float forecast_hour(time) ;
forecast_hour:units = "hours" ;
forecast_hour:standard_name = "forecast_period" ;

:featureType = "grid" ;

}
1 change: 1 addition & 0 deletions compliance_checker/tests/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def generate_dataset(cdl_path, nc_path):
'dimension_order' : get_filename('tests/data/dimension_order.cdl'),
'example-grid' : get_filename('tests/data/example-grid.cdl'),
'featureType' : get_filename('tests/data/example-grid.cdl'),
'forecast_reference' : get_filename('tests/data/forecast_reference.cdl'),
'fvcom' : get_filename('tests/data/examples/fvcom.cdl'),
'ghrsst' : get_filename('tests/data/20160919092000-ABOM-L3S_GHRSST-SSTfnd-AVHRR_D-1d_dn_truncate.cdl'),
'glcfs' : get_filename('tests/data/examples/glcfs.cdl'),
Expand Down
14 changes: 14 additions & 0 deletions compliance_checker/tests/test_feature_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,20 @@ def test_auxiliary_coordinates(self):
aux_coord_vards = util.get_auxiliary_coordinate_variables(nc)
assert set(['lat', 'lon']) == set(aux_coord_vards)

def test_forecast_reference_metadata(self):
'''
Tests variables used for forecast reference metadata to ensure they are
not misclassified as geophysical variables.
'''
with Dataset(resources.STATIC_FILES['forecast_reference']) as nc:
self.assertFalse(util.is_geophysical(nc, 'forecast_reference_time'))
self.assertFalse(util.is_geophysical(nc, 'forecast_hour'))
self.assertTrue(util.is_geophysical(nc, 'air_temp'))
self.assertFalse(util.is_geophysical(nc, 'time'))

assert len(util.get_coordinate_variables(nc)) == 3
assert len(util.get_geophysical_variables(nc)) == 1

def test_rotated_pole_grid(self):
with Dataset(resources.STATIC_FILES['rotated_pole_grid']) as nc:
latitudes = util.get_latitude_variables(nc)
Expand Down

0 comments on commit 307aab6

Please sign in to comment.