Repository: ioos/compliance-checker

Issue:
Orthogonal multidimensional array representation of time series detected as reduced horizontal grid
I've got a time series netCDF file which seems to confuse the compliance checker. Or I'm misunderstanding the output.

Running `compliance-checker g2_BIOPAR_SWI-TS_201001310000_C0102_ASCAT_V1.0.0.nc --test cf` on the nc file in [g2_BIOPAR_SWI-TS_201001310000_C0102_ASCAT_V1.0.0.zip](https://github.com/ioos/compliance-checker/files/354087/g2_BIOPAR_SWI-TS_201001310000_C0102_ASCAT_V1.0.0.zip) gives me the output at the end of this post.

As far as I can tell my file follows the example H.2 from [the CF conventions](http://cfconventions.org/cf-conventions/cf-conventions.html#_orthogonal_multidimensional_array_representation_of_time_series)

The messages about wrong data types do not matter to me but I'm confused about section 5.3 since the data is not a reduced horizontal grid. I also do not understand the output of section 2.4.
### CDL (only one data variable, they are all the same)

```
netcdf file:/home/cpa/workspace/CGLS/SWI_TS/tests/data/output_data/g2_BIOPAR_SWI-TS_201001310000_C0250_ASCAT_V1.0.0.nc {
  dimensions:
    locations = 2060;
    time = UNLIMITED;   // (31 currently)
  variables:
    double lon(locations=2060);
      :units = "degrees_east";
      :long_name = "location longitude";
      :standard_name = "longitude";
      :valid_range = -180.0, 180.0; // double
      :_ChunkSizes = 2060; // int

    double lat(locations=2060);
      :units = "degrees_north";
      :long_name = "location latitude";
      :standard_name = "latitude";
      :valid_range = -90.0, 90.0; // double
      :_ChunkSizes = 2060; // int

    double alt(locations=2060);
      :units = "m";
      :long_name = "vertical distance above the surface";
      :standard_name = "height";
      :axis = "Z";
      :positive = "up";
      :_ChunkSizes = 2060; // int

    long location_id(locations=2060);
      :long_name = "Location indices on DGG";
      :cf_role = "timeseries_id";
      :_ChunkSizes = 2060; // int

    double time(time=31);
      :units = "days since 1900-01-01 00:00:00";
      :long_name = "time of measurement";
      :standard_name = "time";
      :calendar = "standard";
      :_ChunkSizes = 100; // int

    byte SWI_005(locations=2060, time=31);
      :_FillValue = -1UB; // byte
      :scale_factor = 0.5; // double
      :name = "SWI_005";
      :coordinates = "lat lon alt location_id";
      :valid_range = 0UB, -56UB; // byte
      :long_name = "SWI with T=5";
      :units = "%";
      :missing_value = -1UB; // byte
      :_Unsigned = "true";
      :_ChunkSizes = 2060, 100; // int
```
### Checker output

``` shell
Running Compliance Checker on the dataset from: g2_BIOPAR_SWI-TS_201001310000_C0102_ASCAT_V1.0.0.nc


--------------------------------------------------------------------------------
                    The dataset scored 192 out of 244 points                    
                              during the cf check                               
--------------------------------------------------------------------------------
                               Scoring Breakdown:                               


all_features_are_same_type              :2:     0/0
                                 High Priority                                  
--------------------------------------------------------------------------------
    Name                            :Priority: Score
§2.2 Valid netCDF data types           :3:     4/22
§2.3 Legal variable names              :3:    22/22
§2.4 Unique dimensions                 :3:    22/22
§2.5.1 _FillValue outside of valid ran :3:    17/17
§2.6.1 Global Attribute Conventions in :3:     1/1
§2.6.2 Convention Attributes           :3:     2/2
§3.1 Variables contain valid CF Units  :3:     0/1
§3.1 Variables contain valid units for :3:     4/4
§3.3 Standard Names                    :3:     4/4
§4 Axis attributes and coordinate vari :3:     3/3
§4 Coordinate Variable latitude contai :3:     1/1
§4 Coordinate Variable longitude conta :3:     1/1


                                Medium Priority                                 
--------------------------------------------------------------------------------
    Name                            :Priority: Score
§4 Coordinate Variables                :2:     1/1
§4.1 Coordinates representing latitude :2:     3/3
§4.1 Coordinates representing longitud :2:     3/3
§4.3 Vertical coordinates contain vali :3:     2/2
§4.3.1 Vertical dimension coordinates  :3:     1/1
§4.4 Time coordinate variable and attr :3:     2/2
§5.1 Geophysical variables contain val :2:    17/17
§5.2 Latitude and longitude coordinate :3:     4/4
§5.3 Is reduced horizontal grid        :2:     0/17
§6.2 Alternative Coordinates           :2:     4/4
§8.1 Packed Data                       :2:    16/32
§9.3.1 Orthogonal Multidimensional Arr :2:     5/5
§9.3.2 Incomplete Multidimensional Arr :2:    17/17
§9.4 featureType attribute             :2:     1/1
§9.5 Discrete Geometry                 :2:    18/18
§9.6 Missing Data                      :2:    17/17


--------------------------------------------------------------------------------
                  Reasoning for the failed tests given below:                   


Name                             Priority:     Score:Reasoning
--------------------------------------------------------------------------------
§2.2 Valid netCDF data types          :3:     4/22 : The variable location_id
                                                      failed because the
                                                      datatype is int64, The
                                                      variable SWI_005 failed
                                                      because the datatype is
                                                      uint8, The variable
                                                      SWI_010 failed because the
                                                      datatype is uint8, The
                                                      variable SWI_020 failed
                                                      because the datatype is
                                                      uint8, The variable
                                                      SWI_001 failed because the
                                                      datatype is uint8, The
                                                      variable SWI_015 failed
                                                      because the datatype is
                                                      uint8, The variable
                                                      QFLAG_040 failed because
                                                      the datatype is uint8, The
                                                      variable SWI_100 failed
                                                      because the datatype is
                                                      uint8, The variable
                                                      QFLAG_010 failed because
                                                      the datatype is uint8, The
                                                      variable SWI_040 failed
                                                      because the datatype is
                                                      uint8, The variable
                                                      QFLAG_015 failed because
                                                      the datatype is uint8, The
                                                      variable QFLAG_100 failed
                                                      because the datatype is
                                                      uint8, The variable
                                                      SWI_060 failed because the
                                                      datatype is uint8, The
                                                      variable QFLAG_020 failed
                                                      because the datatype is
                                                      uint8, The variable
                                                      QFLAG_005 failed because
                                                      the datatype is uint8, The
                                                      variable SSF failed
                                                      because the datatype is
                                                      uint8, The variable
                                                      QFLAG_001 failed because
                                                      the datatype is uint8, The
                                                      variable QFLAG_060 failed
                                                      because the datatype is
                                                      uint8
§3.1 Variables contain valid CF Units :3:     0/ 1 : unknown units type (None)
                                                      for SSF
§5.3 Is reduced horizontal grid       :2:     0/17 :  
    QFLAG_001                          :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    QFLAG_005                          :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    QFLAG_010                          :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    QFLAG_015                          :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    QFLAG_020                          :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    QFLAG_040                          :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    QFLAG_060                          :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    QFLAG_100                          :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SSF                                :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SWI_001                            :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SWI_005                            :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SWI_010                            :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SWI_015                            :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SWI_020                            :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SWI_040                            :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SWI_060                            :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
    SWI_100                            :2:     0/ 1 :  
        is_reduced_horizontal_grid     :2:     0/ 1 : Coordinate lat's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate lon's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate alt's
                                                      dimension, locations, is
                                                      not a coordinate variable,
                                                      Coordinate location_id's
                                                      dimension, locations, is
                                                      not a coordinate variable
§8.1 Packed Data                      :2:    16/32 :  
    QFLAG_001                          :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    QFLAG_005                          :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    QFLAG_010                          :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    QFLAG_015                          :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    QFLAG_020                          :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    QFLAG_040                          :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    QFLAG_060                          :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    QFLAG_100                          :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    SWI_001                            :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    SWI_005                            :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    SWI_010                            :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    SWI_015                            :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    SWI_020                            :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    SWI_040                            :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    SWI_060                            :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
    SWI_100                            :2:     1/ 2 :  
        packed_data                    :2:     0/ 1 : Variable is not of type
                                                      byte, short, or int.
§2.4 Dimension order                  :1:    27/44 : Variable SWI_005 has a
                                                      non-space-time dimension
                                                      after space-time-
                                                      dimensions, Variable
                                                      SWI_010 has a non-space-
                                                      time dimension after
                                                      space-time-dimensions,
                                                      Variable SWI_020 has a
                                                      non-space-time dimension
                                                      after space-time-
                                                      dimensions, Variable
                                                      SWI_001 has a non-space-
                                                      time dimension after
                                                      space-time-dimensions,
                                                      Variable SWI_015 has a
                                                      non-space-time dimension
                                                      after space-time-
                                                      dimensions, Variable
                                                      QFLAG_040 has a non-space-
                                                      time dimension after
                                                      space-time-dimensions,
                                                      Variable SWI_100 has a
                                                      non-space-time dimension
                                                      after space-time-
                                                      dimensions, Variable
                                                      QFLAG_010 has a non-space-
                                                      time dimension after
                                                      space-time-dimensions,
                                                      Variable SWI_040 has a
                                                      non-space-time dimension
                                                      after space-time-
                                                      dimensions, Variable
                                                      QFLAG_015 has a non-space-
                                                      time dimension after
                                                      space-time-dimensions,
                                                      Variable QFLAG_100 has a
                                                      non-space-time dimension
                                                      after space-time-
                                                      dimensions, Variable
                                                      SWI_060 has a non-space-
                                                      time dimension after
                                                      space-time-dimensions,
                                                      Variable QFLAG_020 has a
                                                      non-space-time dimension
                                                      after space-time-
                                                      dimensions, Variable
                                                      QFLAG_005 has a non-space-
                                                      time dimension after
                                                      space-time-dimensions,
                                                      Variable SSF has a non-
                                                      space-time dimension after
                                                      space-time-dimensions,
                                                      Variable QFLAG_001 has a
                                                      non-space-time dimension
                                                      after space-time-
                                                      dimensions, Variable
                                                      QFLAG_060 has a non-space-
                                                      time dimension after
                                                      space-time-dimensions
```



Discussion:
The 5.3 issue can be solved if the variable `location_id` is renamed to `locations` so that the dimension and the variable have the same name. 

It seems that the 2.4 check just does not take into account the Orthogonal time series representation.

This is scheduled to be replaced as part of the 2.3 milestone. It was brought up in separate issues, including this one, so I just made a new issue that fundamentally describes what needs to be done:

https://github.com/ioos/compliance-checker/issues/285

The checker has a lot of issues with feature type detection at the moment. When it can't accurately determine the feature type it falls back to reduced horizontal grid. 

When we coded it, we wrote it as we read the CF 1.6 conventions document, each section of the document was coded into an individual checker. My aim is to refactor the checker so that it checks each section of the CF conventions based on it's detected feature. I wrote some much better feature detection code as part of the NCEI plugin, which I am suggesting most people use until I can refactor the CF checker. 

Thank you for the explanation. When you talk about the NCEI plugin do you mean https://github.com/ioos/cc-plugin-glider ?

I mean the https://github.com/ioos/cc-plugin-ncei/ checker. You can use it online at http://data.ioos.us/compliance/

This output also highlights another issue, the `§2.4 Dimension order` explanations are completely missing from the summary view on top.  I'll file another issue.

#195 is another example of a file incorrectly classified as reduced horizontal grid. We found a work-around for that case, but I still believe the errors returned for the original file constitute a bug.

Yeah, it's really the only thing that's a real "big" problem with the compliance checker in my opinion. The rest are features and small issues. Once we get this fixed, the checker will be really awesome in my opinion.


Resolving patch:
diff --git a/compliance_checker/cf/cf.py b/compliance_checker/cf/cf.py
--- a/compliance_checker/cf/cf.py
+++ b/compliance_checker/cf/cf.py
@@ -1235,7 +1235,7 @@ def check_coordinate_types(self, ds):
             standard_name = getattr(variable, 'standard_name', None)
 
             valid_coord.assert_true(axis is not None or standard_name is not None,
-                                    "coordinate types are should define either axis or standard_name attributes")
+                                    "coordinate types should define either axis or standard_name attributes")
             ret_val.append(valid_coord.to_result())
 
             if axis is not None:
@@ -2105,60 +2105,50 @@ def check_reduced_horizontal_grid(self, ds):
 
         """
         ret_val = []
-        coord_vars = self._get_coord_vars(ds)
+        # Create a set of coordinate varaibles defining `compress`
+        lats = set(cfutil.get_latitude_variables(ds))
+        lons = set(cfutil.get_longitude_variables(ds))
 
-        for name, var in ds.variables.items():
-            if name in coord_vars:
+        for name in self._find_geophysical_vars(ds):
+            coords = getattr(ds.variables[name], 'coordinates', None)
+            axis_map = cfutil.get_axis_map(ds, name)
+            # If this variable has no coordinate that defines compression
+            if 'C' not in axis_map:
                 continue
-            if not hasattr(var, 'coordinates'):
+
+            valid_rgrid = TestCtx(BaseCheck.HIGH, '§5.3 {} is a valid reduced horizontal grid'.format(name))
+            # Make sure reduced grid features define coordinates
+            valid_rgrid.assert_true(isinstance(coords, basestring) and coords,
+                                    "reduced grid feature {} must define coordinates attribute"
+                                    "".format(name))
+            # We can't check anything else if there are no defined coordinates
+            if not isinstance(coords, basestring) and coords:
                 continue
 
-            reasoning = []
-            valid_in_variables = True
-            valid_dim = True
-            valid_coord = True
-            valid_cdim = True
-            result = None
-
-            coords = var.coordinates.split(' ')
-            for coord in coords:
-                is_reduced_horizontal_grid = True
-                if coord not in ds.variables:
-                    valid_in_variables = False
-                    reasoning.append("Coordinate %s is not a proper variable" % coord)
+            coord_set = set(coords.split())
+
+            # Make sure it's associated with valid lat and valid lon
+            valid_rgrid.assert_true(len(coord_set.intersection(lons)) > 0,
+                                    '{} must be associated with a valid longitude coordinate'.format(name))
+            valid_rgrid.assert_true(len(coord_set.intersection(lats)) > 0,
+                                    '{} must be associated with a valid latitude coordinate'.format(name))
+            valid_rgrid.assert_true(len(axis_map['C']) == 1,
+                                    '{} can not be associated with more than one compressed coordinates: '
+                                    '({})'.format(name, ', '.join(axis_map['C'])))
+
+            for compressed_coord in axis_map['C']:
+                coord = ds.variables[compressed_coord]
+                compress = getattr(coord, 'compress', None)
+                valid_rgrid.assert_true(isinstance(compress, basestring) and compress,
+                                        "compress attribute for compression coordinate {} must be a non-empty string"
+                                        "".format(compressed_coord))
+                if not isinstance(compress, basestring):
                     continue
-
-                for dim_name in ds.variables[coord].dimensions:
-
-                    if dim_name not in var.dimensions:
-                        valid_dim = False
-                        reasoning.append("Coordinate %s's dimension, %s, is not a dimension of %s" % (coord, dim_name, name))
-                        continue
-
-                    if dim_name not in coord_vars:
-                        valid_coord = False
-                        reasoning.append("Coordinate %s's dimension, %s, is not a coordinate variable" % (coord, dim_name))
-                        continue
-
-                    dim = ds.variables[dim_name]
-                    if not hasattr(dim, 'compress'):
-                        is_reduced_horizontal_grid = False
-                        continue
-
-                    compress_dims = dim.compress.split(' ')
-                    for cdim in compress_dims:
-                        if cdim not in ds.dimensions:
-                            valid_cdim = False
-                            reasoning.append("Dimension %s compresses non-existent dimension, %s" % (dim_name, cdim))
-                            continue
-            if is_reduced_horizontal_grid is True:
-                result = Result(BaseCheck.MEDIUM,
-                                (valid_in_variables and valid_dim and valid_coord and valid_cdim),
-                                ('§5.3 Is reduced horizontal grid', name, 'is_reduced_horizontal_grid'),
-                                reasoning)
-
-            if result:
-                ret_val.append(result)
+                for dim in compress.split():
+                    valid_rgrid.assert_true(dim in ds.dimensions,
+                                            "dimension {} referenced by {}:compress must exist"
+                                            "".format(dim, compressed_coord))
+            ret_val.append(valid_rgrid.to_result())
 
         return ret_val
 


Test patch:
diff --git a/compliance_checker/tests/test_cf.py b/compliance_checker/tests/test_cf.py
--- a/compliance_checker/tests/test_cf.py
+++ b/compliance_checker/tests/test_cf.py
@@ -782,19 +782,21 @@ def test_check_two_dimensional(self):
     def test_check_reduced_horizontal_grid(self):
         dataset = self.load_dataset(STATIC_FILES['rhgrid'])
         results = self.cf.check_reduced_horizontal_grid(dataset)
-        rd = { r.name[1] : r.value for r in results }
-        self.assertTrue(rd['PS'])
+
+        result_dict = {result.name: result for result in results}
+        result = result_dict[u'§5.3 PS is a valid reduced horizontal grid']
+        assert result.value == (7, 7)
 
         dataset = self.load_dataset(STATIC_FILES['bad-rhgrid'])
         results = self.cf.check_reduced_horizontal_grid(dataset)
-        rd = { r.name[1] : (r.value, r.msgs) for r in results }
 
-        for name, (value, msg) in rd.items():
-            self.assertFalse(value)
-
-        self.assertIn('Coordinate longitude is not a proper variable', rd['PSa'][1])
-        self.assertIn("Coordinate latitude's dimension, latdim, is not a dimension of PSb", rd['PSb'][1])
-        assert 'PSc' not in rd
+        result_dict = {result.name: result for result in results}
+        result = result_dict[u'§5.3 PSa is a valid reduced horizontal grid']
+        assert result.value == (6, 7)
+        assert result.msgs[0] == "PSa must be associated with a valid longitude coordinate"
+        result = result_dict[u'§5.3 PSb is a valid reduced horizontal grid']
+        # The dimensions don't line up but another §5.0 check catches it.
+        assert result.value == (7, 7)
 
     def test_check_horz_crs_grid_mappings_projections(self):
         dataset = self.load_dataset(STATIC_FILES['mapping'])