Repository: ARM-DOE/ACT

Issue:
QC bitwise comparison of float values
Some xarray methods may cast the QC variables from type int to type float. For example .resample() will convert all variables to float type. This causes issues when performing bitwise comparisons of QC variables. In particular 

in function get_qc_test_mask() 

    check_bit = set_bit(0, test_number) & qc_variable

Need to check variable type and cast to int before performing bitwise comparison.


Discussion:
This can be assigned to me

Resolving patch:
diff --git a/act/qc/qcfilter.py b/act/qc/qcfilter.py
--- a/act/qc/qcfilter.py
+++ b/act/qc/qcfilter.py
@@ -277,7 +277,7 @@ def add_test(self, var_name, index=None, test_number=None,
         # python indexing.
         if index is not None and not isinstance(index, (np.ndarray, tuple)):
             index = np.array(index)
-            if index.dtype.kind == 'f':
+            if index.dtype.kind not in np.typecodes["AllInteger"]:
                 index = index.astype(int)
 
         # Ensure assessment is lowercase and capitalized to be consistent
@@ -644,18 +644,22 @@ def get_qc_test_mask(self, var_name=None, test_number=None, qc_var_name=None,
             qc_var_name = self._obj.qcfilter.check_for_ancillary_qc(var_name)
 
         qc_variable = self._obj[qc_var_name].values
+        # Ensure the qc_variable data type is integer. This ensures bitwise comparison
+        # will not cause an error.
+        if qc_variable.dtype.kind not in np.typecodes["AllInteger"]:
+            qc_variable = qc_variable.astype(int)
 
         if flag_value:
-            tripped = np.where(qc_variable == test_number)
+            tripped = qc_variable == test_number
         else:
             check_bit = set_bit(0, test_number) & qc_variable
-            tripped = np.where(check_bit > 0)
+            tripped = check_bit > 0
 
-        test_mask = np.zeros(qc_variable.shape, dtype='int')
+        test_mask = np.full(qc_variable.shape, False, dtype='bool')
         # Make sure test_mask is an array. If qc_variable is scalar will
         # be retuned from np.zeros as scalar.
         test_mask = np.atleast_1d(test_mask)
-        test_mask[tripped] = 1
+        test_mask[tripped] = True
         test_mask = np.ma.make_mask(test_mask, shrink=False)
 
         if return_index:


Test patch:
diff --git a/act/tests/test_qc.py b/act/tests/test_qc.py
--- a/act/tests/test_qc.py
+++ b/act/tests/test_qc.py
@@ -283,6 +283,22 @@ def test_qcfilter2():
         'Bad', 'Indeterminate', 'Bad', 'Indeterminate']
 
 
+def test_qcfilter3():
+    ds_object = read_netcdf(EXAMPLE_IRT25m20s)
+    var_name = 'inst_up_long_dome_resist'
+    result = ds_object.qcfilter.add_test(var_name, index=range(0, 100), test_meaning='testing')
+    qc_var_name = result['qc_variable_name']
+    assert ds_object[qc_var_name].values.dtype.kind in np.typecodes["AllInteger"]
+
+    ds_object[qc_var_name].values = ds_object[qc_var_name].values.astype(np.float32)
+    assert ds_object[qc_var_name].values.dtype.kind not in np.typecodes["AllInteger"]
+
+    result = ds_object.qcfilter.get_qc_test_mask(var_name=var_name, test_number=1, return_index=False)
+    assert np.sum(result) == 100
+    result = ds_object.qcfilter.get_qc_test_mask(var_name=var_name, test_number=1, return_index=True)
+    assert np.sum(result) == 4950
+
+
 def test_qctests():
     ds_object = read_netcdf(EXAMPLE_IRT25m20s)
     var_name = 'inst_up_long_dome_resist'