From 00385bd7cc651ee36215c93563cfd816ee4b72b9 Mon Sep 17 00:00:00 2001
From: Paolo Veglio <paolo.veglio@ssec.wisc.edu>
Date: Thu, 2 Feb 2023 16:21:41 +0000
Subject: [PATCH] repository clean up

---
 .gitignore                                    |   1 +
 dev/conf_xr.py                                | 186 ++++
 dev/hires_module.py                           | 228 +++++
 dev/main_hires.py                             | 125 +++
 dev/ocean_day_tests.py                        | 338 +++++++
 dev/plot_data.py                              | 161 ++++
 dev/plot_mask.py                              | 295 +++++++
 dev/sort_inputs.py                            |  97 ++
 dev/tests.py                                  | 535 ++++++++++++
 dev/write_temp_data.py                        |  24 +
 mvcm/__init__.py                              |   1 +
 mvcm/ancillary.pyx                            | 205 +++++
 mvcm/c_tools/__init__.py                      |   0
 mvcm/c_tools/assign_geos_vals.c               | 268 ++++++
 mvcm/c_tools/bilinearInterpSST.c              | 112 +++
 mvcm/c_tools/check_reg_uniformity.c           | 141 +++
 mvcm/c_tools/cithr.c                          | 230 +++++
 mvcm/c_tools/get_GEOS.c                       | 206 +++++
 mvcm/c_tools/get_NDVI_background.c            | 184 ++++
 mvcm/c_tools/get_Olson_eco.c                  | 141 +++
 mvcm/c_tools/get_Reynolds_SST.c               | 162 ++++
 mvcm/c_tools/get_b1_thresholds.c              | 191 ++++
 mvcm/c_tools/get_geos_times.c                 |  81 ++
 mvcm/c_tools/get_granule_times.c              |  76 ++
 mvcm/c_tools/get_ti_vars.c                    |  91 ++
 mvcm/c_tools/get_ti_vars_geos.c               |  91 ++
 mvcm/c_tools/get_ti_weights.c                 |  69 ++
 mvcm/c_tools/get_ti_weights_geos.c            |  69 ++
 mvcm/c_tools/getcoord.c                       | 650 ++++++++++++++
 mvcm/c_tools/include/ancillary.h              |  41 +
 .../include/mask_processing_constants.h       |  39 +
 mvcm/c_tools/include/sst.h                    |  26 +
 mvcm/c_tools/read_GEOS.c                      | 219 +++++
 mvcm/c_tools/read_GEOS_constants.c            | 200 +++++
 mvcm/c_tools/read_GEOS_lndocn.c               | 187 ++++
 mvcm/c_tools/snow_mask.c                      | 226 +++++
 mvcm/c_tools/swap_bytes.c                     | 126 +++
 mvcm/conf.py                                  | 274 ++++++
 mvcm/main.py                                  | 532 +++++++++++
 mvcm/preprocess_thresholds.py                 | 567 ++++++++++++
 mvcm/read_data.py                             | 581 ++++++++++++
 mvcm/restoral.py                              | 127 +++
 mvcm/scene.py                                 | 446 ++++++++++
 mvcm/spectral_tests.py                        | 826 ++++++++++++++++++
 mvcm/utils.py                                 | 329 +++++++
 pyproject.toml                                |  56 +-
 setup.py                                      |  46 +-
 tests/test_read_data.py                       |  16 +-
 thresholds/thresholds.mvcm.snpp.v0.0.1.yaml   | 706 +++++++++++++++
 unit_tests.py                                 |  97 --
 50 files changed, 10484 insertions(+), 141 deletions(-)
 create mode 100644 dev/conf_xr.py
 create mode 100644 dev/hires_module.py
 create mode 100644 dev/main_hires.py
 create mode 100644 dev/ocean_day_tests.py
 create mode 100644 dev/plot_data.py
 create mode 100644 dev/plot_mask.py
 create mode 100644 dev/sort_inputs.py
 create mode 100644 dev/tests.py
 create mode 100644 dev/write_temp_data.py
 create mode 100644 mvcm/__init__.py
 create mode 100644 mvcm/ancillary.pyx
 create mode 100644 mvcm/c_tools/__init__.py
 create mode 100644 mvcm/c_tools/assign_geos_vals.c
 create mode 100644 mvcm/c_tools/bilinearInterpSST.c
 create mode 100644 mvcm/c_tools/check_reg_uniformity.c
 create mode 100644 mvcm/c_tools/cithr.c
 create mode 100644 mvcm/c_tools/get_GEOS.c
 create mode 100644 mvcm/c_tools/get_NDVI_background.c
 create mode 100644 mvcm/c_tools/get_Olson_eco.c
 create mode 100644 mvcm/c_tools/get_Reynolds_SST.c
 create mode 100644 mvcm/c_tools/get_b1_thresholds.c
 create mode 100644 mvcm/c_tools/get_geos_times.c
 create mode 100644 mvcm/c_tools/get_granule_times.c
 create mode 100644 mvcm/c_tools/get_ti_vars.c
 create mode 100644 mvcm/c_tools/get_ti_vars_geos.c
 create mode 100644 mvcm/c_tools/get_ti_weights.c
 create mode 100644 mvcm/c_tools/get_ti_weights_geos.c
 create mode 100644 mvcm/c_tools/getcoord.c
 create mode 100644 mvcm/c_tools/include/ancillary.h
 create mode 100644 mvcm/c_tools/include/mask_processing_constants.h
 create mode 100644 mvcm/c_tools/include/sst.h
 create mode 100644 mvcm/c_tools/read_GEOS.c
 create mode 100644 mvcm/c_tools/read_GEOS_constants.c
 create mode 100644 mvcm/c_tools/read_GEOS_lndocn.c
 create mode 100644 mvcm/c_tools/snow_mask.c
 create mode 100644 mvcm/c_tools/swap_bytes.c
 create mode 100644 mvcm/conf.py
 create mode 100644 mvcm/main.py
 create mode 100644 mvcm/preprocess_thresholds.py
 create mode 100644 mvcm/read_data.py
 create mode 100644 mvcm/restoral.py
 create mode 100644 mvcm/scene.py
 create mode 100644 mvcm/spectral_tests.py
 create mode 100644 mvcm/utils.py
 create mode 100644 thresholds/thresholds.mvcm.snpp.v0.0.1.yaml
 delete mode 100644 unit_tests.py

diff --git a/.gitignore b/.gitignore
index bc2e71f..f1b919c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
 
 *.vscode
 
+ancillary.c
 viz.py
 
 build
diff --git a/dev/conf_xr.py b/dev/conf_xr.py
new file mode 100644
index 0000000..655f687
--- /dev/null
+++ b/dev/conf_xr.py
@@ -0,0 +1,186 @@
+import numpy as np
+import xarray as xr
+
+
+def test(flipped=False):
+    bt = np.arange(265, 275)
+    if flipped is False:
+        thr = np.array([267, 270, 273, 1, 1])
+    else:
+        thr = np.array([273, 270, 267, 1, 1])
+    c = conf_test(bt, thr)
+    print(c)
+
+
+def test_dble(flipped=False):
+    bt = np.arange(260, 282)
+    if flipped is False:
+        thr = np.array([264, 267, 270, 273, 276, 279, 1, 1])
+    else:
+        thr = np.array([279, 276, 273, 270, 267, 264, 1, 1])
+    c = conf_test_dble(bt, thr)
+    print(c)
+
+
+def conf_test(data, band):
+    '''
+    Assuming a linear function between min and max confidence level, the plot below shows
+    how the confidence (y axis) is computed as function of radiance (x axis).
+    This case illustrates alpha < gamma, obviously in case alpha > gamma, the plot would be
+    flipped.
+                       gamma
+    c  1                 ________
+    o  |                /
+    n  |               /
+    f  |              /
+    i  |     beta    /
+    d 1/2    |....../
+    e  |           /
+    n  |          /
+    c  |         /
+    e  0________/
+       |      alpha
+    --------- radiance ---------->
+    '''
+
+    hicut = data.threshold[:, :, 2]
+    beta = data.threshold[:, :, 1]
+    locut = data.threshold[:, :, 0]
+    power = data.threshold[:, :, 3]
+    coeff = np.power(2, (power - 1))
+
+    gamma = data.threshold.where(hicut > locut, data.threshold[:, :, 0])[:, :, 2]
+    alpha = data.threshold.where(hicut > locut, data.threshold[:, :, 2])[:, :, 0]
+    flipped = xr.zeros_like(data[band]).where(hicut > locut, 1)
+
+    # Rad between alpha and beta
+    range_ = 2. * (beta - alpha)
+    s1 = (data[band].values - alpha)/range_
+    conf_tmp1 = (coeff * np.power(s1, power)).where((data[band] <= beta) & (flipped == 0))
+    conf_tmp2 = (1.0 - coeff * np.power(s1, power)).where((data[band] <= beta) & (flipped == 1))
+    conf_tmp12 = conf_tmp1.where(flipped == 0, conf_tmp2)
+
+    # Rad between beta and gamma
+    range_ = 2. * (beta - gamma)
+    s1 = (data[band].values - gamma)/range_
+    conf_tmp3 = (1.0 - coeff * np.power(s1, power)).where((data[band] <= beta) & (flipped == 0))
+    conf_tmp4 = (coeff * np.power(s1, power)).where((data[band] <= beta) & (flipped == 1))
+    conf_tmp34 = conf_tmp3.where(flipped == 0, conf_tmp4)
+
+    confidence = conf_tmp12.where(data[band] <= beta, conf_tmp34)
+
+    confidence = confidence.where(confidence > 0, 0)
+    confidence = confidence.where(confidence < 1, 1)
+
+    return confidence
+
+
+def conf_test_dble(rad, coeffs):
+    # '''
+    #            gamma1                         gamma2
+    #    c  1_______                               ________
+    #    o  |       \                             /
+    #    n  |        \                           /
+    #    f  |         \                         /
+    #    i  |          \   beta1       beta2   /
+    #    d 1/2          \....|          |...../
+    #    e  |            \                   /
+    #    n  |             \                 /
+    #    c  |              \               /
+    #    e  0               \_____________/
+    #       |             alpha1       alpha2
+    #    --------------------- radiance ------------------------->
+    # '''
+
+    coeffs = np.array(coeffs)
+    radshape = rad.shape
+    rad = rad.reshape(np.prod(radshape))
+    confidence = np.zeros(rad.shape)
+
+    alpha1, gamma1 = np.empty(rad.shape), np.empty(rad.shape)
+    alpha2, gamma2 = np.empty(rad.shape), np.empty(rad.shape)
+
+    if coeffs.ndim == 1:
+        coeffs = np.full((rad.shape[0], 7), coeffs[:7]).T
+
+    gamma1 = coeffs[0, :]
+    beta1 = coeffs[1, :]
+    alpha1 = coeffs[2, :]
+    alpha2 = coeffs[3, :]
+    beta2 = coeffs[4, :]
+    gamma2 = coeffs[5, :]
+    power = coeffs[6, :]
+
+    coeff = np.power(2, (power - 1))
+    # radshape = rad.shape
+    # rad = rad.reshape((rad.shape[0]*rad.shape[1]))
+
+    # ## Find if interval between inner cutoffs passes or fails test
+
+    # Inner region fails test
+
+    # Value is within range of lower set of limits
+    range_ = 2. * (beta1 - alpha1)
+    s1 = (rad - alpha1) / range_
+    idx = np.nonzero((rad <= alpha1) & (rad >= beta1) & (alpha1 - gamma1 > 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    range_ = 2. * (beta1 - gamma1)
+    s1 = (rad - gamma1) / range_
+    idx = np.nonzero((rad >= gamma1) & (rad < beta1) & (alpha1 - gamma1 > 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Value is within range of upper set of limits
+    range_ = 2. * (beta2 - alpha2)
+    s1 = (rad - alpha2) / range_
+    idx = np.nonzero((rad > alpha1) & (rad <= beta2) & (alpha1 - gamma1 > 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    range_ = 2. * (beta2 - gamma2)
+    s1 = (rad - gamma2) / range_
+    idx = np.nonzero((rad > alpha1) & (rad > beta2) & (alpha1 - gamma1 > 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Check for value beyond function range
+    confidence[(alpha1 - gamma1 > 0) & (rad > alpha1) & (rad < alpha2)] = 0
+    confidence[(alpha1 - gamma1 > 0) & ((rad < gamma1) | (rad > gamma2))] = 1
+
+    ###
+
+    # Inner region passes test
+    print("I NEED TO REVIEW THIS TO WRITE IT MORE CLEARLY")
+    # FOR NOW ALPHA AND GAMMA ARE SWITCHED BECAUSE OF HOW THE ARRAYS ARE DEFINED.
+    # THINK ON HOW THIS COULD BE WRITTEN SO THAT IT'S EASIER TO UNDERSTAND (AND DEBUG)
+    # Value is within range of lower set of limits
+    range_ = 2 * (beta1 - alpha1)
+    s1 = (rad - alpha1) / range_
+    idx = np.nonzero((rad > alpha1) & (rad <= gamma1) & (rad <= beta1) & (alpha1 - gamma1 <= 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    range_ = 2 * (beta1 - gamma1)
+    s1 = (rad - gamma1) / range_
+    idx = np.nonzero((rad > alpha1) & (rad <= gamma1) & (rad > beta1) & (alpha1 - gamma1 <= 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Values is within range of upper set of limits
+    range_ = 2 * (beta2 - alpha2)
+    s1 = (rad - alpha2) / range_
+    idx = np.nonzero((rad > gamma2) & (rad < alpha2) & (rad >= beta2) & (alpha1 - gamma1 <= 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    range_ = 2 * (beta2 - gamma2)
+    s1 = (rad - gamma2) / range_
+    idx = np.nonzero((rad > gamma2) & (rad < alpha2) & (rad < beta2) & (alpha1 - gamma1 <= 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    confidence[(alpha1 - gamma1 <= 0) & ((rad > gamma1) | (rad < gamma2))] = 0
+    confidence[(alpha1 - gamma1 <= 0) & (rad <= alpha1) & (rad >= alpha2)] = 1
+
+    confidence[confidence > 1] = 1
+    confidence[confidence < 0] = 0
+
+    return confidence
+
+
+if __name__ == "__main__":
+    test_dble()
diff --git a/dev/hires_module.py b/dev/hires_module.py
new file mode 100644
index 0000000..bbca3fa
--- /dev/null
+++ b/dev/hires_module.py
@@ -0,0 +1,228 @@
+import numpy as np
+import xarray as xr
+import netCDF4 as nc
+import sys
+
+from glob import glob
+
+try:
+    from ruamel import yaml as yml
+except ImportError:
+    import ruamel_yaml as yml
+
+import spectral_tests as tst
+import restoral
+import read_data as rd
+import scene as scn
+
+import importlib
+
+importlib.reload(tst)
+
+# #################################################################### #
+# TEST CASE
+# data:
+_datapath = '/ships19/hercules/pveglio/mvcm_viirs_hires'
+_fname_mod02 = ''
+_fname_mod03 = ''
+_fname_img02 = ''
+_fname_img03 = ''
+
+# MVCM output
+_cld_mask = ''
+
+# thresholds:
+_threshold_file = '/home/pveglio/mvcm/thresholds.mvcm.snpp.v0.0.1.yaml'
+
+# ancillary files:
+_geos_atm_1 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1200.V01.nc4'
+_geos_atm_2 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1500.V01.nc4'
+_geos_land = 'GEOS.fpit.asm.tavg1_2d_lnd_Nx.GEOS5124.20220622_1430.V01.nc4'
+_geos_ocean = 'GEOS.fpit.asm.tavg1_2d_ocn_Nx.GEOS5124.20220622_1430.V01.nc4'
+_geos_constants = 'GEOS.fp.asm.const_2d_asm_Nx.00000000_0000.V01.nc4'
+_ndvi_file = 'NDVI.FM.c004.v2.0.WS.00-04.177.hdf'
+_sst_file = 'oisst.20220622'
+_eco_file = 'goge1_2_img.v1'
+
+# #################################################################### #
+
+
+def main(*,
+         data_path: str = _datapath,
+         mod02: str = _fname_mod02,
+         mod03: str = _fname_mod03,
+         img02: str = _fname_img02,
+         img03: str = _fname_img03,
+         threshold_file: str = _threshold_file,
+         geos_atm_1: str = _geos_atm_1,
+         geos_atm_2: str = _geos_atm_2,
+         geos_land: str = _geos_land,
+         geos_ocean: str = _geos_ocean,
+         geos_constants: str = _geos_constants,
+         ndvi_file: str = _ndvi_file,
+         sst_file: str = _sst_file,
+         out_fname: str = 'temp.nc') -> None:
+
+    check = nc.Dataset(mod02)
+    if 'M01' not in list(check['observation_data'].variables):
+        check.close()
+        return
+
+    if check.isopen():
+        check.close()
+
+    print(threshold_file)
+    with open(threshold_file) as f:
+        text = f.read()
+    thresholds = yml.safe_load(text)
+
+    file_names = {'MOD02': f'{data_path}/{mod02}',
+                  'MOD03': f'{data_path}/{mod03}',
+                  'IMG02': f'{data_path}/{img02}',
+                  'IMG03': f'{data_path}/{img03}',
+                  'GEOS_atm_1': f'{geos_atm_1}',
+                  'GEOS_atm_2': f'{geos_atm_2}',
+                  'GEOS_land': f'{geos_land}',
+                  'GEOS_ocean': f'{geos_ocean}',
+                  'GEOS_constants': f'{geos_constants}',
+                  'NDVI': f'{ndvi_file}',
+                  'SST': f'{sst_file}',
+                  'ANC_DIR': f'{data_path}/ancillary'
+                  }
+
+    sunglint_angle = thresholds['Sun_Glint']['bounds'][3]
+
+    viirs_data = rd.get_data(file_names, sunglint_angle, hires=True)
+    if 'I01' not in viirs_data:
+        return
+
+#    cm_flag = np.array((np.array(cm[0, :, :], np.byte) &
+#                       (pow(2, 1) + pow(2, 2)))/pow(2, 1))
+#
+#    mvcm_conf = np.ones(cm_flag.shape)
+#    # conf[cm_flag == 3] = 1
+#    mvcm_conf[cm_flag == 2] = 0.95
+#    mvcm_conf[cm_flag == 1] = 0.66
+#    mvcm_conf[cm_flag == 0] = 0
+
+    viirs_lores = rd.read_data('viirs', mod02, data_path + '/' + mod03)
+
+    viirs_data['M02'] = (('number_of_lines_2', 'number_of_pixels_2'),
+                         viirs_lores.M02.values.repeat(2, 0).repeat(2, 1))
+    # viirs_data['M07'] = (('number_of_lines_2', 'number_of_pixels_2'),
+    #                      viirs_lores.M07.values.repeat(2, 0).repeat(2, 1))
+    viirs_data['M01'] = (('number_of_lines_2', 'number_of_pixels_2'), np.zeros(viirs_data.I01.shape))
+    viirs_data['M07'] = viirs_data.I02
+    viirs_data['M10'] = viirs_data.I03
+    viirs_data['M12'] = viirs_data.I04
+    viirs_data['M14'] = (('number_of_lines_2', 'number_of_pixels_2'),
+                         viirs_lores.M14.values.repeat(2, 0).repeat(2, 1))
+    viirs_data['M15'] = (('number_of_lines_2', 'number_of_pixels_2'),
+                         viirs_lores.M15.values.repeat(2, 0).repeat(2, 1))
+    viirs_data['M16'] = (('number_of_lines_2', 'number_of_pixels_2'),
+                         viirs_lores.M16.values.repeat(2, 0).repeat(2, 1))
+    viirs_data['M15-M16'] = (('number_of_lines_2', 'number_of_pixels_2'),
+                             viirs_data.M15.values - viirs_data.M16.values)
+    viirs_data['M14-M15'] = (('number_of_lines_2', 'number_of_pixels_2'),
+                             viirs_data.M14.values - viirs_data.M15.values)
+
+    cmin_G1 = np.ones(viirs_data.M15.shape)
+    cmin_G2 = np.ones(viirs_data.M15.shape)
+    cmin_G3 = np.ones(viirs_data.M15.shape)
+
+    MyScene = tst.CloudTests(viirs_data, 'Ocean_Day', thresholds)
+
+    cmin_G1, bit1 = MyScene.test_11um('I05', cmin_G1, test_name='11um_Test')
+    cmin_G1, bit2 = MyScene.sst_test('M15', 'M16', cmin_G1, test_name='SST_Test')
+
+    cmin_G2, bit4 = MyScene.test_11_12um_diff('M15-M16', cmin_G2, test_name='11-12um_Cirrus_Test')
+    cmin_G2, bit3 = MyScene.oceanic_stratus_11_4um_test('I05-I04', cmin_G2,
+                                                        test_name='11-4um_Oceanic_Stratus_Test')
+    # cmin_G2, bit_x = MyScene.bt_diff_86_11um('M14-M15', cmin_G2, test_name='8.6-11um_Test')
+
+    cmin_G3, bit4_1 = MyScene.nir_reflectance_test('M07', cmin_G3, test_name='NIR_Reflectance_Test')
+    cmin_G3, bit5 = MyScene.vis_nir_ratio_test('I02-I01ratio', cmin_G3, test_name='Vis/NIR_Ratio_Test')
+    # visnir, bit5 = MyScene.vis_nir_ratio_test('I02-I01ratio', np.ones(viirs_data.M15.shape), test_name='Vis/NIR_Ratio_Test')
+    cmin_G3, bit6 = MyScene.nir_reflectance_test('M10', cmin_G3, test_name='1.6_2.1um_NIR_Reflectance_Test')
+
+    confidence = np.power(cmin_G1 * cmin_G2 * cmin_G3, 1/3)
+    temp_confidence = np.power(cmin_G1 * cmin_G2 * cmin_G3, 1/3)
+    total_bit = np.zeros(confidence.shape)
+    total_bit = bit1 + bit2 + bit4
+
+    sunglint_angle = thresholds['Sun_Glint']['bounds'][3]
+    scene_flags = scn.find_scene(viirs_data, sunglint_angle)
+
+    idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['ice'] == 0) &
+                     (scene_flags['uniform'] == 1) & (confidence <= 0.99) & (confidence >= 0.05))
+    confidence[idx] = restoral.spatial(viirs_data, thresholds['Sun_Glint'], scene_flags, temp_confidence)[idx]
+
+    temp_confidence = confidence + 0
+    idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['sunglint'] == 1) &
+                     (scene_flags['uniform'] == 1) & (confidence <= 0.95))
+    confidence[idx] = restoral.sunglint(viirs_data, thresholds['Sun_Glint'], total_bit, temp_confidence)[idx]
+
+    # total_confidence = np.sqrt(confidence * mvcm_conf.repeat(2, 0).repeat(2, 1))
+
+    date = mod02.split('.')[1]
+    time = mod02.split('.')[2]
+
+#    mvcm_confidence = mvcm_conf.repeat(2, 0).repeat(2, 1)
+    out_xr = xr.Dataset(
+                data_vars=dict(
+                    hires_confidence=(['number_of_lines', 'number_of_pixels'], confidence),
+                    ocean_day_mask=(['number_of_lines', 'number_of_pixels'], viirs_data.Ocean_Day.values),
+                    ),
+                coords=dict(
+                    latitude=(['number_of_lines', 'number_of_pixels'], viirs_data.latitude.values),
+                    longitude=(['number_of_lines', 'number_of_pixels'], viirs_data.longitude.values),
+                    )
+                )
+    out_path = '/ships19/hercules/pveglio/mvcm_viirs_hires/outputs'
+    out_xr.to_netcdf(f'{out_path}/hires_MVCM.{date}.{time}.nc')
+    # out_xr.to_netcdf(out_fname)
+
+#    # np.savez(f'{data_path}/pyMVCM_{date}.{time}',
+#    np.savez(f'{data_path}/outputs/pyMVCM_{date}.{time}',
+#             confidence=total_confidence, py_conf=confidence, mvcm_conf=mvcm_conf,
+#             lat=viirs_data.latitude.values, lon=viirs_data.longitude.values,
+#             ocean_day_mask=viirs_data.Ocean_Day.values)
+#    # c1=cmin_G1, c2=cmin_G2, c3=cmin_G3)
+
+    # return confidence  # , conf, total_bit
+
+
+def read_bits(byte, bits):
+    # orig_shape = byte.shape
+    # if byte.ndim > 1:
+    #     byte = byte.reshape((byte.shape[0]*byte.shape[1],))
+
+    if type(bits) is int:
+        flag = np.array((np.array(byte, np.byte) & pow(2, bits))/pow(2, bits),
+                        dtype='bool')
+    else:
+        flag = (np.array(byte, np.byte) & pow(2, bits[0]) +
+                pow(2, bits[1]))/pow(2, bits[0])
+
+        flag = np.array(np.trunc(1.0-flag/2.0), dtype='bool')
+    # flag = flag.reshape((orig_shape))
+    return flag
+
+
+if __name__ == "__main__":
+    main(data_path=sys.argv[1],
+         mod02=sys.argv[2],
+         mod03=sys.argv[3],
+         img02=sys.argv[4],
+         img03=sys.argv[5],
+         threshold_file=sys.argv[6],
+         geos_atm_1=sys.argv[7],
+         geos_atm_2=sys.argv[8],
+         geos_land=sys.argv[9],
+         geos_ocean=sys.argv[10],
+         geos_constants=sys.argv[11],
+         ndvi_file=sys.argv[12],
+         sst_file=sys.argv[13],
+         out_fname=sys.argv[14])
+
+
diff --git a/dev/main_hires.py b/dev/main_hires.py
new file mode 100644
index 0000000..e1f421f
--- /dev/null
+++ b/dev/main_hires.py
@@ -0,0 +1,125 @@
+import ruamel_yaml as yml
+import numpy as np
+# import xarray as xr
+
+from glob import glob
+
+import read_data as rd
+import scene as scn
+# import tests
+import ocean_day_tests as odt
+import restoral
+
+# #################################################################### #
+# TEST CASE
+# data:
+_datapath = '/ships19/hercules/pveglio/mvcm_viirs_hires'
+_fname_mod02 = glob(f'{_datapath}/VNP02MOD.A2022173.1454.001.*.uwssec_bowtie_restored.nc')[0]
+_fname_mod03 = glob(f'{_datapath}/VNP03MOD.A2022173.1454.001.*.uwssec.nc')[0]
+_fname_img02 = glob(f'{_datapath}/VNP02IMG.A2022173.1454.001.*.uwssec_bowtie_restored.nc')[0]
+_fname_img03 = glob(f'{_datapath}/VNP03IMG.A2022173.1454.001.*.uwssec.nc')[0]
+
+# thresholds:
+_threshold_file = '/home/pveglio/mvcm_leo/thresholds/new_thresholds.mvcm.snpp.v1.0.0.yaml'
+
+# ancillary files:
+_geos_atm_1 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1200.V01.nc4'
+_geos_atm_2 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1500.V01.nc4'
+_geos_land = 'GEOS.fpit.asm.tavg1_2d_lnd_Nx.GEOS5124.20220622_1430.V01.nc4'
+_geos_ocean = 'GEOS.fpit.asm.tavg1_2d_ocn_Nx.GEOS5124.20220622_1430.V01.nc4'
+_geos_constants = 'GEOS.fp.asm.const_2d_asm_Nx.00000000_0000.V01.nc4'
+_ndvi_file = 'NDVI.FM.c004.v2.0.WS.00-04.177.hdf'
+_sst_file = 'oisst.20220622'
+_eco_file = 'goge1_2_img.v1'
+
+mod_bands = ['M01', 'M02', 'M03', 'M04', 'M06', 'M08', 'M09', 'M11', 'M13', 'M14', 'M15', 'M16']
+
+# #################################################################### #
+
+
+def main(*, data_path=_datapath, mod02=_fname_mod02, mod03=_fname_mod03,
+         img02=_fname_img02, img03=_fname_img03, threshold_file=_threshold_file,
+         geos_atm_1=_geos_atm_1, geos_atm_2=_geos_atm_2, geos_land=_geos_land,
+         geos_ocean=_geos_ocean, geos_constants=_geos_constants, ndvi_file=_ndvi_file, sst_file=_sst_file):
+
+    # datapath = '/ships19/hercules/pveglio/neige_data/snpp_test_input'
+    # fname_l1b = 'VNP02MOD.A2014213.1548.001.2017301015346.uwssec.bowtie_restored_scaled.nc'
+    # fname_geo = 'VNP03MOD.A2014213.1548.001.2017301015705.uwssec.nc'
+    # thresh_file = '/home/pveglio/mvcm_leo/thresholds/new_thresholds.mvcm.snpp.v1.0.0.yaml'
+
+    ancillary_file_names = {'GEOS_atm_1': f'{geos_atm_1}',
+                            'GEOS_atm_2': f'{geos_atm_2}',
+                            'GEOS_land': f'{geos_land}',
+                            'GEOS_ocean': f'{geos_ocean}',
+                            'GEOS_constants': f'{geos_constants}',
+                            'NDVI': f'{ndvi_file}',
+                            'SST': f'{sst_file}',
+                            'ANC_DIR': f'{data_path}/ancillary'
+                            }
+
+    viirs_data = rd.read_data('viirs', f'{mod02}', f'{mod03}')
+    viirs_hires = rd.read_data('viirs', f'{img02}', f'{img03}')
+
+    for bnd in mod_bands:
+        viirs_hires[bnd] = (['number_of_lines', 'number_of_pixels'], viirs_data[bnd].values.repeat(2, 0).repeat(2, 1))
+    viirs_hires = viirs_hires.rename({'I01': 'M05', 'I02': 'M07', 'I03': 'M10', 'I04': 'M12'})
+    print('VIIRS data read')
+
+    viirs_hires = rd.read_ancillary_data(ancillary_file_names, viirs_hires, resolution=2)
+    print('ancillary data read')
+
+    viirs_data = viirs_hires
+
+    with open(threshold_file) as f:
+        text = f.read()
+    thresholds = yml.safe_load(text)
+
+    sunglint_angle = thresholds['Sun_Glint']['bounds'][3]
+    scene_flags = scn.find_scene(viirs_data, sunglint_angle)
+
+    cmin1 = np.ones(viirs_data.M01.shape)
+    cmin2 = np.ones(viirs_data.M01.shape)
+    cmin3 = np.ones(viirs_data.M01.shape)
+    cmin4 = np.ones(viirs_data.M01.shape)
+
+    c = np.ones((9, viirs_data['M01'].shape[0], viirs_data['M01'].shape[1]))
+    cmin1, c[0, :, :], bit1 = odt.simple_test(viirs_data.M15.values, thresholds['Daytime_Ocean']['bt11'], cmin1)
+    # For this test I changed the sst to [7.000, 6.500, 6.000, 1.0, 1.0]
+    cmin1, c[1, :, :], bit2 = odt.sst_test(viirs_data.M15.values, viirs_data.M16.values,
+                                           viirs_data.sensor_zenith.values, viirs_data.geos_sfct.values,
+                                           thresholds['Daytime_Ocean']['sst'], cmin1)
+    cmin2, c[2, :, :], bit3 = odt.simple_test(viirs_data.M14.values-viirs_data.M15.values,
+                                              thresholds['Daytime_Ocean']['diff_11_86um'], cmin2)
+    cmin2, c[3, :, :], bit4 = odt.test_11_12_diff(viirs_data, thresholds['Daytime_Ocean']['diff11_12um'], cmin2)
+    cmin2, c[4, :, :] = odt.test_11_4_diff(viirs_data.I05.values, viirs_data.M12.values,
+                                           thresholds['Daytime_Ocean']['test11_4lo'], scene_flags, cmin2)
+    cmin3, c[5, :, :] = odt.nir_refl_test(viirs_data.M07.values, thresholds['Daytime_Ocean'],
+                                          thresholds['Sun_Glint'], viirs_data, cmin3)
+    cmin3, c[6, :, :] = odt.vis_nir_ratio_test(viirs_data.M05.values, viirs_data.M07.values,
+                                               thresholds, scene_flags, cmin3)
+    cmin3, c[7, :, :] = odt.nir_refl_test(viirs_data.M10.values, thresholds['Daytime_Ocean'],
+                                          thresholds['Sun_Glint'], viirs_data, cmin3)
+
+    total_bit = bit1 + bit2 + bit4
+    temp_confidence = cmin1 * cmin2 * cmin3 * cmin4
+    confidence = cmin1 * cmin2 * cmin3 * cmin4
+    # idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['ice'] == 0) & (scene_flags['uniform'] == 1) &
+    #                  (confidence <= 0.99) & (confidence >= 0.05))
+    # confidence[idx] = restoral.spatial(viirs_data, thresholds['Sun_Glint'], scene_flags, confidence)[idx]
+
+    idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['sunglint'] == 1) &
+                     (scene_flags['uniform'] == 1) & (confidence <= 0.95))
+    confidence[idx] = restoral.sunglint(viirs_data, thresholds['Sun_Glint'], total_bit, temp_confidence)[idx]
+
+    temp = np.zeros((viirs_data.M01.shape[0], viirs_data.M01.shape[1]))
+    temp[idx] = 1
+    c[8, :, :] = temp
+
+    np.savez('test_confidence', confidence=confidence, conf_test=c,
+             lat=viirs_data.latitude.values, lon=viirs_data.longitude.values)
+
+    return confidence
+
+
+if __name__ == "__main__":
+    main()
diff --git a/dev/ocean_day_tests.py b/dev/ocean_day_tests.py
new file mode 100644
index 0000000..6bd8d2c
--- /dev/null
+++ b/dev/ocean_day_tests.py
@@ -0,0 +1,338 @@
+import numpy as np
+
+from numpy.lib.stride_tricks import sliding_window_view
+
+import utils
+import conf
+
+import ancillary_data as c_tools
+
+
+# ############## GROUP 1 TESTS ############## #
+
+# 11 micron brightness temperature threshold test
+def simple_test(rad, threshold, cmin):
+
+    radshape = rad.shape
+    rad = rad.reshape(np.prod(radshape))
+
+    thr = np.array(threshold)
+    confidence = np.ones(rad.shape)
+    bit = np.zeros(rad.shape)
+
+    if thr[4] == 1:
+        print("simple test running")
+        # the C code has the line below that I don't quite understand the purpose of.
+        # It seems to be setting the bit to 0 if the BT value is greater than the midpoint
+        #
+        # if (m31 >= dobt11[1]) (void) set_bit(13, pxout.testbits);
+
+        # confidence = utils.conf_test(rad, thr)
+        confidence = conf.conf_test(rad, thr)
+        bit[rad >= thr[1]] = 1
+
+    return np.minimum(cmin, confidence.reshape(radshape)), confidence.reshape(radshape), bit.reshape(radshape)
+
+
+def sst_test(rad1, rad2, vza, surf_temp, threshold, cmin):
+
+    a1 = 1.8860
+    a2 = 0.9380
+    a3 = 0.1280
+    a4 = 1.0940
+
+    radshape = rad1.shape
+    b31 = rad1.reshape(np.prod(radshape)) - 273.16
+    b32 = rad2.reshape(np.prod(radshape)) - 273.16
+
+    thr = np.array(threshold)
+    confidence = np.ones(b31.shape)
+    bit = np.zeros(b31.shape)
+
+    rad_diff = b31 - b32
+    sstc = surf_temp.reshape(np.prod(radshape)) - 273.16
+    mu = np.cos(vza.reshape(np.prod(radshape)) * np.pi/180.0)
+
+    modsst = 273.16 + a1 + a2*b31 + a3*rad_diff*sstc + a4*rad_diff*((1/mu)-1)
+    sfcdif = surf_temp.reshape(np.prod(radshape)) - modsst
+
+    if thr[4] == 1:
+        print('SST test running')
+        confidence = conf.conf_test(sfcdif, thr)
+        bit[sfcdif < thr[1]] = 1
+
+    return np.minimum(cmin, confidence.reshape(radshape)), confidence.reshape(radshape), bit.reshape(radshape)
+
+
+def test_11_12_diff(data, threshold, cmin):
+    radshape = data.M15.shape
+    b31 = data.M15.values.reshape(np.prod(radshape))
+    b32 = data.M15.values.reshape(np.prod(radshape))
+    vza = data.sensor_zenith.values.reshape(np.prod(radshape))
+    thr = np.array(threshold)
+
+    confidence = np.ones(b31.shape)
+    bit = np.zeros(b31.shape)
+    rad_diff = b31 - b32
+
+    # Get secant of viewing zenith angle
+    dtr = np.pi/180
+    cosvza = np.cos(vza * dtr)
+    schi = np.full(cosvza.shape, 99.0)
+    schi[cosvza > 0.0] = 1.0/cosvza[cosvza > 0.0]
+
+    # Need to define this in cython
+    btd_thr = c_tools.py_cithr(1, np.array(schi, dtype=np.float32), np.array(b31, dtype=np.float32))
+    idx = np.nonzero((btd_thr < 0.1) | (np.abs(schi-99.0) < 0.0001))
+    btd_thr[idx] = thr[0]
+    locut = btd_thr + 0.3*btd_thr
+    hicut = btd_thr - 1.25
+    corr_thr = np.array([locut, btd_thr, hicut, np.ones(locut.shape)], dtype=np.float)
+
+    if thr[1] == 1:
+        print('11-12um diff test running')
+        bit[rad_diff < thr[1]] = 1
+        confidence = conf.conf_test(rad_diff, corr_thr)
+
+    return np.minimum(cmin, confidence.reshape(radshape)), confidence.reshape(radshape), bit.reshape(radshape)
+
+
+def test_11_4_diff(rad1, rad2, threshold, scene_flags, cmin):
+    radshape = rad1.shape
+    b31 = rad1.reshape(np.prod(radshape))
+    b20 = rad2.reshape(np.prod(radshape))
+    thr = np.array(threshold)
+    sunglint = scene_flags['sunglint'].reshape(np.prod(radshape))
+
+    confidence = np.ones(b31.shape)
+
+    if thr[4] == 1:
+        print('11-4um diff test running')
+        confidence[sunglint == 0] = conf.conf_test((b31-b20)[sunglint == 0], thr)
+
+    return np.minimum(cmin, confidence.reshape(radshape)), confidence.reshape(radshape)
+
+
+def vis_nir_ratio_test(rad1, rad2, threshold, scene, cmin):
+    if threshold['Daytime_Ocean']['vis_nir_ratio'][6] == 1:
+        print("NIR-Visible ratio test running")
+
+        radshape = rad1.shape
+        rad1 = rad1.reshape(np.prod(radshape))
+        rad2 = rad2.reshape(np.prod(radshape))
+        sunglint = scene['sunglint'].reshape(np.prod(radshape))
+        vrat = rad2/rad1
+
+        confidence = np.ones(rad1.shape)
+        tmp = threshold['Daytime_Ocean']['vis_nir_ratio']
+        thr_no_sunglint = np.array([tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], 1, 1])
+        tmp = threshold['Sun_Glint']['snglnt']
+        thr_sunglint = np.array([tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], 1])
+        # thr_no_sunglint = np.array(threshold['Daytime_Ocean']['vis_nir_ratio'])
+        # thr_sunglint = np.array(threshold['Sun_Glint']['snglnt'])
+        # thr_sunglint = np.append(thr_sunglint, 1)
+        # temp value to avoid linter bitching at me
+        # eventually we would have the test run in two blocks as:
+        # confidence[sunglint == 1] = conf.conf_test_dble(vrat[sunglint == 1], sg_threshold['snglnt'])
+        # confidence[sunglint == 0] = conf.conf_test_dble(vrat[sunglint == 0], threshold['vis_nir_ratio'])
+        # sunglint needs to be defined somewhere
+        # thr = np.full((rad.shape[0], 4), thr[:4]).T
+
+        # thresh = np.full((rad1.shape[0], thr_no_sunglint.shape[0]), thr_no_sunglint)
+        # thresh[sunglint == 1, :6] = thr_sunglint
+
+        confidence[sunglint == 0] = conf.conf_test_dble(vrat, thr_no_sunglint)[sunglint == 0]
+        confidence[sunglint == 1] = conf.conf_test_dble(vrat, thr_sunglint)[sunglint == 1]
+        # confidence = conf.conf_test_dble(vrat, thresh.T)
+
+        return np.minimum(cmin, confidence.reshape(radshape)), confidence.reshape(radshape)
+
+
+def nir_refl_test(rad, threshold, sunglint_thresholds, viirs_data, cmin):
+
+    print("NIR reflectance test running")
+    sza = viirs_data.solar_zenith.values
+    refang = viirs_data.sunglint_angle.values
+    vza = viirs_data.sensor_zenith.values
+    dtr = np.pi/180
+    # Keep in mind that band_n uses MODIS band numbers (i.e. 2=0.86um and 7=2.1um)
+    # For VIIRS would be 2=M07 (0.865um) and 7=M11 (2.25um)
+    band_n = 2
+    vzcpow = 0.75  # THIS NEEDS TO BE READ FROM THE THRESHOLDS FILE
+
+    radshape = rad.shape
+    rad = rad.reshape(np.prod(radshape))
+    confidence = np.ones(rad.shape)
+    sza = sza.reshape(rad.shape)
+    vza = vza.reshape(rad.shape)
+    refang = refang.reshape(rad.shape)
+    sunglint_flag = utils.sunglint_scene(refang, sunglint_thresholds).reshape(rad.shape)
+
+    # ref2 [5]
+    # b2coeffs [4]
+    # b2mid [1]
+    # b2bias_adj [1]
+    # b2lo [1]
+    # vzcpow [3] (in different place)
+
+    cosvza = np.cos(vza*dtr)
+    coeffs = threshold['b2coeffs']
+    hicut0 = np.array(coeffs[0] + coeffs[1]*sza + coeffs[2]*np.power(sza, 2) + coeffs[3]*np.power(sza, 3))
+    hicut0 = (hicut0 * 0.01) + threshold['b2adj']
+    hicut0 = hicut0 * threshold['b2bias_adj']
+    midpt0 = hicut0 + (threshold['b2mid'] * threshold['b2bias_adj'])
+    locut0 = midpt0 + (threshold['b2lo'] * threshold['b2bias_adj'])
+    thr = np.array([locut0, midpt0, hicut0, threshold['ref2'][3]*np.ones(rad.shape)])
+    corr_thr = np.zeros((4, rad.shape[0]))
+
+    corr_thr[:3, sunglint_flag == 0] = thr[:3, sunglint_flag == 0] * (1./np.power(cosvza[sunglint_flag == 0], vzcpow))
+    corr_thr[3, sunglint_flag == 0] = thr[3, sunglint_flag == 0]
+    # corr_thr[:3, :] = thr[:3, :] * (1./np.power(cosvza[:], vzcpow))
+    # corr_thr[3, :] = thr[3, :]
+
+    for flag in range(1, 4):
+        if len(refang[sunglint_flag == flag]) > 0:
+            sunglint_thr = utils.get_sunglint_thresholds(refang, sunglint_thresholds, band_n, flag, thr)
+            corr_thr[:3, sunglint_flag == flag] = sunglint_thr[:3, sunglint_flag == flag] * (1./np.power(cosvza[sunglint_flag == flag], vzcpow))
+            corr_thr[3, sunglint_flag == flag] = sunglint_thr[3, sunglint_flag == flag]
+
+    confidence = conf.conf_test(rad, corr_thr)
+
+    return np.minimum(cmin, confidence.reshape(radshape)), confidence.reshape(radshape)
+
+
+def nir_high_cloud_test():
+    pass
+
+
+
+
+
+
+
+
+
+
+
+def test_11um_var(rad, threshold, var_threshold):
+
+    print("11um variability test running")
+    thr = np.array(threshold['11um_var'])
+
+    radshape = rad.shape
+    var = np.zeros((radshape[0], radshape[1], 9))
+
+    # chk_spatial2() need to figure out what this is
+    # np = rg_var.num_small_diffs * 1.0
+    test = sliding_window_view(np.pad(rad, [1, 1], mode='constant'), (3, 3)) - np.expand_dims(rad, (2, 3))
+
+    var[np.abs(test).reshape(radshape[0], radshape[1], 9) < var_threshold['dovar11']] = 1
+    var = var.sum(axis=2).reshape(np.prod(radshape))
+
+    rad = rad.reshape(np.prod(radshape))
+    confidence = np.zeros(rad.shape)
+
+    confidence[var == 9] = conf.conf_test(rad[var == 9], thr)
+
+    return confidence.reshape(radshape)
+
+
+def test_11_4diff(rad1, rad2, threshold, viirs_data, sg_thresh):
+
+    print("11um - 4um difference test running")
+    radshape = rad1.shape
+    raddiff = (rad1 - rad2).reshape(np.prod(radshape))
+
+    day = np.zeros(radshape)
+    day[viirs_data.solar_zenith <= 85] = 1
+    day = day.reshape(raddiff.shape)
+    sunglint = np.zeros(rad1.shape)
+    sunglint[viirs_data.sunglint_angle <= sg_thresh] = 1
+    sunglint = sunglint.reshape(raddiff.shape)
+    thr = np.array(threshold['test11_4lo'])
+    confidence = np.zeros(raddiff.shape)
+
+    # confidence[(day == 1) & (sunglint == 0)] = utils.conf_test(raddiff[(day == 1) & (sunglint == 0)], thr)
+    confidence[(day == 1) & (sunglint == 0)] = conf.conf_test(raddiff[(day == 1) & (sunglint == 0)], thr)
+
+    return confidence.reshape(radshape)
+
+
+def vir_refl_test(rad, threshold, viirs_data):
+
+    print('Visible reflectance test running')
+
+    thr = threshold['vis_refl_test']
+
+    radshape = rad.shape()
+    rad = rad.reshape(np.prod(radshape))
+    confidence = np.zeros(radshape)
+    vzcpow = 0.75  # THIS NEEDS TO BE READ FROM THE THRESHOLDS FILE
+
+    vza = viirs_data.sensor_zenith.values
+    dtr = np.pi/180
+    cosvza = np.cos(vza*dtr)
+
+    coeffs = utils.get_b1_thresholds()
+    coeffs[:, :3] = coeffs[:, :3] * threshold['b1_bias_adj']
+
+    # this quantity is the return of get_b1_thresholds() in the C code
+    # it's defined here to keep a consistent logic with the original source, for now
+    irtn = 0
+
+    if irtn != 0:
+        coeffs = thr
+
+    coeffs[:, :3] = coeffs[:, :3] * 1/np.power(cosvza, vzcpow)
+
+    confidence = conf.conf_test(rad, coeffs)
+
+    return confidence.reshape(radshape)
+
+
+
+class CloudMaskTests(object):
+
+    def __init__(self, scene, radiance, coefficients):
+        self.scene = scene
+        self.coefficients = coefficients
+
+    def select_coefficients(self):
+        pass
+
+    def test_G1(self):
+        pass
+
+    def test_G2(self):
+        pass
+
+    def test_G3(self):
+        pass
+
+    def test_G4(self):
+        pass
+
+    def overall_confidence(self):
+        pass
+
+
+def test():
+    rad = np.random.randint(50, size=[4, 8])
+    # coeffs = [5, 42, 20, 28, 15, 35, 1]
+    # coeffs = [20, 28, 5, 42, 15, 35, 1]
+    coeffs = [35, 15, 20, 1, 1]
+    # confidence = conf_test_dble(rad, coeffs)
+    confidence = test_11um(rad, coeffs)
+    print(rad)
+    print('\n')
+    print(confidence)
+
+
+if __name__ == "__main__":
+    test()
+
+
+
+
+
+
diff --git a/dev/plot_data.py b/dev/plot_data.py
new file mode 100644
index 0000000..31358cd
--- /dev/null
+++ b/dev/plot_data.py
@@ -0,0 +1,161 @@
+import matplotlib.pyplot as plt
+import matplotlib
+import cartopy.crs as ccrs
+import numpy as np
+import netCDF4 as nc
+
+import sys
+import os
+
+from scene import test_scene
+
+matplotlib.use('Agg')
+
+
+def main(scene_flag):
+    scn = test_scene()
+    lat = scn['lat']
+    lon = scn['lon']
+
+    plt.figure(figsize=[15, 10])
+    ax = plt.axes(projection=ccrs.PlateCarree())
+    ax.set_extent([np.min(lon)-15, np.max(lon)+15,
+                   np.min(lat)-15, np.max(lat)+15], crs=ccrs.PlateCarree())
+
+    ax.coastlines(resolution='50m', color='black', linewidth=1)
+
+    plt.pcolormesh(lon, lat, scn[scene_flag],
+                   transform=ccrs.PlateCarree())
+
+    plt.show()
+
+
+def plot_tests(numpy_file='test_confidence.npz'):
+    data = np.load(numpy_file)
+    lat = data['lat']
+    lon = data['lon']
+    # confidence = data['confidence']
+    conf_test = data['conf_test']
+    confidence = data['confidence']
+    cm = np.zeros(confidence.shape)
+    cm[confidence > 0.66] = 1
+    cm[confidence > 0.95] = 2
+    cm[confidence > 0.99] = 3
+
+    for i in range(9):
+        print(f'Making plot {i}\n')
+        plt.figure(figsize=[15, 10])
+        ax = plt.axes(projection=ccrs.PlateCarree())
+        ax.set_extent([np.min(lon)-5, np.max(lon)+5,
+                       np.min(lat)-5, np.max(lat)+5], crs=ccrs.PlateCarree())
+
+        ax.coastlines(resolution='50m', color='black', linewidth=1)
+
+        plt.pcolormesh(lon, lat, conf_test[i, :, :], vmin=0, vmax=1,
+                       transform=ccrs.PlateCarree())
+        plt.title(f'Confidence {i}')
+        plt.colorbar()
+
+        plt.savefig(f'figures/confplot_{i}.png')
+
+    plt.figure(figsize=[15, 10])
+    ax = plt.axes(projection=ccrs.PlateCarree())
+    ax.set_extent([np.min(lon)-5, np.max(lon)+5,
+                   np.min(lat)-5, np.max(lat)+5], crs=ccrs.PlateCarree())
+
+    ax.coastlines(resolution='50m', color='black', linewidth=1)
+
+    plt.pcolormesh(lon, lat, cm, vmin=0, vmax=3,
+                   transform=ccrs.PlateCarree())
+    plt.title('Cloud Mask')
+    # plt.colorbar()
+    cb = plt.colorbar(ticks=range(4))
+    cb.set_ticklabels(['confident cloudy', 'probably cloudy', 'probably clear', 'confident clear'])
+
+    plt.savefig('figures/total_cm.png')
+
+
+def plot_confidence(numpy_file='test_confidence.npz'):
+    data = np.load(numpy_file)
+    lat = data['lat']
+    lon = data['lon']
+    # confidence = data['confidence']
+    confidence = data['confidence']
+    cm = np.zeros(confidence.shape)
+    cm[confidence > 0.66] = 1
+    cm[confidence > 0.95] = 2
+    cm[confidence > 0.99] = 3
+
+    plt.figure(figsize=[15, 10])
+    ax = plt.axes(projection=ccrs.PlateCarree())
+    ax.set_extent([np.min(lon)-5, np.max(lon)+5,
+                   np.min(lat)-5, np.max(lat)+5], crs=ccrs.PlateCarree())
+
+    ax.coastlines(resolution='50m', color='black', linewidth=1)
+
+    plt.pcolormesh(lon, lat, cm, vmin=0, vmax=3,
+                   transform=ccrs.PlateCarree())
+    # plt.title('Confidence')
+    # plt.colorbar()
+
+    plt.title('Cloud Mask')
+    cb = plt.colorbar(ticks=range(4))
+    cb.set_ticklabels(['confident cloudy', 'probably cloudy', 'probably clear', 'confident clear'])
+    plt.savefig('figures/python_cloudmask.png', dpi=300)
+
+
+def plot_cloud_mask():
+    # f = nc.Dataset('/ships19/hercules/pveglio/mvcm_viirs_hires/CLDMSK_L2_VIIRS_SNPP.A2022173.1454.001.2022174035130.nc')
+    f = nc.Dataset('/ships19/hercules/pveglio/mvcm_viirs_hires/outputs/CLDMSK_L2_MODIS_SNPP.A2022173.1454.001_viirs_cris.nc')
+    lat = f['geolocation_data/latitude'][:]
+    lon = f['geolocation_data/longitude'][:]
+    # icm = f['geophysical_data/Integer_Cloud_Mask'][:]
+    cm = f['geophysical_data/Cloud_Mask'][:]
+    qa = f['geophysical_data/Quality_Assurance'][:]
+    sst = f['geolocation_data/SST'][:]
+    f.close()
+
+    # cm_flag = np.array((np.array(cm[0, :, :], np.byte) &
+    #                    (pow(2, 1) + pow(2, 2)))/pow(2, 1))
+    # cm_flag = read_bits(qa[:, :, 2], 2) & ~read_bits(cm[2, :, :], 2)
+    cm_flag = read_bits(qa[:, :, 2], 0) & ~read_bits(cm[2, :, :], 0)
+
+    plt.figure(figsize=[15, 10])
+    ax = plt.axes(projection=ccrs.PlateCarree())
+    ax.set_extent([np.min(lon)-5, np.max(lon)+5,
+                   np.min(lat)-5, np.max(lat)+5], crs=ccrs.PlateCarree())
+
+    ax.coastlines(resolution='50m', color='black', linewidth=1)
+
+    plt.pcolormesh(lon, lat, cm_flag, vmin=0, vmax=1,
+                   transform=ccrs.PlateCarree())
+    # cb = plt.colorbar(ticks=range(4))
+    # cb.set_ticklabels(['confident cloudy', 'probably cloudy', 'probably clear', 'confident clear'])
+    cb = plt.colorbar(ticks=range(2))
+    cb.set_ticklabels(['No', 'Yes'])
+    # plt.savefig('figures/ref_cloudmask.png', dpi=300)
+    plt.savefig('figures/1_38um_high_cloud_test.png', dpi=300)
+
+
+def read_bits(byte, bits):
+    # orig_shape = byte.shape
+    # if byte.ndim > 1:
+    #     byte = byte.reshape((byte.shape[0]*byte.shape[1],))
+
+    if type(bits) is int:
+        flag = np.array((np.array(byte, np.byte) & pow(2, bits))/pow(2, bits),
+                        dtype='bool')
+    else:
+        flag = (np.array(byte, np.byte) & pow(2, bits[0]) +
+                pow(2, bits[1]))/pow(2, bits[0])
+
+        flag = np.array(np.trunc(1.0-flag/2.0), dtype='bool')
+    # flag = flag.reshape((orig_shape))
+    return flag
+
+
+if __name__ == "__main__":
+    # main(sys.argv[1])
+    # plot_tests(sys.argv[1])
+    plot_confidence(sys.argv[1])
+    # plot_cloud_mask()
diff --git a/dev/plot_mask.py b/dev/plot_mask.py
new file mode 100644
index 0000000..ff56476
--- /dev/null
+++ b/dev/plot_mask.py
@@ -0,0 +1,295 @@
+import sys
+
+import numpy as np
+import matplotlib.pyplot as plt
+import matplotlib.image as mpimg
+
+#import cartopy.crs as ccrs
+
+from matplotlib import colors
+from netCDF4 import Dataset
+from numpy import less, greater
+
+import argparse
+
+_map_names = ['bit0', 'cloudy', 'cloud_mask_value', 'day', 'sunglint', 'snow_ice',
+         'land', 'thin_cirrus_solar', 'snow_covered_from_anc', 'thin_cirrus_IR',
+         'cloudy_plus_1_pixel_adj', 'ocean_BT_threshold', 'high_cloud_flag_138',
+         'high_cloud_flag_39_12', 'high_cloud_flag_11_12',
+         'cloud_flag_39_11_BTD_test', 'cloud_flag_VisNIR_test',
+         'cloud_flag_VisNIR_ratio', 'shallow_water_clear_sky_restoral',
+         'cloud_flag_16_21_test', 'high_cloud_flag_86_11_BTD_test',
+         'ocean_clear_sky_restoral_test', 'land_sunglint_polar_clearsky_restoral',
+         'cloud_flag_sfc_temp_test', 'cloud_flag_11um_var_test',
+         'low_cloud_flag_39_11BTD_test', 'simple_IR_test',
+         'high_cloud_CO2_test', 'high_cloud_067um_test',
+         'cloud_vis_reflectance_test', 'cloud_near_IR_reflectance_test',
+         'vza_mask_00', 'vza_mask_10', 'vza_mask_30', 'vza_mask_50', 'sct_mask_70',
+         'sct_mask_90', 'sct_mask_110', 'sct_mask_130', 'sct_mask_150',
+         'sct_mask_170', 'sct_mask_180']
+
+
+_map_names = ['cloud_mask_value']
+
+def cmap_discretize(cmap, N, N2=1024):
+    """Return a discrete colormap from the continuous colormap cmap.
+
+        cmap: colormap instance, eg. cm.jet.
+        N: number of colors.
+
+    Example
+        x = resize(arange(100), (5,100))
+        djet = cmap_discretize(cm.jet, 5)
+        imshow(x, cmap=djet)
+    """
+
+    if type(cmap) == str:
+        cmap = cm.get_cmap(cmap)
+
+    colors_i = np.concatenate((np.linspace(0, 1., N), (0., 0., 0., 0.,)))
+    colors_rgba = cmap(colors_i)
+    indices = np.linspace(0, 1., N+1)
+    cdict = {}
+    for ki, key in enumerate(('red', 'green', 'blue')):
+        cdict[key] = [(indices[i], colors_rgba[i-1, ki], colors_rgba[i, ki])
+                      for i in range(N+1)]
+
+    return colors.LinearSegmentedColormap(cmap.name + '_%d' % N, cdict, N2)
+
+
+def main(fname, test_name='',
+         c1='mediumblue', c2='skyblue', c3='yellowgreen', c4='olivedrab'):
+
+    #data = read_data(fname, 18, 21, 102, 107)
+    data = read_data(fname, -90, 90, -180, 180)
+    masks = create_masks(data)
+
+    for test_name in _map_names:
+        print(test_name)
+        x = masks[test_name]
+
+        zz = np.zeros((x.shape[0], x.shape[1], 4), dtype=float)
+
+        ind = np.where(x == 0)
+        zz[ind[0], ind[1], :] = np.array(colors.to_rgba(c1))
+
+        ind = np.where(x == 1)
+        zz[ind[0], ind[1], :] = np.array(colors.to_rgba(c2))
+
+        ind = np.where(x == 2)
+        zz[ind[0], ind[1], :] = np.array(colors.to_rgba(c3))
+
+        ind = np.where(x == 3)
+        zz[ind[0], ind[1], :] = np.array(colors.to_rgba(c4))
+
+        mpimg.imsave(f'{test_name}.png', np.uint8(zz*255))
+
+
+def read_data(filename, lat1, lat2, lon1, lon2):
+    f = Dataset(filename)
+    lat = f['geolocation_data/latitude'][:]
+    lon = f['geolocation_data/longitude'][:]
+    ind = np.nonzero(np.bitwise_and(
+                     np.bitwise_and(lat>lat1, lat<lat2),
+                     np.bitwise_and(lon>lon1, lon<lon2) ))
+    x1, x2, y1, y2 = np.min(ind[0]), np.max(ind[0]), np.min(ind[1]), np.max(ind[1])
+    qa = f['geophysical_data/Quality_Assurance'][x1:x2, y1:y2, :]
+    clearsky_conf = f['geophysical_data/Clear_Sky_Confidence'][x1:x2, y1:y2]
+    cm = f['geophysical_data/Cloud_Mask'][:, x1:x2, y1:y2]
+    vza = f['geolocation_data/sensor_zenith'][x1:x2, y1:y2]
+    sza = f['geolocation_data/solar_zenith'][x1:x2, y1:y2]
+    senaz = f['geolocation_data/sensor_azimuth'][x1:x2, y1:y2]
+    solaz = f['geolocation_data/solar_azimuth'][x1:x2, y1:y2]
+
+    f.close()
+
+    data = {'QA': qa, 'clear_sky_confidence': clearsky_conf,
+            'cloud_mask': cm, 'latitude': lat, 'longitude': lon,
+            'sensor_zenith': vza, 'solar_zenith': sza,
+            'sensor_azimuth': senaz, 'solar_azimuth': solaz}
+
+    return data
+
+
+def create_masks(data):
+    # 2
+    dtr = 3.14159 / 180.0
+    rtd = 180.0 / 3.14159
+    qa = data['QA']
+    cm = data['cloud_mask']
+    vza = data['sensor_zenith']
+    sza = data['solar_zenith']
+    senaz = data['sensor_azimuth']
+    solaz = data['solar_azimuth']
+
+    array_mask = cm[0, :, :].mask
+
+    bit0 = read_bits(cm[0, :, :], 0)
+    # clear_sky_confidence = 0
+
+    # 3
+    cloudy = read_bits(cm[0, :, :], [1, 2])
+#   cloudy = np.array((np.array(cm[0, :, :], np.byte) &
+#                      (pow(2, 1) + pow(2, 2))) / pow(2, 1))
+    cm_flag = np.array((np.array(cm[0, :, :], np.byte) &
+                       (pow(2, 1) + pow(2, 2)))/pow(2, 1))
+
+    # 4
+    day = read_bits(cm[0, :, :], 3)
+    sunglint = ~read_bits(cm[0, :, :], 4)
+    snow_ice = ~read_bits(cm[0, :, :], 5)
+#    land = read_bits(cm[0, :, :], [6, 7])
+    only_land = np.array((np.array(cm[0, :, :], np.byte) &
+                         (pow(2, 6) + pow(2, 7)))/pow(2, 6))
+    only_land = np.array(only_land, dtype='bool') + 0
+
+    # only CM additional info
+    simpleIRtest = read_bits(cm[1, :, :], 5);
+    hicld_CO2 = read_bits(cm[1, :, :], 6);
+    hicld_67um = read_bits(cm[1, :, :], 7);
+    cld_visrefl = read_bits(cm[2, :, :], 4);
+    cld_nearIR = read_bits(cm[2, :, :], 6);
+    # 5
+    thin_cirrus_solar = read_bits(qa[:, :, 1], 1) & ~read_bits(cm[1, :, :], 1)
+    snow_cover = read_bits(qa[:, :, 1], 2) & ~read_bits(cm[1, :, :], 2)
+    thin_cirrus_IR = read_bits(qa[:, :, 1], 3) & ~read_bits(cm[1, :, :], 3)
+    cloudy_plus1 = read_bits(qa[:, :, 1], 4) & ~read_bits(cm[1, :, :], 4)
+    oceanBTthresh = read_bits(qa[:, :, 1], 5) & ~read_bits(cm[1, :, :], 5)
+    hicloud_1_38 = read_bits(qa[:, :, 2], 0) & ~read_bits(cm[2, :, :], 0)
+    hicloud_39_12 = read_bits(qa[:, :, 2], 1) & ~read_bits(cm[2, :, :], 1)
+    hicloud_11_12 = read_bits(qa[:, :, 2], 2) & ~read_bits(cm[2, :, :], 2)
+    cloud_39_11BTD = read_bits(qa[:, :, 2], 3) & ~read_bits(cm[2, :, :], 3)
+    cloud_VisNIR = read_bits(qa[:, :, 2], 4) & ~read_bits(cm[2, :, :], 4)
+    cloud_NIRVis_ratio = read_bits(qa[:, :, 2], 5) & ~read_bits(cm[2, :, :], 5)
+    shallow_clear = read_bits(qa[:, :, 2], 6) & read_bits(cm[2, :, :], 6)
+    cloud_16_21 = read_bits(qa[:, :, 2], 7) & ~read_bits(cm[2, :, :], 7)
+    hicloud_86_11BTD = read_bits(qa[:, :, 3], 0) & ~read_bits(cm[3, :, :], 0)
+    ocean_clear = read_bits(qa[:, :, 3], 1) & read_bits(cm[3, :, :], 1)
+    land_sunglint_polar = read_bits(qa[:, :, 3], 2) & read_bits(cm[3, :, :], 2)
+    cloud_sfctemp = read_bits(qa[:, :, 3], 3) & ~read_bits(cm[3, :, :], 3)
+    cloud_11umvar = read_bits(qa[:, :, 3], 6) & ~read_bits(cm[3, :, :], 6)
+    lowcloud_39_11BTD = read_bits(qa[:, :, 3], 7) & ~read_bits(cm[3, :, :], 7)
+    vzamask00 = (vza[:, :] <= 10).astype(int)
+    vzamask10 = ((vza[:, :] > 10) & (vza[:, :] <= 30)).astype(int)
+    vzamask30 = ((vza[:, :] > 30) & (vza[:, :] <= 50)).astype(int)
+    vzamask50 = (vza[:, :] > 50).astype(int)
+
+    raz = np.abs(180.0 - np.abs(senaz[:, :] - solaz[:, :]))
+    cos_sctang = -1.0 * (np.cos(sza[:, :]*dtr) *np.cos(vza[:, :]*dtr) -
+                         np.sin(sza[:, :]*dtr) *np.sin(vza[:, :]*dtr) *np.cos(raz[:, :]*dtr) )
+    sct = ( np.arccos(cos_sctang[:, :]) * rtd);
+
+    sctmask70 = (sct[:, :] <= 70).astype(int)
+    sctmask90 = ((sct[:, :] > 70) & (sct[:, :] <= 90)).astype(int)
+    sctmask110 = ((sct[:, :] > 90) & (sct[:, :] <= 110)).astype(int)
+    sctmask130 = ((sct[:, :] > 110) & (sct[:, :] <= 130)).astype(int)
+    sctmask150 = ((sct[:, :] > 130) & (sct[:, :] <= 150)).astype(int)
+    sctmask170 = ((sct[:, :] > 150) & (sct[:, :] <= 170)).astype(int)
+    sctmask180 = (sct[:, :] > 170).astype(int)
+
+    masks = {'bit0': bit0,
+             'cloudy': cloudy,
+             'cloud_mask_value': cm_flag,
+             'day': day,
+             'sunglint': sunglint,
+             'snow_ice': snow_ice,
+             'land': only_land,
+             'thin_cirrus_solar': thin_cirrus_solar,
+             'snow_covered_from_anc': snow_cover,
+             'thin_cirrus_IR': thin_cirrus_IR,
+             'cloudy_plus_1_pixel_adj': cloudy_plus1,
+             'ocean_BT_threshold': oceanBTthresh,
+             'high_cloud_flag_138': hicloud_1_38,
+             'high_cloud_flag_39_12': hicloud_39_12,
+             'high_cloud_flag_11_12': hicloud_11_12,
+             'cloud_flag_39_11_BTD_test': cloud_39_11BTD,
+             'cloud_flag_VisNIR_test': cloud_VisNIR,
+             'cloud_flag_VisNIR_ratio': cloud_NIRVis_ratio,
+             'shallow_water_clear_sky_restoral': shallow_clear,
+             'cloud_flag_16_21_test': cloud_16_21,
+             'high_cloud_flag_86_11_BTD_test': hicloud_86_11BTD,
+             'ocean_clear_sky_restoral_test': ocean_clear,
+             'land_sunglint_polar_clearsky_restoral': land_sunglint_polar,
+             'cloud_flag_sfc_temp_test': cloud_sfctemp,
+             'cloud_flag_11um_var_test': cloud_11umvar,
+             'low_cloud_flag_39_11BTD_test': lowcloud_39_11BTD,
+             'simple_IR_test': simpleIRtest,
+             'high_cloud_CO2_test': hicld_CO2,
+             'high_cloud_067um_test': hicld_67um,
+             'cloud_vis_reflectance_test': cld_visrefl,
+             'cloud_near_IR_reflectance_test': cld_nearIR,
+             'vza_mask_00': vzamask00,
+             'vza_mask_10': vzamask10,
+             'vza_mask_30': vzamask30,
+             'vza_mask_50': vzamask50,
+             'sct_mask_70' : sctmask70,
+             'sct_mask_90' : sctmask90,
+             'sct_mask_110' : sctmask110,
+             'sct_mask_130' : sctmask130,
+             'sct_mask_150' : sctmask150,
+             'sct_mask_170' : sctmask170,
+             'sct_mask_180' : sctmask180}
+
+    for k in masks.keys():
+        masks[k] = np.ma.array(masks[k], mask=array_mask)
+
+    return masks
+
+
+def read_bits(byte, bits):
+    # orig_shape = byte.shape
+    # if byte.ndim > 1:
+    #     byte = byte.reshape((byte.shape[0]*byte.shape[1],))
+
+    if type(bits) is int:
+        flag = np.array((np.array(byte, np.byte) & pow(2, bits))/pow(2, bits),
+                        dtype='bool')
+    else:
+        flag = (np.array(byte, np.byte) & pow(2, bits[0]) +
+                         pow(2, bits[1]))/pow(2, bits[0])
+
+        flag = np.array(np.trunc(1.0-flag/2.0), dtype='bool')
+    # flag = flag.reshape((orig_shape))
+    return flag
+
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('input')
+parser.add_argument('test_name')
+parser.add_argument('-c1', default='mediumblue')
+parser.add_argument('-c2', default='skyblue')
+parser.add_argument('-c3', default='yellowgreen')
+parser.add_argument('-c4', default='olivedrab')
+
+args = parser.parse_args()
+
+
+if __name__ == "__main__":
+
+    map_names = ['bit0', 'cloudy', 'cloud_mask_value', 'day', 'sunglint', 'snow_ice',
+             'land', 'thin_cirrus_solar', 'snow_covered_from_anc', 'thin_cirrus_IR',
+             'cloudy_plus_1_pixel_adj', 'ocean_BT_threshold', 'high_cloud_flag_138',
+             'high_cloud_flag_39_12', 'high_cloud_flag_11_12',
+             'cloud_flag_39_11_BTD_test', 'cloud_flag_VisNIR_test',
+             'cloud_flag_VisNIR_ratio', 'shallow_water_clear_sky_restoral',
+             'cloud_flag_16_21_test', 'high_cloud_flag_86_11_BTD_test',
+             'ocean_clear_sky_restoral_test', 'land_sunglint_polar_clearsky_restoral',
+             'cloud_flag_sfc_temp_test', 'cloud_flag_11um_var_test',
+             'low_cloud_flag_39_11BTD_test', 'simple_IR_test',
+             'high_cloud_CO2_test', 'high_cloud_067um_test',
+             'cloud_vis_reflectance_test', 'cloud_near_IR_reflectance_test',
+             'vza_mask_00, vza_mask_10, vza_mask_30, vza_mask_50, sct_mask_70',
+             'sct_mask_90', 'sct_mask_110', 'sct_mask_130', 'vza_mask_150',
+             'sct_mask170', 'sct_mask_180']
+
+#    for n in map_names:
+#        print(n)
+    main(args.input,
+#             n,
+         args.test_name,
+         c1=args.c1,
+         c2=args.c2,
+         c3=args.c3,
+         c4=args.c4)
+
diff --git a/dev/sort_inputs.py b/dev/sort_inputs.py
new file mode 100644
index 0000000..df8a9ac
--- /dev/null
+++ b/dev/sort_inputs.py
@@ -0,0 +1,97 @@
+import os.path
+
+import numpy as np
+
+from datetime import datetime
+from glob import glob
+
+import hires_module as hm
+
+
+def create_filenames(vnp02mod_fname):
+
+    datapath = os.path.dirname(vnp02mod_fname)
+    filename = os.path.basename(vnp02mod_fname)
+
+    vnpdate = filename.split('.')[1]
+    vnptime = filename.split('.')[2]
+
+    geos_times = ['0000', '0300', '0600', '0900', '1200', '1500', '1800', '2100']
+    geos_date_from_viirs = datetime.strftime(datetime.strptime(vnpdate, 'A%Y%j'), '%Y%m%d')
+    geos_flist = glob(f'{datapath}/ancillary/GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.{geos_date_from_viirs}*.nc4')
+    geos_flist = np.sort(geos_flist)
+    fmt = '%H%M'
+    diff_times = [(datetime.strptime(gt, fmt) - datetime.strptime(vnptime, fmt)).total_seconds()
+                  for gt in geos_times]
+
+    file_index = np.argmin(np.abs(diff_times))
+    if diff_times[file_index] <= 0:
+        geos_file1 = geos_flist[file_index]
+        geos_file2 = geos_flist[file_index + 1]
+    else:
+        geos_file1 = geos_flist[file_index - 1]
+        geos_file2 = geos_flist[file_index]
+
+    land_ocean_fnames = glob(f'{datapath}/ancillary/GEOS.fpit.asm.tavg1_2d_lnd_Nx.GEOS5124.{geos_date_from_viirs}*.nc4')
+    land_ocean_timelist = [t.split('.')[5].split('_')[1] for t in land_ocean_fnames]
+
+    diff_times = [(datetime.strptime(gt, fmt) - datetime.strptime(vnptime, fmt)).total_seconds()
+                  for gt in land_ocean_timelist]
+
+    land_ocean_index = np.argmin(np.abs(diff_times))
+    geos_land_flist = glob(f'{datapath}/ancillary/GEOS.fpit.asm.tavg1_2d_lnd_Nx.GEOS5124.{geos_date_from_viirs}*.nc4')
+    geos_land_file = np.sort(geos_land_flist)[land_ocean_index]
+    geos_ocean_flist = glob(f'{datapath}/ancillary/GEOS.fpit.asm.tavg1_2d_ocn_Nx.GEOS5124.{geos_date_from_viirs}*.nc4')
+    geos_ocean_file = np.sort(geos_ocean_flist)[land_ocean_index]
+
+    vnp03mod = glob(f'{datapath}/VNP03MOD.{vnpdate}.{vnptime}*.nc')[0]
+    vnp02img = glob(f'{datapath}/VNP02IMG.{vnpdate}.{vnptime}*_bowtie_corrected.nc')[0]
+    vnp03img = glob(f'{datapath}/VNP03IMG.{vnpdate}.{vnptime}*.nc')[0]
+    cld_msk = glob(f'{datapath}/CLDMSK_L2_VIIRS_SNPP.{vnpdate}.{vnptime}*.nc')[0]
+
+    out_fnames = {'VNP03MOD': os.path.basename(vnp03mod),
+                  'VNP02IMG': os.path.basename(vnp02img),
+                  'VNP03IMG': os.path.basename(vnp03img),
+                  'GEOS_atm_1': os.path.basename(geos_file1),
+                  'GEOS_atm_2': os.path.basename(geos_file2),
+                  'GEOS_land': os.path.basename(geos_land_file),
+                  'GEOS_ocean': os.path.basename(geos_ocean_file),
+                  'GEOS_constants': 'GEOS.fp.asm.const_2d_asm_Nx.00000000_0000.V01.nc4',
+                  'SST_file': 'oisst.20220622',
+                  'ECO_file': 'goge1_2_img.v1',
+                  'NDVI_file': 'NDVI.FM.c004.v2.0.WS.00-04.177.hdf',
+                  'data_path': datapath,
+                  'cloud_mask_file': cld_msk
+                  }
+    for k in list(out_fnames):
+        print(f'{k}: {out_fnames[k]}')
+    return out_fnames
+
+
+def call_mvcm():
+
+    flist = np.sort(glob('/ships19/hercules/pveglio/mvcm_viirs_hires/VNP02MOD*.nc'))
+    # flist = np.sort(glob('/ships19/hercules/pveglio/mvcm_viirs_hires/VNP02MOD.A2022173.1454*.nc'))
+
+    for fname in flist[:210]:
+        print(f'Processing {fname}...')
+        fnames_for_mvcm = create_filenames(fname)
+
+        hm.main(data_path=fnames_for_mvcm['data_path'],
+                mod02=fname,
+                mod03=fnames_for_mvcm['VNP03MOD'],
+                img02=fnames_for_mvcm['VNP02IMG'],
+                img03=fnames_for_mvcm['VNP03IMG'],
+                threshold_file=hm._threshold_file,
+                geos_atm_1=fnames_for_mvcm['GEOS_atm_1'],
+                geos_atm_2=fnames_for_mvcm['GEOS_atm_2'],
+                geos_land=fnames_for_mvcm['GEOS_land'],
+                geos_ocean=fnames_for_mvcm['GEOS_ocean'],
+                geos_constants=fnames_for_mvcm['GEOS_constants'],
+                ndvi_file=fnames_for_mvcm['NDVI_file'],
+                sst_file=fnames_for_mvcm['SST_file'],
+                cloud_mask_file=fnames_for_mvcm['cloud_mask_file'])
+
+
+if __name__ == "__main__":
+    create_filenames
diff --git a/dev/tests.py b/dev/tests.py
new file mode 100644
index 0000000..f4c817a
--- /dev/null
+++ b/dev/tests.py
@@ -0,0 +1,535 @@
+import numpy as np
+import xarray as xr
+
+from numpy.lib.stride_tricks import sliding_window_view
+from typing import Dict
+
+import functools
+
+import utils
+import conf
+import conf_xr
+import preprocess_thresholds as pt
+
+import importlib
+
+_DTR = np.pi/180
+
+# this is used for testing, eventually we want to remove it
+importlib.reload(pt)
+
+
+# ############## GROUP 1 TESTS ############## #
+
+def test_11um_old(rad, threshold):
+
+    radshape = rad.shape
+    rad = rad.reshape(np.prod(radshape))
+
+    thr = np.array(threshold['bt11'])
+    confidence = np.zeros(rad.shape)
+
+    if thr[4] == 1:
+        print("11um test running")
+        # the C code has the line below that I don't quite understand the purpose of.
+        # It seems to be setting the bit to 0 if the BT value is greater than the midpoint
+        #
+        # if (m31 >= dobt11[1]) (void) set_bit(13, pxout.testbits);
+
+        # confidence = utils.conf_test(rad, thr)
+        confidence = conf.conf_test(rad, thr)
+
+    return confidence.reshape(radshape)
+
+
+def test_11um_var(rad, threshold, var_threshold):
+
+    print("11um variability test running")
+    thr = np.array(threshold['11um_var'])
+
+    radshape = rad.shape
+    var = np.zeros((radshape[0], radshape[1], 9))
+
+    # chk_spatial2() need to figure out what this is
+    # np = rg_var.num_small_diffs * 1.0
+    test = sliding_window_view(np.pad(rad, [1, 1], mode='constant'), (3, 3)) - np.expand_dims(rad, (2, 3))
+
+    var[np.abs(test).reshape(radshape[0], radshape[1], 9) < var_threshold['dovar11']] = 1
+    var = var.sum(axis=2).reshape(np.prod(radshape))
+
+    rad = rad.reshape(np.prod(radshape))
+    confidence = np.zeros(rad.shape)
+
+    confidence[var == 9] = conf.conf_test(rad[var == 9], thr)
+
+    return confidence.reshape(radshape)
+
+
+def test_11_4diff(rad1, rad2, threshold, viirs_data, sg_thresh):
+
+    print("11um - 4um difference test running")
+    radshape = rad1.shape
+    raddiff = (rad1 - rad2).reshape(np.prod(radshape))
+
+    day = np.zeros(radshape)
+    day[viirs_data.solar_zenith <= 85] = 1
+    day = day.reshape(raddiff.shape)
+    sunglint = np.zeros(rad1.shape)
+    sunglint[viirs_data.sunglint_angle <= sg_thresh] = 1
+    sunglint = sunglint.reshape(raddiff.shape)
+    thr = np.array(threshold['test11_4lo'])
+    confidence = np.zeros(raddiff.shape)
+
+    # confidence[(day == 1) & (sunglint == 0)] = utils.conf_test(raddiff[(day == 1) & (sunglint == 0)], thr)
+    confidence[(day == 1) & (sunglint == 0)] = conf.conf_test(raddiff[(day == 1) & (sunglint == 0)], thr)
+
+    return confidence.reshape(radshape)
+
+
+def vir_refl_test(rad, threshold, viirs_data):
+
+    print('Visible reflectance test running')
+
+    thr = threshold['vis_refl_test']
+
+    radshape = rad.shape()
+    rad = rad.reshape(np.prod(radshape))
+    confidence = np.zeros(radshape)
+    vzcpow = 0.75  # THIS NEEDS TO BE READ FROM THE THRESHOLDS FILE
+
+    vza = viirs_data.sensor_zenith.values
+    dtr = np.pi/180
+    cosvza = np.cos(vza*dtr)
+
+    coeffs = utils.get_b1_thresholds()
+    coeffs[:, :3] = coeffs[:, :3] * threshold['b1_bias_adj']
+
+    # this quantity is the return of get_b1_thresholds() in the C code
+    # it's defined here to keep a consistent logic with the original source, for now
+    irtn = 0
+
+    if irtn != 0:
+        coeffs = thr
+
+    coeffs[:, :3] = coeffs[:, :3] * 1/np.power(cosvza, vzcpow)
+
+    confidence = conf.conf_test(rad, coeffs)
+
+    return confidence.reshape(radshape)
+
+
+def nir_refl_test(rad, threshold, sunglint_thresholds, viirs_data):
+
+    print("NIR reflectance test running")
+    sza = viirs_data.solar_zenith.values
+    refang = viirs_data.sunglint_angle.values
+    vza = viirs_data.sensor_zenith.values
+    dtr = np.pi/180
+    # Keep in mind that band_n uses MODIS band numbers (i.e. 2=0.86um and 7=2.1um)
+    # For VIIRS would be 2=M07 (0.865um) and 7=M11 (2.25um)
+    band_n = 2
+    vzcpow = 0.75  # THIS NEEDS TO BE READ FROM THE THRESHOLDS FILE
+
+    radshape = rad.shape
+    rad = rad.reshape(np.prod(radshape))
+    confidence = np.zeros(rad.shape)
+    sza = sza.reshape(rad.shape)
+    vza = vza.reshape(rad.shape)
+    refang = refang.reshape(rad.shape)
+    sunglint_flag = utils.sunglint_scene(refang, sunglint_thresholds).reshape(rad.shape)
+
+    # ref2 [5]
+    # b2coeffs [4]
+    # b2mid [1]
+    # b2bias_adj [1]
+    # b2lo [1]
+    # vzcpow [3] (in different place)
+
+    cosvza = np.cos(vza*dtr)
+    coeffs = threshold['b2coeffs']
+    hicut0 = np.array(coeffs[0] + coeffs[1]*sza + coeffs[2]*np.power(sza, 2) + coeffs[3]*np.power(sza, 3))
+    hicut0 = (hicut0 * 0.01) + threshold['b2adj']
+    hicut0 = hicut0 * threshold['b2bias_adj']
+    midpt0 = hicut0 + (threshold['b2mid'] * threshold['b2bias_adj'])
+    locut0 = midpt0 + (threshold['b2lo'] * threshold['b2bias_adj'])
+    thr = np.array([locut0, midpt0, hicut0, threshold['ref2'][3]*np.ones(rad.shape)])
+    # corr_thr = np.zeros((4, 4))
+    corr_thr = np.zeros((4, rad.shape[0]))
+
+    corr_thr[:3, sunglint_flag == 0] = thr[:3, sunglint_flag == 0] * (1./np.power(cosvza[sunglint_flag == 0], vzcpow))
+    corr_thr[3, sunglint_flag == 0] = thr[3, sunglint_flag == 0]
+
+    for flag in range(1, 4):
+        if len(refang[sunglint_flag == flag]) > 0:
+            sunglint_thr = utils.get_sunglint_thresholds(refang, sunglint_thresholds, band_n, flag, thr)
+            corr_thr[:3, sunglint_flag == flag] = sunglint_thr[:3, sunglint_flag == flag] * (1./np.power(cosvza[sunglint_flag == flag], vzcpow))
+            corr_thr[3, sunglint_flag == flag] = sunglint_thr[3, sunglint_flag == flag]
+
+    confidence = conf.conf_test(rad, corr_thr)
+
+    return confidence.reshape(radshape)
+
+
+def vis_nir_ratio_test(rad1, rad2, threshold, sg_threshold):
+    print("NIR-Visible ratio test running")
+    if threshold['vis_nir_ratio'][6] == 1:
+
+        radshape = rad1.shape
+        rad1 = rad1.reshape(np.prod(radshape))
+        rad2 = rad2.reshape(np.prod(radshape))
+
+        vrat = rad2/rad1
+
+        thresh = np.zeros((7,))
+
+        # temp value to avoid linter bitching at me
+        # eventually we would have the test run in two blocks as:
+        # confidence[sunglint == 1] = conf.conf_test_dble(vrat[sunglint == 1], sg_threshold['snglnt'])
+        # confidence[sunglint == 0] = conf.conf_test_dble(vrat[sunglint == 0], threshold['vis_nir_ratio'])
+        # sunglint needs to be defined somewhere
+        sunglint = 0
+        if sunglint:
+            thresh = threshold['snglnt']
+        else:
+            thresh = threshold['vis_nir_ratio']
+
+        confidence = conf.conf_test_dble(vrat, thresh)
+
+        return confidence.reshape(radshape)
+
+
+# old class, doesn't use xarray much
+class CloudTests_old:
+
+    def __init__(self, scene_ids, scene_name, thresholds):
+        self.scene = scene_ids
+        self.scene_name = scene_name
+        self.idx = np.where(scene_ids[scene_name] == 1)
+        self.threshold = thresholds[scene_name]
+
+    def single_threshold_test(self, test_name, rad, cmin):
+
+        radshape = rad.shape
+        rad = rad.reshape(np.prod(radshape))
+
+        thr = np.array(self.threshold[test_name])
+        confidence = np.zeros(radshape)
+
+        if thr[4] == 1:
+            print('test running')
+            confidence[self.idx] = conf.conf_test(rad[self.idx], thr)
+
+        cmin[self.idx] = np.minimum(cmin[self.idx], confidence[self.idx])
+        return cmin
+
+    def double_threshold_test(self):
+        pass
+
+
+# new class to try to use xarray more extensively
+class CloudTests(object):
+
+    def __init__(self,
+                 data: xr.Dataset,
+                 scene_name: str,
+                 thresholds: Dict) -> None:
+        self.data = data
+        self.scene_name = scene_name
+        self.thresholds = thresholds
+        self.scene_idx = tuple(np.nonzero(data[scene_name] == 1))
+
+    def run_if_test_exists_for_scene(func):
+        @functools.wraps(func)
+        def wrapper(self, *args, test_name, **kwargs):
+            if test_name not in self.thresholds[self.scene_name]:
+                print('Not running test for this scene')
+                # returns cmin. This could be changed into a keyworded argument for readability
+                return args[-1]
+            return func(self, *args, test_name, **kwargs)
+        return wrapper
+
+    @run_if_test_exists_for_scene
+    def test_11um(self,
+                  band: str,
+                  cmin: np.ndarray,
+                  test_name: str = '11um_Test') -> np.ndarray:
+        confidence = np.ones(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if threshold['perform'] is True:
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            confidence[self.scene_idx] = conf.conf_test_new(rad, threshold['thr'])
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin
+
+    @run_if_test_exists_for_scene
+    def sst_test(self,
+                 band31: str,
+                 band32: str,
+                 cmin: np.ndarray,
+                 test_name: str = 'SST_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if threshold['perform'] is True:
+            m31 = self.data[band31].values - 273.16
+            bt_diff = self.data[band31].values - self.data[band32].values
+            sst = self.data.geos_sfct.values - 273.16
+            cosvza = np.cos(self.data.sensor_zenith.values*_DTR)
+
+            c = threshold['coeffs']
+
+            modsst = 273.16 + c[0] + c[1]*m31 + c[2]*bt_diff*sst + c[3]*bt_diff*((1/cosvza) - 1)
+            sfcdif = self.data.geos_sfct.values - modsst
+
+            print(f'Testing "{self.scene_name}"\n')
+            confidence[self.scene_idx] = conf.conf_test_new(sfcdif[self.scene_idx], threshold['thr'])
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin
+
+    @run_if_test_exists_for_scene
+    def bt_diff_86_11um(self,
+                        band: str,
+                        cmin: np.ndarray,
+                        test_name: str = '8.6-11um_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if threshold['perform'] is True:
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            confidence[self.scene_idx] = conf.conf_test_new(rad, threshold['thr'])
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin
+
+    def run_tests_temp(self):
+        cmin_G1 = np.ones(self.data.M01.shape)
+        cmin_G2 = np.ones(self.data.M01.shape)
+
+        # Group 1
+        cmin_G1 = self.test_11um('M15', cmin_G1, test_name='11um_Test')
+        cmin_G1 = self.sst_test('M15', 'M16', cmin_G1, test_name='SST_Test')
+
+        # Group 2
+        cmin_G2 = self.bt_diff_86_11um('M14-M15', cmin_G2, '8.6-11um_Test')
+
+        cmin = cmin_G1 * cmin_G2
+
+        return cmin
+
+    def single_threshold_test(self, test_name, band, cmin):
+
+        if band == 'bad_data':
+            return cmin
+        print(f'Running test "{test_name}" for "{self.scene_name}"')
+
+        # preproc_thresholds()
+        if 'thr' in self.thresholds[self.scene_name][test_name]:
+            thr = np.array(self.thresholds[self.scene_name][test_name]['thr'])
+        else:
+            thr = np.array(self.thresholds[self.scene_name][test_name])
+        thr_xr = xr.Dataset()
+
+        if test_name == '11-12um_Cirrus_Test':
+            thr_xr['threshold'] = pt.preproc(self.data, self.thresholds[self.scene_name], self.scene_name)
+            thr = np.ones((5,))  # This is only temporary to force the logic of the code
+                                 # I need to find a better solution at some point
+        elif test_name == 'SST_Test':
+            thr_xr['threshold'] = (('number_of_lines', 'number_of_pixels', 'z'),
+                                   np.ones((self.data[band].shape[0], self.data[band].shape[1], 5))*thr)
+        elif test_name == '7.3-11um_BTD_Mid_Level_Cloud_Test':
+            thr_xr['threshold'] = pt.get_pn_thresholds(self.data, self.thresholds, self.scene_name,
+                                                       '7.3-11um_BTD_Mid_Level_Cloud_Test')
+            thr = np.ones((5,))
+        elif test_name == 'Surface_Temperature_Test':
+            thr_xr['threshold'] = pt.preproc_surf_temp(self.data, self.thresholds[self.scene_name])
+            thr = np.ones((5,))
+        elif (test_name == '11-4um_Oceanic_Stratus_Test' and
+              self.scene_name in ['Land_Day_Desert', 'Land_Day_Desert_Coast', 'Polar_Day_Desert',
+                                  'Polar_Day_Desert_Coast']):
+            thr = np.array([self.thresholds[self.scene_name][test_name][i] for i in range(8)])
+        elif test_name == 'NIR_Reflectance_Test':
+            corr_thr = pt.preproc_nir(self.data, self.thresholds, self.scene_name)
+            thr_xr['threshold'] = (('number_of_lines', 'number_of_pixels', 'z'), corr_thr)
+        elif test_name == 'Visible_Reflectance_Test':
+            thr_xr['threshold'], self.data['M128'] = pt.vis_refl_thresholds(self.data,
+                                                                            self.thresholds,
+                                                                            self.scene_name)
+        elif test_name == '1.6_2.1um_NIR_Reflectance_Test':
+            corr_thr = pt.nir_refl(self.data, self.thresholds, self.scene_name)
+            thr_xr['threshold'] = (('number_of_lines', 'number_of_pixels', 'z'), corr_thr)
+            thr = np.ones((5,))
+        elif test_name == '4-12um_BTD_Thin_Cirrus_Test':
+            thr_xr['threshold'] = pt.get_pn_thresholds(self.data, self.thresholds, self.scene_name,
+                                                       '4-12um_BTD_Thin_Cirrus_Test')
+            thr = np.ones((5,))
+        elif test_name == 'GEMI_Test':
+            thr_xr['threshold'] = pt.GEMI_test(self.data, self.thresholds, self.scene_name)
+            thr = np.ones((5,))
+        elif (test_name == '1.38um_High_Cloud_Test' and self.scene_name in ['Ocean_Day', 'Polar_Ocean_Day']):
+            thr_xr['threshold'] = pt.test_1_38um_preproc(self.data, self.thresholds, self.scene_name)
+            thr = np.ones((5,))
+        else:
+            thr_xr['threshold'] = (('number_of_lines', 'number_of_pixels', 'z'),
+                                   np.ones((self.data[band].shape[0], self.data[band].shape[1], 5))*thr)
+
+        data = xr.Dataset(self.data, coords=thr_xr)
+
+        if test_name == 'SST_Test':
+            data['sfcdif'] = (('number_of_lines', 'number_of_pixels'),
+                              pt.preproc_sst(data, self.thresholds[self.scene_name][test_name]).values)
+            band = 'sfcdif'
+        if test_name == '11um_Variability_Test':
+            var = pt.var_11um(self.data, self.thresholds)
+            data['11um_var'] = data.M15
+            data['11um_var'].values[var != 9] = np.nan
+
+        if thr[4] == 1:
+            print('test running...')
+            confidence = conf_xr.conf_test(data, band)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin
+
+    def double_threshold_test(self, test_name, band, cmin):
+        data = self.data
+        if test_name == '11-4um_BT_Difference_Test':
+            thr = pt.bt11_4um_preproc(self.data, self.thresholds, self.scene_name)
+            print('test running...')
+            confidence = conf.conf_test_dble(data['M15-M13'].values, thr)
+            confidence = confidence.reshape(data.M01.shape)
+
+        if test_name == 'Vis/NIR_Ratio_Test':
+            print('test running...')
+            thr_no_sunglint = np.array([self.thresholds[self.scene_name][test_name][i] for i in range(8)])
+            thr_sunglint = np.array([self.thresholds['Sun_Glint']['snglnt'][i] for i in range(8)])
+            vrat = data.M07.values/data.M05.values
+            _dtr = np.pi/180.0
+            sza = data.sensor_zenith.values
+            raz = data.relative_azimuth.values
+            vza = data.sensor_zenith.values
+            cos_refang = np.sin(vza*_dtr) * np.sin(sza*_dtr) * np.cos(raz*_dtr) + \
+                np.cos(vza*_dtr) * np.cos(sza*_dtr)
+            refang = np.arccos(cos_refang) * 180./np.pi
+            idx = np.nonzero((data.solar_zenith <= 85) & (refang <= data.sunglint_angle))
+
+            confidence = conf.conf_test_dble(vrat, thr_no_sunglint)
+            confidence = confidence.reshape(data.M01.shape)
+            confidence[idx] = conf.conf_test_dble(vrat[idx], thr_sunglint)
+
+            confidence = confidence.reshape(data.M01.shape)
+
+        if (test_name == '11-4um_Oceanic_Stratus_Test' and
+            self.scene_name in ['Land_Day_Desert', 'Land_Day_Desert_Coast', 'Polar_Day_Desert',
+                                'Polar_Day_Desert_Coast']):
+            thr = np.array([self.thresholds[self.scene_name][test_name][i] for i in range(8)])
+
+            print('test running...')
+            confidence = conf.conf_test_dble(data['M15-M16'].values, thr)
+            confidence = confidence.reshape(data.M01.shape)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin
+
+
+class ComputeTests(CloudTests):
+
+    def __init__(self,
+                 data: xr.Dataset,
+                 scene_name: str,
+                 thresholds: Dict) -> None:
+        super().__init__(data, scene_name, thresholds)
+
+    def run_tests(self):
+        cmin_G1 = np.ones(self.data.M01.shape)
+        cmin_G2 = np.ones(self.data.M01.shape)
+
+        # Group 1
+        cmin_G1 = self.test_11um('M15', cmin_G1, test_name='11um_Test')
+        cmin_G1 = self.sst_test('M15', 'M16', cmin_G1, test_name='SST_Test')
+
+        # Group 2
+        cmin_G2 = self.bt_diff_86_11um('M14-M15', cmin_G2, test_name='8.6-11um_Test')
+
+        cmin = cmin_G1 * cmin_G2
+
+        return cmin
+
+
+def preproc_thresholds(thresholds, data):
+    thr = np.array(thresholds)
+    thr_xr = xr.Dataset()
+    thr_xr['tresholds'] = (('number_of_lines', 'number_of_pixels', 'z'),
+                           np.ones((data['M01'].shape[0], data['M01'].shape[1], 5))*thr)
+
+    nl_sfct1 = thresholds['Land_Night']['Surface_Temperature_Test'][0]
+#    nl_sfct2 = thresholds['Land_Night']['Surface_Temperature_Test'][1]
+#    nlsfct_pfm = thresholds['Land_Night']['Surface_Temperature_Test'][2]
+    nl_df1 = thresholds['Land_Night']['Surface_Temperature_Test_difference'][0:2]
+    nl_df2 = thresholds['Land_Night']['Surface_Temperature_Test_difference'][2:]
+
+#    df1 = data.M15 - data.M16
+#    df2 = data.M15 - data.M13
+    thr_xr = thr_xr.where(data.desert != 1, nl_sfct1)
+    thr_xr = thr_xr.where((data['M15-M16'] > nl_df1[0]) |
+                          ((data['M15-M16'] < nl_df1[0]) &
+                           ((data['M15-M13'] <= nl_df2[0]) | (data['M15-M13'] >= nl_df2[1]))),
+                          nl_sfct1[0])
+
+    data = xr.Dataset(data, coords=thr_xr)
+
+    return data
+
+
+def single_threshold_test(test, rad, threshold):
+
+    radshape = rad.shape
+    rad = rad.reshape(np.prod(radshape))
+
+    thr = np.array(threshold[test])
+    confidence = np.zeros(rad.shape)
+
+    if thr[4] == 1:
+        print(f"{test} test running")
+        # the C code has the line below that I don't quite understand the purpose of.
+        # It seems to be setting the bit to 0 if the BT value is greater than the midpoint
+        #
+        # if (m31 >= dobt11[1]) (void) set_bit(13, pxout.testbits);
+
+        # confidence = utils.conf_test(rad, thr)
+        confidence = conf.conf_test(rad, thr)
+
+    return confidence.reshape(radshape)
+
+
+def test():
+    rad = np.random.randint(50, size=[4, 8])
+    # coeffs = [5, 42, 20, 28, 15, 35, 1]
+    # coeffs = [20, 28, 5, 42, 15, 35, 1]
+    coeffs = [35, 15, 20, 1, 1]
+    # confidence = conf_test_dble(rad, coeffs)
+    confidence = test_11um(rad, coeffs)
+    print(rad)
+    print('\n')
+    print(confidence)
+
+
+if __name__ == "__main__":
+    test()
+
+
+
+
+
+
diff --git a/dev/write_temp_data.py b/dev/write_temp_data.py
new file mode 100644
index 0000000..ae60698
--- /dev/null
+++ b/dev/write_temp_data.py
@@ -0,0 +1,24 @@
+import main
+import read_data as rd
+
+
+def main_fcn():
+    file_names = {'MOD02': main._fname_mod02,
+                  'MOD03': main._fname_mod03,
+                  'IMG02': main._fname_img02,
+                  'IMG03': main._fname_img03,
+                  'GEOS_atm_1': main._geos_atm_1,
+                  'GEOS_atm_2': main._geos_atm_2,
+                  'GEOS_land': main._geos_land,
+                  'GEOS_ocean': main._geos_ocean,
+                  'GEOS_constants': main._geos_constants,
+                  'NDVI': main._ndvi_file,
+                  'SST': main._sst_file,
+                  'ANC_DIR': f'{main._datapath}/ancillary'}
+
+    viirs_data = rd.get_data(file_names, 40, hires=False)
+    viirs_data.to_netcdf('viirs_data.nc')
+
+
+if __name__ == "__main__":
+    main_fcn()
diff --git a/mvcm/__init__.py b/mvcm/__init__.py
new file mode 100644
index 0000000..ffcc925
--- /dev/null
+++ b/mvcm/__init__.py
@@ -0,0 +1 @@
+__version__ = '0.0.3'
diff --git a/mvcm/ancillary.pyx b/mvcm/ancillary.pyx
new file mode 100644
index 0000000..73894f8
--- /dev/null
+++ b/mvcm/ancillary.pyx
@@ -0,0 +1,205 @@
+# cython: language_level=3
+# cython: c_string_Type=unicode, c_string_encoding=utf8
+
+cdef extern void get_Reynolds_SST(float *, float *, int, char *, char *, float *)
+cdef extern void get_NDVI_background(float *, float *, int, char *, char *, float *)
+cdef extern void get_Olson_eco(float *, float *, int, char *, unsigned char *)
+cdef extern void get_GEOS(float *, float *, int, char *, char *, char *, char *, char *, char *, char *,
+                          float *, float *, float *, float *, float *, float *)
+cdef extern void snow_mask(char *, unsigned char)
+cdef extern float cithr(int, float, float)
+cdef extern void check_reg_uniformity(int, int, int, int, int, unsigned char, unsigned char *,
+                                      float *, float *, unsigned char *, int *, int *, int *, int *)
+#cdef extern int get_b1_thresholds(float *, float *, float *, float *)
+
+import cython
+from cython.view cimport array as cvarray
+
+import numpy as np
+cimport numpy as np
+
+np.import_array()
+
+ctypedef np.float_t DTYPE_t
+DTYPE = np.float32
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.initializedcheck(False)
+def py_get_Reynolds_SST(np.ndarray[float, ndim=1] lat,
+                        np.ndarray[float, ndim=1] lon, res,
+                        char *anc_dir, char *sst_file, sst):
+
+    # cdef np.ndarray sst = np.zeros((3232*3200, ), order='C', dtype=np.float32)
+    if not sst.flags['C_CONTIGUOUS']:
+        sst = np.ascontiguousarray(sst)
+
+    cdef float[::1] sst_mv = sst
+
+    get_Reynolds_SST(&lat[0], &lon[0], res, anc_dir, sst_file, &sst_mv[0])
+
+    return sst
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.initializedcheck(False)
+def py_get_NDVI_background(np.ndarray[float, ndim=1] lat,
+                           np.ndarray[float, ndim=1] lon, res,
+                           char *anc_dir, char *ndvi_file, ndvi):
+
+    if not ndvi.flags['C_CONTIGUOUS']:
+        ndvi = np.ascontiguousarray(ndvi)
+
+    cdef float[::1] ndvi_mv = ndvi
+
+    get_NDVI_background(&lat[0], &lon[0], res, anc_dir, ndvi_file, &ndvi_mv[0])
+
+    return ndvi
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.initializedcheck(False)
+def py_get_Olson_eco(np.ndarray[float, ndim=1] lat,
+                     np.ndarray[float, ndim=1] lon, res,
+                     char *anc_dir, eco):
+
+    if not eco.flags['C_CONTIGUOUS']:
+        eco = np.ascontiguousarray(eco)
+
+    cdef unsigned char[::1] eco_mv = eco
+
+    get_Olson_eco(&lat[0], &lon[0], res, anc_dir, &eco_mv[0])
+
+    return eco
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.initializedcheck(False)
+def py_get_GEOS(np.ndarray[float, ndim=1] lat, np.ndarray[float, ndim=1] lon, int res, char *startTime,
+                char *anc_dir, char *geos1, char *geos2, char *geos_lnd, char *geos_ocn, char *geos_cnst,
+                geos_data):
+
+    for v in geos_data:
+        if not geos_data[v].flags['C_CONTIGUOUS']:
+            geos_data[v] = np.ascontiguousarray(geos_data[v])
+
+    cdef float[::1] tpw_mv = geos_data['tpw']
+    cdef float[::1] snowfr_mv = geos_data['snow_fraction']
+    cdef float[::1] icefr_mv = geos_data['ice_fraction']
+    cdef float[::1] ocnfr_mv = geos_data['ocean_fraction']
+    cdef float[::1] landicefr_mv = geos_data['land_ice_fraction']
+    cdef float[::1] sfct_mv = geos_data['surface_temperature']
+
+    get_GEOS(&lat[0], &lon[0], res, startTime, anc_dir, geos1, geos2, geos_lnd, geos_ocn, geos_cnst,
+             &tpw_mv[0], &snowfr_mv[0], &icefr_mv[0], &ocnfr_mv[0], &landicefr_mv[0], &sfct_mv[0])
+
+    geos_dict = {'tpw': geos_data['tpw'],
+                 'snow_fraction': geos_data['snow_fraction'],
+                 'ice_fraction': geos_data['ice_fraction'],
+                 'ocean_fraction': geos_data['ocean_fraction'],
+                 'land_ice_fraction': geos_data['land_ice_fraction'],
+                 'surface_temperature': geos_data['surface_temperature']
+                 }
+
+    return geos_dict
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.initializedcheck(False)
+def py_snow_mask(char *satname, unsigned char lsf):
+    # need to have for loop here to compute all the pixels since the function, as with everything else,
+    # is run per pixel.
+    pass
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.initializedcheck(False)
+def py_cithr(int key, np.ndarray[float, ndim=1] sec_vza, np.ndarray[float, ndim=1] bt11):
+
+    cdef np.ndarray tci_thr = np.zeros((bt11.shape[0], ), order='C', dtype=np.float)
+
+    for i in range(bt11.shape[0]):
+        tci_thr[i] = cithr(key, sec_vza[i], bt11[i])
+
+    return tci_thr
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.initializedcheck(False)
+def py_check_reg_uniformity(np.ndarray[unsigned char, ndim=2] eco_type, np.ndarray[unsigned char, ndim=2] eco,
+                            np.ndarray[float, ndim=2] snowfr,
+                            np.ndarray[float, ndim=2] icefr, np.ndarray[unsigned char, ndim=2] lsf):
+
+    cdef int coast, land, water, loc_uniform
+    cdef int i, j
+
+    cdef np.ndarray coast_r = np.zeros((eco_type.shape[0], eco_type.shape[1]), order='C', dtype=np.int32)
+    cdef np.ndarray land_r = np.zeros((eco_type.shape[0], eco_type.shape[1]), order='C', dtype=np.int32)
+    cdef np.ndarray water_r = np.zeros((eco_type.shape[0], eco_type.shape[1]), order='C', dtype=np.int32)
+    cdef np.ndarray loc_uniform_r = np.zeros((eco_type.shape[0], eco_type.shape[1]), order='C', dtype=np.int32)
+    cdef int[:, ::1] coast_mv = coast_r
+    cdef int[:, ::1] land_mv = land_r
+    cdef int[:, ::1] water_mv = water_r
+    cdef int[:, ::1] loc_uniform_mv = loc_uniform_r
+
+    lines = eco_type.shape[0]
+    eles = eco_type.shape[1]
+    for i in range(lines):
+        for j in range(eles):
+            if (i == 0 or i == lines):
+                line_edge = 1
+            else:
+                line_edge = 0
+            if (j == 0 or j == eles):
+                elem_edge = 1
+            else:
+                elem_edge = 0
+            check_reg_uniformity(eles, line_edge, elem_edge, i, j, eco_type[i][j],
+                                &eco[0, 0], &snowfr[0, 0], &icefr[0, 0], &lsf[0, 0],
+                                &coast, &land, &water, &loc_uniform)
+
+            coast_mv[i][j] = coast
+            land_mv[i][j] = land
+            water_mv[i][j] = water
+            loc_uniform_mv[i][j] = loc_uniform
+
+    scene_uniformity = {'coast': coast_r,
+                        'land': land_r,
+                        'water': water_r,
+                        'loc_uniform': loc_uniform_r}
+
+    return scene_uniformity
+
+
+#@cython.boundscheck(False)
+#@cython.wraparound(False)
+#@cython.initializedcheck(False)
+#def py_get_b1_thresholds(np.ndarray[float, ndim=2] locut, np.ndarray[float, ndim=2] midpt,
+#                         np.ndarray[float, ndim=2] hicut, np.ndarray[float, ndim=2] power):
+#
+#    cdef int i, j
+#
+#    cdef np.ndarray_locut_r = np.zeros((), order='C', dtype=np.float32)
+#
+#    cdef float[:, ::1] locut_mv = locut
+#    cdef float[:, ::1] midpt_mv = midpt
+#    cdef float[:, ::1] hicut_mv = hicut
+#    cdef float[:, ::1] power_mv = power
+#    lines = 0
+#    eles = 0
+#
+#    for i in range(lines):
+#        for j in range(eles):
+#
+#            get_b1_thresholds(&locut[0, 0], &midpt[0, 0], &hicut[0, 0], &power[0, 0])
+#
+#            locut_mv[i][j] = locut
+#            midpt_mv[i][j] = midpt
+#            hicut_mv[i][j] = hicut
+#            power_mv[i][j] = power
diff --git a/mvcm/c_tools/__init__.py b/mvcm/c_tools/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/mvcm/c_tools/assign_geos_vals.c b/mvcm/c_tools/assign_geos_vals.c
new file mode 100644
index 0000000..4648c19
--- /dev/null
+++ b/mvcm/c_tools/assign_geos_vals.c
@@ -0,0 +1,268 @@
+/*******************************************************************************
+Description:
+
+  Integer function assign_geos_vals.c
+  Assigns GEOS variable values to each pixel location in the input L1b granule.
+  Also spatially interpolates GEOS values to pixel locations if desired.
+  Called from get_GEOS.c
+
+Input arguments:
+  none
+
+Output arguments:
+  none
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+  Pointers to inputs are stored in ancillary.h.
+  Pointers to output variables are stored in ancillary.h.
+
+Revision History:
+  10/2012 R. Frey  Original version
+  01/2021 R. Frey  Made changes to accomodate sector processing.
+                   Code cleanup.
+  06/2021 R. Frey  Modified from assign_gdas_vals.c
+  06/2021 R. Frey  Added 'snowfr' and 'icefr'.
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//MVCCM includes
+#include "ancillary.h"
+//#include "granule_geolocation.h"
+//#include "granule.h"
+#include "mask_processing_constants.h"
+
+/******************************************************************************/
+
+int assign_geos_vals(float *lat, float *lon, int res)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+    extern double fabs(double);
+
+    const int eles=3200*res;
+    const int lines=3232*res;
+
+    int row, col;
+    int i, j, ij, i1, i2, j1, j2, ic, jc;
+    int j1c, j2c, i1c, i2c;
+    int line1, line2, ele1, ele2;
+    long int n_pixels = eles * lines;
+    long int nwp_index, nwp_index_c;
+    long int nwp_index_c1, nwp_index_c2, nwp_index_c3, nwp_index_c4;
+    long int indx00, indx01, indx10, indx11;
+    long int idx, idxloc;
+    float lst_sum;
+    double x0 = -180.0;
+    double dx = 0.625;
+    double dxc = 0.3125;
+    double y0 = -90.0;
+    double dy = 0.50;
+    double dyc = 0.25;
+    double x, lat1, lat2, lat00, lat01, lat10, lat11, dyi, pyi, py;
+    double y, lon1, lon2, lon00, lon01, lon10, lon11, dxi, pxi, px;
+    double t, tx, ty, t00, t01, t10, t11;
+
+    int return_code = 0;
+
+/******************************************************************************/
+
+//  Allocate memory for assigned data.  Structure variables are stored in
+//  ancillary.h.
+    if ((grn_anc.sfct = (float *) malloc(sizeof(float) * n_pixels)) == NULL) {
+      printf("Could not allocate memory for sfct\n");
+      return(-1);
+    }
+    if ((grn_anc.tpw = (float *) malloc(sizeof(float) * n_pixels)) == NULL) {
+      printf("Could not allocate memory for tpw\n");
+      return(-2);
+    }
+    if ((grn_anc.snowfr = (float *) malloc(sizeof(float) * n_pixels)) == NULL) {
+      printf("Could not allocate memory for snowfr\n");
+      return(-2);
+    }
+    if ((grn_anc.icefr = (float *) malloc(sizeof(float) * n_pixels)) == NULL) {
+      printf("Could not allocate memory for icefr\n");
+      return(-2);
+    }
+    if ((grn_anc.geos_ocnfr = (float *) malloc(sizeof(float) * n_pixels)) == NULL) {
+      printf("Could not allocate memory for geos_ocnfr\n");
+      return(-2);
+    }
+    if ((grn_anc.landicefr = (float *) malloc(sizeof(float) * n_pixels)) == NULL) {
+      printf("Could not allocate memory for landicefr\n");
+      return(-2);
+    }
+
+/******************************************************************************/
+
+//   Get TPW, snow fraction, ice fraction, and bilinearly interpolated surface
+//   temperature.
+
+/******************************************************************************/
+
+// Use 0-based line and element numbers.
+   idx = 0;
+   idxloc = 0;
+   ij = 0;
+
+   for ( row=0; row<lines; ++row) {
+     for ( col=0; col<eles; ++col) {
+
+/******************************************************************************/
+
+//       Find GEOS 0.5-degree grid box corresponding to current location.
+         y = min(max(lat[idx], -90.00), 90.00);
+         j = (int) ((y - y0 + dy) / dy);
+
+         x = min(max(lon[idx], -180.00), 179.375);
+         i = (int) ((x - x0 + dx) / dx);
+
+         nwp_index = (j-1)*nwp_cols + (i-1);
+         if(nwp_index < 0 || nwp_index > nwp_cells) {
+           printf("NWP grid box out of range: %ld\n", nwp_index);
+           return (-3);
+         }
+
+//       Find GEOS 0.25-degree grid box corresponding to current location.
+         jc = (int) ((y - y0 + dyc) / dyc);
+         ic = (int) ((x - x0 + dxc) / dxc);
+         nwp_index_c = ((jc-1)*nwp_cols*2) + (ic-1);
+
+/******************************************************************************/
+//       Don't spatially interpolate the following variables.
+         grn_anc.tpw[idxloc] = *(ti_tpw + nwp_index);
+         grn_anc.snowfr[idxloc] = *(g_snow + nwp_index);
+         grn_anc.icefr[idxloc] = *(g_ice + nwp_index);
+         grn_anc.geos_ocnfr[idxloc] = (*(g_frocn + nwp_index_c));
+         grn_anc.landicefr[idxloc] = (*(g_frlandice + nwp_index_c));
+
+//       Get non-interpolated value in case interpolation is not performed.
+         grn_anc.sfct[idxloc] = *(ti_sfct + nwp_index);
+
+/******************************************************************************/
+
+//       Find four GEOS values that surround the pixel of interest.  Begin by
+//       finding the two surrounding latitude bands and computing the fractional
+//       distance between the pixel and the closest latitude band.
+         lat1 = y0 + ((j-1) * dy);
+         lat2 = y0 + ((j+1) * dy);
+         if( fabs(y-lat1) <= fabs(y-lat2) ) {
+           j1 = j-1;
+           j2 = j;
+         }else {
+           j1 = j;
+           j2 = j+1;
+         }
+         lat00 = y0 + (j1 * dy);
+         lat10 = y0 + (j2 * dy);
+         dyi = lat00 - lat10;
+         pyi = lat00 - y;
+         if(pyi > 0.0) {
+           py = pyi / dyi;
+         }
+         else {
+           py = dy;
+         }
+         if(j1 < 0 || fabs(dyi) < 0.000001 || py > 1.00 || py < 0.00) {
+           printf("problem with j index in spatial interpolation\n");
+           printf("%d %d %ld %d %d %d %f %f %f %f %f %f\n", row,col,idx,j,j1,j2,lat00,lat10,dyi,y,pyi,py);
+         }
+
+//       Find surrounding longitudes and compute the fractional distance between
+//       the pixel of interest and the closest longitude box.
+         lon1 = x0 + ((i-1) * dx);
+         lon2 = x0 + ((i+1) * dx);
+//       if(lon1 < 0.0) lon1 = lon1 + 360.0;
+         if( fabs(x-lon1) < fabs(x-lon2) ) {
+           i1 = i-1;
+           i2 = i;
+         }else {
+           i1 = i;
+           i2 = i+1;
+         }
+
+         lon00 = x0 + (i1 * dx);
+         lon10 = x0 + (i2 * dx);
+         dxi = lon00 - lon10;
+         pxi = lon00 - x;
+         if(pxi > 0.0) {
+           px = pxi/dxi;
+         }
+         else {
+           px = dx;
+         }
+         if(fabs(dxi) < 0.000001 || px > 1.00 || px < 0.00) {
+           printf("problem with i index in spatial interplation\n");
+           printf("%d %d %ld %d %d %d %f %f %f %f %f %f\n", row,col,idx,i,i1,i2,lon00,lon10,dxi,x,pxi,px);
+         }
+
+//       Following are indices of surrounding NWP gridboxes.
+         indx00 = (j1 * nwp_cols) + i1;
+         indx01 = (j1 * nwp_cols) + i2;
+         indx10 = (j2 * nwp_cols) + i1;
+         indx11 = (j2 * nwp_cols) + i2;
+
+//       Interpolate only if region contains only land or water.
+//       Get ocean fraction for the four regions.
+         j1c = (j1*2)-1;
+         j2c = j2*2;
+         i1c = (i1*2)-1;
+         i2c = i2*2;
+         if(j1c < 0) j1c = 1;
+         if(i1c < 0) i1c = 1;
+         nwp_index_c1 = (j1c-1)*nwp_cols*2 + (i1c-1);
+         nwp_index_c2 = (j1c-1)*nwp_cols*2 + (i2c-1);
+         nwp_index_c3 = (j2c-1)*nwp_cols*2 + (i1c-1);
+         nwp_index_c4 = (j2c-1)*nwp_cols*2 + (i2c-1);
+         lst_sum =  *(g_frocn+nwp_index_c1) + *(g_frocn+nwp_index_c2) + *(g_frocn+nwp_index_c3) + *(g_frocn+nwp_index_c4);
+//       printf("\nlat,lon: %f %f %f %f %f %f\n", grn_geo.lat[idx], grn_geo.lon[idx],lat00,lat10,lon00,lon10);
+//       printf("nfrocean1: %d %d %d %d %d %d %d %d\n", j1,j2,i1,i2,j1c,j2c,i1c,i2c);
+//       printf("frocean2: %f %f %f %f %f\n", *(g_frocn+nwp_index_c1),*(g_frocn+nwp_index_c2),*(g_frocn+nwp_index_c3),
+//             *(g_frocn+nwp_index_c4),lst_sum);
+
+         if(lst_sum == 4.0 || lst_sum == 0.0) {
+
+           t00 = ti_sfct[indx00];
+           t01 = ti_sfct[indx01];
+           tx = ((1.0 - px) * t00) + (px * t01);
+
+           t10 = ti_sfct[indx10];
+           t11 = ti_sfct[indx11];
+           ty = ((1.0 - px) * t10) + (px * t11);
+           t = ((1.0 - py) * tx) + (py * ty);
+
+           grn_anc.sfct[idxloc] = t;
+
+//         if(ij < 10)
+//           printf("interp: %f %f %f %f %f %f %f %f\n", lst_sum, t00, t01, tx, t10, t11, ty, t);
+//         ij++;
+
+         }
+
+         idx++;
+         idxloc++;
+
+     }
+   }
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/bilinearInterpSST.c b/mvcm/c_tools/bilinearInterpSST.c
new file mode 100644
index 0000000..cea548d
--- /dev/null
+++ b/mvcm/c_tools/bilinearInterpSST.c
@@ -0,0 +1,112 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h> 
+
+#include "sst.h"
+
+int bilinearInterpSST(int, int, float, float, const SST *, float *);
+
+/* ===== bilinearInterpSST ================================================== */
+/*
+*| Name:
+*|    bilinearInterpSST - Bi-linear interpolation scheme used with Reynolds
+*|                     blended SST data
+*|
+*| Interface:
+*|    int
+*|    bilinearInterpSST (int xindx, int yindx, float lat, float lon, const SST *sstGrid float returnValue)
+*|
+*| Input:
+*|   i         Grid index in the x direction
+*|   j         Grid index in the y direction
+*|   lat       Pixel latitude value
+*|   lon       Pixel longitude value
+*|   sstGrid   The SST grid from the Reynold's SST file
+*|
+*|
+*| Input and Output:
+*|    none
+*|
+*| Output:
+*|    t    Interpolated SST Value
+*|
+*| Return values:
+*|    0        - Success.
+*|   -1        - Unsuccessful
+*|
+*|
+*/
+
+int
+  bilinearInterpSST (int i, int j, float lat, float lon, const SST *sstGrid, float * t)
+{
+
+  extern float fabsf(float);
+
+  int npoints_x = 360;
+  int npoints_y = 180;
+  float dx=1.0;
+  float dy=1.0;
+  int doYInterp = 1;
+  int i1, j1;
+  float p, ip0, ip1, jp0, jp1, t00, t01, t0, t10, t11, t1, ldi, ldj;
+
+  if ( j < 0 || j > (npoints_y -1))
+    return(-1);
+  if ( i < 0 || i > (npoints_x -1))
+    return(-1);
+  
+  ldi = lon - i; 
+  if (ldi >= 0.5) {
+     i1 = i + 1;
+     if ( i == (npoints_x - 1))
+        i1 = 0;
+  }
+  else {
+     i1 = i - 1;
+     if ( i == 0)
+        i1 =  npoints_x - 1;
+  }
+  
+  ldj = lat - (j - 90.0);
+
+  if (ldj >= 0.5) {
+     j1 = j + 1;
+     if ( j == (npoints_y - 1))
+        doYInterp = 0;
+  }
+  else {
+     j1 = j - 1;
+     if ( j == 0)
+        doYInterp = 0;
+  }
+
+  p = lon - (i1 + 0.5);
+  if ( p < -1.0 ) 
+     p = p + 360.0;
+  if ( p > 1.0) 
+     p = p - 360.0;
+  ip0 = fabsf( p / dx);
+  ip1 = dx - ip0;
+
+  t00 = sstGrid->values[j][i];
+  t01 = sstGrid->values[j][i1];
+  t0 = (ip0 * t00) + (ip1 * t01);
+
+  if( doYInterp ) {
+    p = lat - (j1 - 90.0 + 0.5);
+    jp0 = fabsf( p / dy );
+    jp1 = dy - jp0;
+
+    t10 = sstGrid->values[j1][i];
+    t11 = sstGrid->values[j1][i1];
+    t1 = (ip0 * t10) + (ip1 * t11);
+
+    *t = (jp0 * t0) + (jp1 * t1);
+  }
+  else
+    *t = t0;
+
+  return (0);
+}
+
diff --git a/mvcm/c_tools/check_reg_uniformity.c b/mvcm/c_tools/check_reg_uniformity.c
new file mode 100644
index 0000000..5fa882f
--- /dev/null
+++ b/mvcm/c_tools/check_reg_uniformity.c
@@ -0,0 +1,141 @@
+/**********************************************************************
+Description:
+
+  Integer function check_reg_uniformity.c
+  Evaluates surface uniformity.
+  Called from get_pxldat.c.
+
+Input arguments:
+  none
+
+  Inputs from mask_processing_constants.h
+
+Output arguments:
+  none
+
+  int uniform       value of 1 if uniform, otherwise 0
+                    (returned through function call)
+
+Revision History:
+  05/2007  R. Frey  Original version.
+  10/2012  R. Frey  Adapted to MVCCM software
+  11/2015  R. Frey  Code clean-up.
+  06/2021  R. Frey  Uses GEOS snow and ice instead of NISE.
+
+***********************************************************************/
+
+/* Includes */
+
+#include <stdio.h>
+//#include "mask_processing_constants.h"
+//#include "granule.h"
+//#include "pixel.h"
+//#include "ancillary.h"
+//#include "granule_geolocation.h"
+
+void check_reg_uniformity(int eles, int line_edge, int elem_edge, int scan, int elem, unsigned char eco_type,
+                          unsigned char *eco, float *snowfr, float *icefr, unsigned char *lst,
+                          int *land, int *coast, int *water, int *loc_uniform)
+
+
+{
+
+/*  Declarations */
+
+    //int loc_uniform;
+    int nland;
+    int nwater;
+    int ncoast;
+    int ntotal;
+    int nl, ne;
+    int imve, imvl;
+    int pxsnow;
+
+    const int nlcntx = 3;
+    const int necntx = 3;
+    long int j, ide;
+
+    unsigned char lsf;
+
+    float snow_frc, ice_frc;
+
+/*  Initializations */
+
+    nwater = 0;
+    nland = 0;
+    ncoast = 0;
+    ntotal = nlcntx * necntx;
+    *loc_uniform = 1;
+    imve = (necntx-1) / 2;
+    imvl = (nlcntx-1) / 2;
+
+//  Check for edges of granule.
+    if(line_edge == 1 || elem_edge == 1) {
+
+      *loc_uniform = 0;
+
+    }
+    else {
+
+//    Check region (3x3 pixel area centered on current pixel) for scene uniformity.
+
+      for ( nl=0; nl<nlcntx; ++nl) {
+        for ( ne=0; ne<necntx; ++ne) {
+
+    	  j = ( (scan + (nl - imvl)) * eles) + elem;
+    	  ide = j + (ne - imve);
+
+//        Ecosystem consistency.
+    	  if ( (eco_type - *(eco+ide)) != 0) *loc_uniform = 0;
+
+//        Snow/ice consistency.
+    	  snow_frc = *(snowfr+ide);
+    	  ice_frc = *(icefr+ide);
+    	  pxsnow = 0;
+    	  if ( (snow_frc > 0.25 && snow_frc <= 1.0) || (ice_frc > 0.25) ) pxsnow = 1;
+    	  if(pxsnow == 1) *loc_uniform = 0;
+
+//        Count land, water, and coast pixels in region for consistency calculation
+//        below.
+    	  lsf = *(lst+ide);
+    	  if(lsf == 1 || lsf == 4) {
+            nland++;
+    	  }
+    	  else if(lsf == 2 || lsf == 3) {
+    	    ncoast++;
+    	  }
+    	  else {
+    	    nwater++;
+    	  }
+
+        }
+      }
+
+//    Check for coastlines in current region.
+      if( (nwater + ncoast) == ntotal) {
+
+        if(nwater != ntotal) {
+          *loc_uniform = 0;
+//        Provide "double coastlines".
+          *coast = 1;
+          *land = 1;
+          *water = 0;
+        }
+
+      }
+      else if( (nland + ncoast) == ntotal) {
+
+        if(nland != ntotal) *loc_uniform = 0;
+
+      }
+      else {
+
+        *loc_uniform = 0;
+        *coast = 1;
+        *land = 1;
+        *water = 0;
+
+      }
+
+    }
+}
diff --git a/mvcm/c_tools/cithr.c b/mvcm/c_tools/cithr.c
new file mode 100644
index 0000000..93fbd93
--- /dev/null
+++ b/mvcm/c_tools/cithr.c
@@ -0,0 +1,230 @@
+/**********************************************************************
+Description:
+
+  Float function cithr.c      
+  Computes the bi-dimensional linear or quadratic interpolation
+  (lagrange form) of a given value to a table of scan angle and 11 um
+  brightness temperature dependent 11-12 micron brightness temperature
+  differences (thin cirrus cloud test thresholds).  Current look-up
+  table from J. Key (NOAA, CIMSS, UW-Madison).
+
+Input arguments:
+  int key            linear (1) or quadratic (2) interpolation
+  float sec_vza      secant of the viewing zenith angle
+  float bt11         11 micron measured brightness temperature (K)
+
+Output arguments:
+  none
+
+  float tci_thr  thin cirrus cloud test threshold
+                 (returned through function call)
+
+Revision History:
+
+  Original from MODIS Cloud Mask FORTRAN code developed at CIMSS,
+  UW-Madison (tview.f).
+
+  05/2007  R. Frey   Converted to C
+  10/2012  R. Frey   RIncluded in MVCCM software
+
+Calls:
+  none
+
+***********************************************************************/
+
+/* Includes */
+
+#include <stdio.h>
+#include <math.h>
+
+/**********************************************************************/
+
+
+float cithr(int key, float sec_vza, float bt11)
+
+
+/**********************************************************************/
+
+{
+
+/*  Declarations */
+
+    int i,i0,i1,i2,ii;
+    int j,j0,j1,j2,jj;
+    int Iflag;
+
+    float lt0,lt1,lt2,lu0,lu1,lu2;
+    float p,p0,p1,p2;
+    float t,t0,t1,t2;
+    float tci_thr;
+    float u,u0,u1,u2;
+
+    float ttab[13] = {190.0, 200.0, 210.0, 220.0, 230.0, 240.0, 250.0,
+                      260.0, 270.0, 280.0, 290.0, 300.0 ,310.0 };
+
+    float utab[5] = { 2.00, 1.75, 1.50, 1.25, 1.00 };
+
+    float tab[5][13] =
+     {.559,.424,.286,.137,.123,.198,.333,.696,1.217,3.184,5.178,8.269,12.452,
+      .542,.416,.294,.162,.156,.240,.366,.704,1.184,2.926,4.854,7.885,11.985,
+      .520,.405,.305,.194,.199,.294,.409,.715,1.141,2.591,4.433,7.389,11.381,
+      .491,.391,.319,.238,.257,.367,.467,.729,1.083,2.140,3.866,6.720,10.567,
+      .450,.370,.340,.300,.340,.470,.550,.750,1.000,1.500,3.060,5.770, 9.410 };
+  
+      
+/*  Initializations */
+
+    lt0 = 0.0;
+    lt1 = 0.0;
+    lt2 = 0.0;
+    lu0 = 0.0;
+    lu1 = 0.0;
+    lu2 = 0.0;
+    p = 0.0;
+    p0 = 0.0;
+    p1 = 0.0;
+    p2 = 0.0;
+    t = 0.0;
+    t0 = 0.0;
+    t1 = 0.0;
+    t2 = 0.0;
+    u = 0.0;
+    u1 = 0.0;
+    u2 = 0.0;
+    i0 = 0;
+    i1 = 0;
+    i2 = 0;
+    j0 = 0;
+    j1 = 0;
+    j2 = 0;
+    jj = 0;
+    
+/*  Bounds check. */
+
+    u = sec_vza;
+    t = bt11;
+    if (u > utab[0]) u = utab[0];
+    if (u < utab[4]) u = utab[4];
+    if (t < ttab[0]) t = ttab[0];
+    if (t > ttab[12]) t = ttab[12];
+
+    Iflag = 0;
+    i = 1;
+    while(i <= 4) {
+
+      ii = i;
+      if (u >= utab[i] && Iflag == 0) {
+        Iflag = 1;
+        if (key == 1) {
+          i0 = ii - 1;
+          i1 = ii; }
+        else {
+          if (ii == 4) {
+            i0 = ii - 2;
+            i1 = ii - 1;
+            i2 = ii; }
+          else {
+            i0 = ii - 1;
+            i1 = ii;
+            i2 = ii + 1;
+          }
+        }
+      }
+
+      i++;
+
+    }
+
+    Iflag = 0;
+    j = 1;
+    while(j <= 12) {
+
+      jj = j;
+      if (t <= ttab[j] && Iflag == 0) {
+        Iflag = 1;
+        if (key == 1) {
+          j0 = jj - 1;
+          j1 = jj; }
+        else {
+          if (jj == 12) {
+            j0 = jj - 2;
+            j1 = jj - 1;
+            j2 = jj; }
+          else {
+            j0 = jj - 1;
+            j1 = jj;
+            j2 = jj + 1;
+          }
+        }
+      }
+
+      j++;
+
+    }
+
+/*  Branch on interpolation method. */
+
+    if (key == 1) {
+
+/*    linear scheme */
+/*    designate index values */
+
+      u0 = utab[i0];
+      u1 = utab[i1];
+      t0 = ttab[j0];
+      t1 = ttab[j1];
+
+/*    lagrange polynomials */
+      lu0 = (u-u1) / (u0-u1);
+      lu1 = (u-u0) / (u1-u0);
+      lt0 = (t-t1) / (t0-t1);
+      lt1 = (t-t0) / (t1-t0);
+
+/*    interpolating polynomials for the first dimension */
+      p0 = tab[i0][j0]*lu0 + tab[i1][j0]*lu1;
+      p1 = tab[i0][j1]*lu0 + tab[i1][j1]*lu1;
+
+/*    interpolating polynomial for second dimension */
+      p = p0*lt0 + p1*lt1;
+
+//    printf("lin cithr: %f %f %f %f %f %f %f\n", u0,u1,t0,t1,p0,p1,p);
+
+    }
+
+    else {
+
+/*    quadratic scheme */
+/*    designate index values */
+
+      u0 = utab[i0];
+      u1 = utab[i1];
+      u2 = utab[i2];
+      t0 = ttab[j0];
+      t1 = ttab[j1];
+      t2 = ttab[j2];
+      
+/*    lagrange polynomials */
+      lu0 = (u-u1) * (u-u2) / (u0-u1) / (u0-u2);
+      lu1 = (u-u0) * (u-u2) / (u1-u0) / (u1-u2);
+      lu2 = (u-u0) * (u-u1) / (u2-u0) / (u2-u1);
+      lt0 = (t-t1) * (t-t2) / (t0-t1) / (t0-t2);
+      lt1 = (t-t0) * (t-t2) / (t1-t0) / (t1-t2);
+      lt2 = (t-t0) * (t-t1) / (t2-t0) / (t2-t1);
+      
+/*    interpolating polynomials for the first dimension */
+      p0 = tab[i0][j0] * lu0 + tab[i1][j0] * lu1 + tab[i2][j0] * lu2;
+      p1 = tab[i0][j1] * lu0 + tab[i1][j1] * lu1 + tab[i2][j1] * lu2;
+      p2 = tab[i0][j2] * lu0 + tab[i1][j2] * lu1 + tab[i2][j2] * lu2;
+      
+/*    interpolating polynomial for second dimension */
+      p = p0*lt0 + p1*lt1 + p2*lt2;
+
+/*    printf("quad cithr: %f %f %f %f\n", p0,p1,p2,p); */
+
+    }
+
+    tci_thr = p;
+
+    return tci_thr;
+
+}
diff --git a/mvcm/c_tools/get_GEOS.c b/mvcm/c_tools/get_GEOS.c
new file mode 100644
index 0000000..2e0692a
--- /dev/null
+++ b/mvcm/c_tools/get_GEOS.c
@@ -0,0 +1,206 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_GEOS.c
+  Opens and reads selected meteorological variables from two GEOS files.
+  The first file ('geos1') is for a time < 3 hours earlier than the target
+    (granule) time.  The second file ('geos2') is for a time < 3 hours later.
+  Variables are temporally interpolated to the target time.
+  Read snow and ice cover values from GEOS land and ocean files, respectively.
+  Surface temperature is then spatially interpolated.
+
+  Called from collect_inputs.c
+
+Input arguments:
+  geos1            file name of earlier GEOS data time
+  geos2            file name of later GEOS data time
+  geos_lnd         file name of GEOS land properties file (for snow cover)
+  geos_ocn         file name of GEOS ocean properties file (for ice cover)
+
+Output arguments:
+  none
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+  Output parameters stored in header file ancillary.h
+
+Revision History:
+  06/2021 R. Frey  Original version
+
+Calls:
+  read_GEOS.c
+  get_granule_times.c
+  get_geos_times.c
+  get_ti_weights.c
+  assign_geos_vals.c
+
+Comments:
+  Variables from the earlier GEOS time have names ending in "1" (e.g., 'sfct1').
+  Variables from the later GEOS time have names ending in "2" (e.g., 'sfct2').
+  These are stored in ancillary.h.
+
+
+*******************************************************************************/
+
+// Includes
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//MVCCM includes
+//#include "granule_geolocation.h"
+//#include "granule.h"
+#include "ancillary.h"
+
+/******************************************************************************/
+
+void get_GEOS(float *lat, float *lon, int res, char *granule_start_time, char *anc_dir, char *geos1, char *geos2, char *geos_lnd,
+              char *geos_ocn, char *geos_cnst, float *tpw, float *snowfr, float *icefr, float *geos_ocnfr,
+              float *landicefr, float *sfct)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+    extern int get_granule_times(char[64], float*);
+    extern int get_geos_times(char[64], char[64], int*, int*);
+    extern int get_ti_weights(float, int, int, float*);
+    extern int get_ti_vars(float);
+    extern int assign_geos_vals(float *, float *, int);
+    extern int read_GEOS(char[256], int);
+    extern int read_GEOS_lndocn(char[256], char[256]);
+    extern int read_GEOS_constants(char[256]);
+
+    const int eles=3200*res;
+    const int lines=3232*res;
+
+    char geos1_wpath[256];
+    char geos2_wpath[256];
+    char geosland_wpath[256];
+    char geosocean_wpath[256];
+    char geosconstants_wpath[256];
+    char s1[64], s2[64];
+    char search[2];
+    char *token;
+    int irt;
+    int geoshr1;
+    int geoshr2;
+    int GEOS_time_index, ihr, imin;
+    float targhr;
+    float wt;
+
+    int return_code = 0;
+
+/******************************************************************************/
+
+/*  Get complete file names. */
+    strcpy(geos1_wpath,anc_dir);strcat(geos1_wpath,"/");strcat(geos1_wpath,geos1);
+    printf("\nGEOS file1: %s\n", geos1_wpath);
+    strcpy(geos2_wpath,anc_dir);strcat(geos2_wpath,"/");strcat(geos2_wpath,geos2);
+    printf("GEOS file2: %s\n", geos2_wpath);
+    strcpy(geosland_wpath,anc_dir);strcat(geosland_wpath,"/");strcat(geosland_wpath,geos_lnd);
+    printf("GEOS LAND file: %s\n", geosland_wpath);
+    strcpy(geosocean_wpath,anc_dir);strcat(geosocean_wpath,"/");strcat(geosocean_wpath,geos_ocn);
+    printf("GEOS OCEAN file: %s\n", geosocean_wpath);
+    strcpy(geosconstants_wpath,anc_dir);strcat(geosconstants_wpath,"/");strcat(geosconstants_wpath,geos_cnst);
+    printf("GEOS CONSTANTS file: %s\n\n", geosconstants_wpath);
+
+/******************************************************************************/
+
+//  Read GEOS time-independent (constants) file.
+//  Get land/sea tags (fraction ocean) and surface elevation (geopotential ht.).
+    irt = read_GEOS_constants(geosconstants_wpath);
+
+//  Read snow and ice fractions from GEOS land and ocean property files.
+//  Variables listed in read_GEOS_lndocn.c
+//  Pointers to GEOS snow and ice fractions are stored in ancillary.h.
+    irt = read_GEOS_lndocn(geosland_wpath, geosocean_wpath);
+
+//  Read selected variables from GEOS 3-hourly analysis files.
+//  Variables listed in read_GEOS.c
+//  Pointers to GEOS variables are stored in ancillary.h.
+
+    GEOS_time_index = 1;
+    irt = read_GEOS(geos1_wpath, GEOS_time_index);
+    if(irt != 0) {
+      printf("read_GEOS unsuccessful: %s\n", geos1_wpath);
+     // return (-1);
+    }
+
+    GEOS_time_index = 2;
+    irt = read_GEOS(geos2_wpath, GEOS_time_index);
+    if(irt != 0) {
+      printf("read_GEOS unsuccessful: %s\n", geos2_wpath);
+     // return (-2);
+    }
+
+/******************************************************************************/
+
+//  Time-interpolate GEOS grids (pointers to data in ancillary.h).
+
+/******************************************************************************/
+
+//  Get granule start date, hour, and minute.
+    printf("GEOS interpolation target time from L1b: %s\n", granule_start_time);
+
+    (void) strcpy(search, " ");
+    token = strtok(granule_start_time, search);
+    (void) strcpy(search, ":");
+    token = strtok(NULL, search);
+    ihr = atoi(token);
+    token = strtok(NULL, search);
+    imin = atoi(token);
+    targhr = ihr + (imin / 60.0);
+//  printf("hour, min: %d %d %f\n", ihr, imin, targhr);
+
+/******************************************************************************/
+
+//  Get GEOS times from file names, at least for now.
+    (void) strcpy(s1, geos1);
+    (void) strcpy(s2, geos2);
+    irt = get_geos_times(s1, s2, &geoshr1, &geoshr2);
+
+/******************************************************************************/
+
+//  Calculate time-interpolation weights.
+    irt = get_ti_weights(targhr, geoshr1, geoshr2, &wt);
+    printf("Time-interpolation weights: %f %d %d %f\n", targhr, geoshr1, geoshr2, wt);
+
+/******************************************************************************/
+
+//  Perform time interplation. Pointers to inputs and outputs are stored in
+//  ancillary.h.
+    irt = get_ti_vars(wt);
+
+/******************************************************************************/
+
+//  Assign GEOS variable values to each pixel location in the input L1b granule.
+//  Also spatially interpolates GEOS values to pixel locations if desired.
+//  Pointers to inputs and outputs are stored in ancillary.h.
+    irt = assign_geos_vals(lat, lon, res);
+
+/******************************************************************************/
+
+    int i;
+    for (i=0; i<lines*eles; i++) {
+        tpw[i] = grn_anc.tpw[i];
+        snowfr[i] = grn_anc.snowfr[i];
+        icefr[i] = grn_anc.icefr[i];
+        geos_ocnfr[i] = grn_anc.geos_ocnfr[i];
+        landicefr[i] = grn_anc.landicefr[i];
+        sfct[i] = grn_anc.sfct[i];
+    }
+    //int i;
+    //
+    //for (i=0; i<10; i++) {
+    //    printf("i: %d\ttpw: %f\tsnowfr: %f\ticefr: %f\tgeos_ocnfr: %f\tlandicefr: %f\tsfct: %f\n",
+    //            i, tpw[i], snowfr[i], icefr[i], geos_ocnfr[i], landicefr[i], sfct[i]);
+    //}
+    // return ( return_code);
+
+}
+
diff --git a/mvcm/c_tools/get_NDVI_background.c b/mvcm/c_tools/get_NDVI_background.c
new file mode 100644
index 0000000..1a2b28b
--- /dev/null
+++ b/mvcm/c_tools/get_NDVI_background.c
@@ -0,0 +1,184 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_NDVI_background.c
+  Opens NDVI background file and reads the data.
+  Called from collect_inputs.c.
+
+Input arguments:
+  Ancillary data directory
+  Input NDVI file name
+
+Output arguments:
+  none
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+  Output parameters in stored in ancillary.h.
+
+Revision History:
+  10/2012     R. Frey   Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//HDF includes
+#include <hdfi.h>
+#include <hdf.h>
+#include <mfhdf.h>
+#include <HdfEosDef.h>
+
+//MVCCM includes
+//#include "granule_geolocation.h"
+//#include "granule.h"
+//#include "ancillary.h"
+
+/******************************************************************************/
+
+void get_NDVI_background(float *lat, float *lon, int res, char *anc_dir, char *NDVI_file, float *g_ndvi_background)
+
+{
+
+/******************************************************************************/
+
+// Declarations and initializations.
+
+/******************************************************************************/
+
+   char NDVI_file_wpath[256];
+   char NDVI_data[23][80];
+   char SDS_name[10];
+   char * ndvi_sub="/NDVI/";
+   float map_res, wm_lon, nm_lat;
+   //float *g_ndvi_background;
+   long int nncols = 21600;
+   long int nnrows = 10800;
+   long int xindex, yindex, indx, idx;
+   int32 start[2], stride[2], edge[2], sds_index, sds_id, irt, id;
+   int jd, xerr, yerr, row, col, numberRows;
+   int ii, jj;
+   int msgcounta = 0, msgcounta_lim = 100;
+   int msgcountb = 0, msgcountb_lim = 100;
+   int16 *ndvi_arr;
+   int fnday[23] = {1, 17, 33, 49, 65, 81, 97, 113, 129, 145, 161, 177, 193, 209, 225, 241, 257, 273, 289, 305, 321, 337, 353};
+
+   const int eles=3200*res;
+   const int lines=3232*res;
+
+   int return_code = 0;
+
+/******************************************************************************/
+
+// Construct file name.
+
+   (void) strcpy(NDVI_file_wpath, anc_dir);
+   (void) strcat(NDVI_file_wpath, "/");
+   (void) strcat(NDVI_file_wpath, NDVI_file);
+// printf("NDVI file: %s\n", NDVI_file_wpath);
+
+/******************************************************************************/
+
+// Check filename here.
+
+/******************************************************************************/
+
+// Open file. */
+   id = SDstart(NDVI_file_wpath, DFACC_READ);
+   if (id <= 0) {
+     printf("Could not open NDVI file %s\n",NDVI_file_wpath);
+     //return (-1);
+   }
+
+// Select proper SDS and read the data */
+
+   start[0] = 0;
+   start[1] = 0;
+   stride[0] = 1;
+   stride[1] = 1;
+   edge[1] = nncols;
+   edge[0] = nnrows;
+   (void) strcpy(SDS_name, "NDVI");
+
+   ndvi_arr = (int16 *) malloc(21600 * 10800 * 2);
+   sds_index = SDnametoindex(id, SDS_name);
+   sds_id = SDselect(id, sds_index);
+   irt = SDreaddata(sds_id, start, stride, edge, ndvi_arr);
+   if (irt < 0) {
+     printf("Could not read NDVI file %s\n",NDVI_file_wpath);
+     //return (-2);
+   }
+
+// Close NDVI file. */
+   id = SDend(id);
+
+   map_res = 360.0 / (float)(nncols);
+   wm_lon = (map_res / 2.0) - 180.0;
+   nm_lat = 90.0 - (map_res / 2.0);
+
+// Loop through current data and assign NDVI value for each pixel. */
+
+   //g_ndvi_background = (float *)malloc(eles*lines*sizeof(float));
+
+   idx = 0;
+   numberRows = lines;
+   for ( row=0;row<(numberRows );++row) {
+     for ( col=0;col<eles;++col) {
+
+//     Initialize.
+       xerr = 0;
+       yerr = 0;
+       g_ndvi_background[idx] = 32.767;
+
+       xindex = (long int)((lon[idx] - wm_lon) / map_res);
+       yindex = (long int)((nm_lat - lat[idx]) / map_res);
+
+       if(xindex < 0 || xindex> nncols) {
+         xerr = 1;
+         if(msgcounta <= msgcounta_lim){
+           printf("xindex out of bounds on NDVI map! \n");
+           msgcounta += 1;
+         }
+         return_code = 1;
+       }
+       if(yindex < 0 || yindex> nnrows) {
+         yerr = 1;
+         if(msgcountb <= msgcountb_lim){
+           printf("yindex out of bounds on NDVI map! \n");
+           msgcountb += 1;
+         }
+         return_code = 2;
+       }
+
+       if(xerr == 0 && yerr == 0) {
+         indx = (yindex*nncols) + xindex;
+         g_ndvi_background[idx] = *(ndvi_arr + indx) * 0.001;
+
+//       if (idx < 10) {
+//         printf("ndvi %d %d %ld %ld %ld %f %f %d %f\n", row, col, xindex, yindex, indx, grn_geo.lat[idx], grn_geo.lon[idx], *(ndvi_arr + indx), g_ndvi_background[idx]);
+//       }
+
+       }
+
+       idx++;
+
+     }
+   }
+
+   //grn_anc.ndvibk = g_ndvi_background;
+
+   free (ndvi_arr);
+
+/******************************************************************************/
+
+//   return (return_code);
+
+}
diff --git a/mvcm/c_tools/get_Olson_eco.c b/mvcm/c_tools/get_Olson_eco.c
new file mode 100644
index 0000000..31f748f
--- /dev/null
+++ b/mvcm/c_tools/get_Olson_eco.c
@@ -0,0 +1,141 @@
+/*******************************************************************************
+Description:
+
+  integer function get_Olson_eco.c
+  Opens and reads Olson ecosystem file.
+  Called from collect_inputs.c.
+
+Input arguments:
+  anc_dir               ancillary data directory
+
+Output arguments:
+  none
+
+Function output:
+  int return_code       successful completion is zero, otherwise non-zero
+
+  Input file name (static) defined here.
+
+  Output stored in header file ancillary.h.
+
+Revision History:
+  10/2012     R. Frey   Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+
+/******************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//MVCCM includes
+//#include "granule_geolocation.h"
+//#include "granule.h"
+//#include "ancillary.h"
+
+/******************************************************************************/
+
+int get_Olson_eco(float *lat, float *lon, int res, char *anc_dir, unsigned char *granule_ecotype)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+//
+/******************************************************************************/
+
+    extern long int lround(double);
+    extern void getcoord_(double*, double*, double*, double*);
+
+    char eco_file[256];
+    unsigned char eco_index;
+    //unsigned char *granule_ecotype;
+    double lt, ln, ltt, lnn;
+    double drow;
+    double dcol;
+    long int idx, irow, icol, byte_loc;
+    long int max_row = 17347;
+    long int max_col = 40031;
+    int row, col, numberRows;
+
+    const int eles=3200*res;
+    const int lines=3232*res;
+
+    size_t bytes_read;
+    FILE *eco_file_ptr;
+
+//  Define static ecosystem file name.
+    char eco_file_name[] = "goge1_2_img.v1";
+
+    int return_code = 0;
+
+/******************************************************************************/
+
+/*  Get file name */
+    (void) strcpy(eco_file, anc_dir);
+    (void) strcat(eco_file, "/");
+    (void) strcat(eco_file, eco_file_name);
+//  printf("ecosystem file: %s \n", eco_file);
+
+/*  Open file */
+    eco_file_ptr = fopen(eco_file, "r");
+    if(eco_file_ptr == NULL) {
+      printf("Cannot open ecosystem file %s \n", eco_file);
+      //return (-1);
+    }
+
+    //granule_ecotype = (unsigned char *)malloc(eles*lines*sizeof(unsigned char));
+    numberRows = lines;
+    idx = 0;
+    for ( row=0; row<(numberRows); ++row) {
+      for ( col=0; col<eles ;++col) {
+
+//      Get lat/lon
+        lt = (double) lat[idx];
+        ln = (double) lon[idx];
+        ltt = lt;
+        lnn = ln;
+
+//      Get row/col of data grid corresponding to lat/lon.
+        (void) getcoord_(&lt, &ln, &drow, &dcol);
+
+        irow = lround(drow);
+        if(irow > max_row) irow = max_row;
+        if(irow < 1) irow = 1;
+        icol = lround(dcol);
+        if(icol > max_col) icol = max_col;
+        if(icol < 1) icol = 1;
+
+        byte_loc = ( ((irow-1) * max_col) + icol) - 1;
+
+//      Read file
+        fseek(eco_file_ptr, byte_loc, SEEK_SET);
+        bytes_read = fread((char *) &eco_index, sizeof(eco_index), 1, eco_file_ptr);
+
+        granule_ecotype[idx] = eco_index;
+
+//      if(idx < 10)
+//        printf("Olson ecosystem index: %d %d\n", idx, eco_index);
+
+         ++idx;
+
+      }
+    }
+
+/*  Close file */
+    (void) fclose(eco_file_ptr);
+
+    //grn_anc.eco = granule_ecotype;
+
+/******************************************************************************/
+
+    //return (return_code);
+
+}
diff --git a/mvcm/c_tools/get_Reynolds_SST.c b/mvcm/c_tools/get_Reynolds_SST.c
new file mode 100644
index 0000000..b288e6f
--- /dev/null
+++ b/mvcm/c_tools/get_Reynolds_SST.c
@@ -0,0 +1,162 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_Reynolds_SST
+  Opens and reads SSTs from Reynolds SST files.
+  Called from collect_inputs.c.
+
+Input arguments:
+  ancillary data directory
+  Reynolds SST file name
+
+Output arguments:
+  none
+
+Function output:
+  int return_code    successful completion is zero, otherwise non-zero
+
+  Output parameters stored in ancillary.h
+
+Revision History:
+  10/2012  R. Frey   Original version
+
+Calls:
+  bilinearInterpSST
+  swapbyte4
+
+*******************************************************************************/
+
+// Includes
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//MVCCM includes
+//#include "granule.h"
+#include "sst.h"
+
+/******************************************************************************/
+
+void get_Reynolds_SST(float *lat, float *lon, int res, char *anc_dir, char *SST_file, float *sstInterp)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+/******************************************************************************/
+
+   extern int bilinearInterpSST(int, int, float, float, const SST *, float *);
+   extern int swapbyte4(void *, int);
+   extern float fmaxf(float, float);
+   extern float fminf(float, float);
+
+   /* number of points around a latitude circle */
+   long int npoints_x = 360;
+   /* number of grid points along a meridian */
+   long int npoints_y = 180;
+   /* the 2 possible sizes of an SST file (formatted/unformatted) */
+   long int unfSize = 583264;
+   char sst_file_wpath[256];
+
+   char fileFormat[4] = "unk";
+   float xlon, ytemp;
+   //float * sstInterp;
+   float tempVal;
+   long int fileSize;
+   long int indx;
+   int i, j, xindx, yindx;
+   int ok;
+
+   const int eles=3200*res;
+   const int lines=3232*res;
+
+   FILE * sst_file_ptr;
+   size_t bytes_read;
+   SST sstGrid;
+
+   int return_code = 0;
+
+/******************************************************************************/
+
+   strcpy(sst_file_wpath, anc_dir);
+   strcat(sst_file_wpath, "/");
+   strcat(sst_file_wpath, SST_file);
+// printf("SST file: %s \n", sst_file_wpath);
+
+   /*  Open file */
+   sst_file_ptr = fopen(sst_file_wpath, "rb");
+   if(sst_file_ptr == NULL) {
+      printf("Cannot open Reynolds SST file %s\n", sst_file_wpath);
+  //    return (-1);
+   }
+   /* Get the file size */
+   fseek(sst_file_ptr, 0, SEEK_END);
+   fileSize = ftell(sst_file_ptr);
+   fseek(sst_file_ptr, 0, SEEK_SET);
+
+   /* read if file is unformatted only */
+   if ( fileSize == unfSize ) {
+       strcpy(fileFormat,"unf");
+       bytes_read = fread(&sstGrid, sizeof(sstGrid), 1, sst_file_ptr);
+       (void) swapbyte4(&sstGrid, 12 + npoints_x * npoints_y);
+   }
+   else {
+       /* Close file */
+       (void) fclose(sst_file_ptr);
+  //     return (-1);
+   }
+   /* Close file */
+   (void) fclose(sst_file_ptr);
+
+   // to make it work with cython I don't need to allocate memory... I guess it's because cython
+   // already does it?!?
+   //sstInterp = (float *) malloc(eles*lines*sizeof(float));
+
+
+   indx = 0;
+   for ( i=0; i < lines; ++i) {
+       for ( j=0; j < eles; ++j) {
+
+         xindx = 0;
+         yindx = 0;
+         ok = -1;
+         sstInterp[indx] = 0.0;
+
+//       if(imgr1->landmask[indx] != 1) {
+
+           if (lon[indx] < 0.0)
+             xlon = lon[indx] + 360.0;
+           else
+             xlon = lon[indx];
+
+           xindx = (int) fminf(fmaxf(xlon,0.0),359.99);
+           ytemp = fminf(fmaxf(lat[indx],-89.99), 89.99);
+           yindx = (int) (ytemp + 90.0);
+
+           ok = bilinearInterpSST(xindx,yindx,lat[indx],xlon,&sstGrid,&tempVal);
+
+           if (ok == 0) {
+             sstInterp[indx] = tempVal + 273.15;
+           }
+
+           if(ok != 0)
+             printf("Bad SST interpolation: %d %d %d %d %d %d %f\n", indx, i, j, xindx, yindx, ok, sstInterp[indx]);
+
+//       }
+
+//       if(indx < 10)
+//         printf("SSTs: %d %d %d %d %f\n", indx, xindx, yindx, ok, sstInterp[indx]);
+
+         indx++;
+      }
+   }
+  //   grn_anc.reynSST = sstInterp;
+
+/******************************************************************************/
+
+   //return (return_code);
+
+}
diff --git a/mvcm/c_tools/get_b1_thresholds.c b/mvcm/c_tools/get_b1_thresholds.c
new file mode 100644
index 0000000..694345a
--- /dev/null
+++ b/mvcm/c_tools/get_b1_thresholds.c
@@ -0,0 +1,191 @@
+/**********************************************************************
+Description:
+
+  Integer function get_b1_thresholds.c
+
+  Computes band 1 (0.66 micron) cloud test thresholds for daytime land
+  conditions.
+  Computes band 8 (0.412 micron) cloud test thresholds for daytime
+  desert conditions.
+
+Input arguments:
+  none
+
+  Input values stored in variables found in pixel.h
+
+Output arguments:
+  locut         Zero confidence value of clear sky confidence interval
+  hicut         1.0 confidence value of clear sky confidence interval
+  midpt         0.5 confidence value of clear sky confidence interval
+  power         Power of clear sky confidence curve
+
+Revision History:
+  10/2008  R. Frey  Converted to C from FORTRAN
+  10/2012  R. Frey  Included in MVCCM software
+
+calls:
+  none
+
+***********************************************************************/
+
+/* Includes */
+
+#include <stdio.h>
+#include <math.h>
+#include "thresholds.h"
+#include "pixel.h"
+
+/**********************************************************************/
+
+
+int get_b1_thresholds(float *locut, float *hicut, float *midpt,
+                      float *power)
+
+
+/**********************************************************************/
+
+{
+
+/*  Declarations */
+
+    extern float powf(float, float);
+
+    int interp=0;
+    int a, i, j, k, indvi, icnf, irtn;
+
+    static float delta_ndvi_bin = 0.1;
+    float x, x1, x2, y1, y2;
+    float coeff[10][3][4], thr[3], thr_adj[3];
+
+//    Transfer all coefficients to one array.
+
+      i = 0;
+
+      for (j=0; j<3; j++) {
+        for( k=0; k<4; k++) {
+
+    	  if(pxin.ndvibk < des_ndvi[0]) {
+
+            coeff[0][j][k] = b8ndvi0[i];
+            coeff[1][j][k] = b8ndvi1[i];
+            coeff[2][j][k] = b8ndvi2[i];
+//          printf("coeffs8: %d %d %d %f %f\n",i, j, k, coeff[2][j][k], b8ndvi2[i]);
+
+    	  }
+    	  else {
+
+            coeff[0][j][k] = b1ndvi0[i];
+            coeff[1][j][k] = b1ndvi1[i];
+            coeff[2][j][k] = b1ndvi2[i];
+            coeff[3][j][k] = b1ndvi3[i];
+            coeff[4][j][k] = b1ndvi4[i];
+            coeff[5][j][k] = b1ndvi5[i];
+            coeff[6][j][k] = b1ndvi6[i];
+            coeff[7][j][k] = b1ndvi7[i];
+            coeff[8][j][k] = b1ndvi8[i];
+            coeff[9][j][k] = b1ndvi9[i];
+//          printf("coeffs1: %d %d %d %f %f\n",i, j, k, coeff[2][j][k], b1ndvi2[i]);
+
+    	  }
+
+          i++;
+
+        }
+      }
+
+//    printf("\nband 1, 8 coefficients initialized \n");
+
+/*    Compute thresholds for current pixel as functions of background
+      NDVI and scattering angle.
+*/
+
+//    Check for special cases and fill values ('ndvibk'=32.767).
+      if(pxin.ndvibk < fill_ndvi1[0] && pxin.ndvibk > fill_ndvi2[0]) {
+
+        if(pxin.ndvibk < ndvi_bnd1[0]) {
+          indvi = 0;
+          x = 0.0;
+          y2 = 0.0;
+          interp = 0;
+        }
+        else if(pxin.ndvibk >= ndvi_bnd2[0]) {
+          indvi = 9;
+          x = 0.0;
+          y2 = 0.0;
+          interp = 0;
+        }
+        else {
+          interp = 1;
+        }
+
+        if(interp) {
+          a = ((pxin.ndvibk / delta_ndvi_bin) - 0.5);
+          if(a < 0) {
+            indvi = 0;
+          }
+          else {
+            indvi = a;
+          }
+//        indvi = min(nbin_ndvi, max(0, int((pxin.ndvibk / delta_ndvi_bin) - 0.5)) );
+
+          x1 = delta_ndvi_bin*indvi + delta_ndvi_bin/2.0;
+          x2 = x1 + delta_ndvi_bin;
+          x = (pxin.ndvibk - x1) / ( x2 - x1 );
+          if(x < 0.0) x = 0.0;
+          if(x > 1.0) x = 1.0;
+
+        }
+
+        for( icnf=0; icnf<3; icnf++) {
+
+          y1 = coeff[indvi][icnf][0] +
+               (coeff[indvi][icnf][1] * pxin.sctang) +
+               (coeff[indvi][icnf][2] * powf(pxin.sctang,2)) +
+               (coeff[indvi][icnf][3] * powf(pxin.sctang,3));
+
+          if(interp) {
+            y2 =  coeff[indvi+1][icnf][0] +
+                  (coeff[indvi+1][icnf][1] * pxin.sctang) +
+                  (coeff[indvi+1][icnf][2] * powf(pxin.sctang,2)) +
+                  (coeff[indvi+1][icnf][3] * powf(pxin.sctang,3));
+          }
+
+          thr[icnf] = ((1.0 - x) * y1) + (x * y2);
+          if(pxin.ndvibk < des_ndvi[0]) {
+        	  thr_adj[icnf] = thr[icnf] * thr_adj_fac_desert[0];
+          }
+          else {
+            thr_adj[icnf] = thr[icnf] * thr_adj_fac_land[0];
+          }
+
+//        printf("ndvi thr: %f %f %f %f %f %f %f\n", des_ndvi[0], fill_ndvi1[0], fill_ndvi2[0],
+//                    ndvi_bnd1[0], ndvi_bnd2[0], thr_adj_fac_desert[0], thr_adj_fac_land[0]);
+//        printf("coeffs: %f %f %f %f %f %f %f %f\n", coeff[indvi][icnf][0], coeff[indvi][icnf][1],
+//        		  coeff[indvi][icnf][2], coeff[indvi][icnf][3], coeff[indvi+1][icnf][0], coeff[indvi+1][icnf][1],
+//        		  coeff[indvi+1][icnf][2], coeff[indvi+1][icnf][3]);
+//        printf("interpolation: %d %d %f %f %f %f %f\n", indvi, icnf, pxin.sctang, y1, y2, thr[icnf], thr_adj[icnf]);
+
+        }
+
+        *(hicut) = (thr[0] + thr_adj[0]) / 100.0;
+        *(midpt) = (thr[1] + thr_adj[1]) / 100.0;
+        *(locut) = (thr[2] + thr_adj[2]) / 100.0;
+        *(power) = 2.0;
+
+        irtn = 0;
+
+      }
+      else {
+
+//      No NDVI-dependent thresholds can be derived for the current pixel.
+    	irtn = -1;
+
+      }
+
+//    printf("\nSubroutine get_b1_thresholds\n");
+//    printf("%d %d %d %f %f %f %f %f %f %f %f \n", irtn, interp, indvi, pxin.ndvibk, pxin.sctang,
+//            x1, x2, x, *(locut), *(midpt), *(hicut));
+
+      return irtn;
+
+}
diff --git a/mvcm/c_tools/get_geos_times.c b/mvcm/c_tools/get_geos_times.c
new file mode 100644
index 0000000..447fc5b
--- /dev/null
+++ b/mvcm/c_tools/get_geos_times.c
@@ -0,0 +1,81 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_geos_times.c
+  Parses GEOS hour from filename.
+  Called from get_GEOS.c
+
+Input arguments:
+  s1               earlier GEOS file name
+  s2               later GEOS file name
+
+Output arguments:
+  geoshr1          integer time for earlier GEOS file
+  geoshr2          integer time for later GEOS file
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+Revision History:
+  10/2012 R. Frey  Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/******************************************************************************/
+
+int get_geos_times(char s1[64], char s2[64], int *geoshr1, int *geoshr2)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+    char search[4];
+    char *geos_time = "0";
+    char *token;
+
+    int return_code = 0;
+
+/******************************************************************************/
+
+//  Get GEOS start hours.
+  
+//  printf("get time 1: %s\n", s1);
+//  printf("get time 2: %s\n", s2);
+
+    (void) strcpy(search, "_");
+    token = strtok(s1, search);
+    token = strtok(NULL, search);
+    token = strtok(NULL, search);
+    token = strtok(NULL, search);
+    token = strtok(NULL, search);
+    (void) strcpy(search, "00.");
+    geos_time = strtok(token, search);
+    *(geoshr1) = atoi(geos_time);
+
+    (void) strcpy(search, "_");
+    token = strtok(s2, search);
+    token = strtok(NULL, search);
+    token = strtok(NULL, search);
+    token = strtok(NULL, search);
+    token = strtok(NULL, search);
+    (void) strcpy(search, "00.");
+    geos_time = strtok(token, search);
+    *(geoshr2) = atoi(geos_time);
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/get_granule_times.c b/mvcm/c_tools/get_granule_times.c
new file mode 100644
index 0000000..ef97765
--- /dev/null
+++ b/mvcm/c_tools/get_granule_times.c
@@ -0,0 +1,76 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_granule_times.c
+  Parses granule start date, hour, and minute from IFF attribute.
+  Called from get_GDAS.c
+
+Input arguments:
+  s1               granule start time string from IFF attribute
+
+Output arguments:
+  targhr           decimal granule start hour
+                   (target hour for time interplation of GDAS 
+                   variables)
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+Revision History:
+  10/2012 R. Frey  Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/******************************************************************************/
+
+int get_granule_times(char s1[64], float *targhr)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+    char s2[64];
+    char search[2];
+    char *start_time = "0";
+    char *start_date = "0";
+    char *start_hour = "0";
+    char *start_min = "0";
+    int imin;
+
+    int return_code = 0;
+
+/******************************************************************************/
+
+//  Get granule start date, hour, and minute.
+
+    (void) strcpy(search, " ");
+    start_date = strtok(s1, search);
+    start_time = strtok(NULL, search);
+
+    (void) strcpy(s2, start_time);
+    (void) strcpy(search, ":");
+    start_hour = strtok(s2, search);
+    start_min = strtok(NULL, search);
+    printf("\nstart date, hour and minute: %s %s %s\n", start_date, start_hour, start_min);
+
+//  Set decimal target hour for time interpolation.
+    imin = atoi(start_min);
+    *(targhr) = (float)atoi(start_hour) + ((float)imin / 60.0);
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/get_ti_vars.c b/mvcm/c_tools/get_ti_vars.c
new file mode 100644
index 0000000..b48470f
--- /dev/null
+++ b/mvcm/c_tools/get_ti_vars.c
@@ -0,0 +1,91 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_ti_vars_geos.c
+  Calculates time-interpolated variables from the two GEOS times.
+  Called from get_GEOS.c
+
+Input arguments:
+  wt               time interpolation weight for later GEOS time
+
+Output arguments:
+  none
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+  Inputs are taken from pointers stored in ancillary.h.
+  Pointers to time-interpolated variables are stored in ancillary.h.
+
+Revision History:
+  10/2012 R. Frey  Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//MVCCM includes
+#include "ancillary.h"
+
+/******************************************************************************/
+
+int get_ti_vars(float wt)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+    long int n;
+    int return_code = 0;
+
+/******************************************************************************/
+
+//  Allocate memory for time-interpolated data.
+    if ((ti_sfct = (float *) malloc(sizeof(float) * nwp_cells)) == NULL) {
+      printf("Could not allocate memory for ti_sfct\n");
+      return(-2);
+    }
+    if ((ti_tpw = (float *) malloc(sizeof(float) * nwp_cells)) == NULL) {
+      printf("Could not allocate memory for ti_tpw\n");
+      return(-4);
+    }
+
+/******************************************************************************/
+
+    for (n=0; n<nwp_cells; n++) {
+
+      ti_sfct[n] = (g_sfct1[n] >= 0.0 && g_sfct2[n] >= 0.0) ?
+           (wt*(g_sfct2[n]) + (1.0-wt)*(g_sfct1[n])) : -999.99;
+//    if(n >= 1000 && n <= 1009)
+//      printf("ti sfc t: %ld %f %f %f %f\n", n, wt, g_sfct1[n], g_sfct2[n], ti_sfct[n]);
+
+      ti_tpw[n] = (g_tpw1[n] >= 0.0 && g_tpw2[n] >= 0.0) ?
+           (wt*(g_tpw2[n]/10.0) + (1.0-wt)*(g_tpw1[n]/10.0)) : -999.99;
+//    if(n >= 1000 && n <= 1009)
+//      printf("ti tpw: %ld %f %f %f %f\n", n, wt, g_tpw1[n], g_tpw2[n], ti_tpw[n]);
+
+    }
+
+/******************************************************************************/
+
+//  Free unneeded memory.
+    free(g_sfct1);
+    free(g_sfct2);
+    free(g_tpw1);
+    free(g_tpw2);
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/get_ti_vars_geos.c b/mvcm/c_tools/get_ti_vars_geos.c
new file mode 100644
index 0000000..b48470f
--- /dev/null
+++ b/mvcm/c_tools/get_ti_vars_geos.c
@@ -0,0 +1,91 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_ti_vars_geos.c
+  Calculates time-interpolated variables from the two GEOS times.
+  Called from get_GEOS.c
+
+Input arguments:
+  wt               time interpolation weight for later GEOS time
+
+Output arguments:
+  none
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+  Inputs are taken from pointers stored in ancillary.h.
+  Pointers to time-interpolated variables are stored in ancillary.h.
+
+Revision History:
+  10/2012 R. Frey  Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//MVCCM includes
+#include "ancillary.h"
+
+/******************************************************************************/
+
+int get_ti_vars(float wt)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+    long int n;
+    int return_code = 0;
+
+/******************************************************************************/
+
+//  Allocate memory for time-interpolated data.
+    if ((ti_sfct = (float *) malloc(sizeof(float) * nwp_cells)) == NULL) {
+      printf("Could not allocate memory for ti_sfct\n");
+      return(-2);
+    }
+    if ((ti_tpw = (float *) malloc(sizeof(float) * nwp_cells)) == NULL) {
+      printf("Could not allocate memory for ti_tpw\n");
+      return(-4);
+    }
+
+/******************************************************************************/
+
+    for (n=0; n<nwp_cells; n++) {
+
+      ti_sfct[n] = (g_sfct1[n] >= 0.0 && g_sfct2[n] >= 0.0) ?
+           (wt*(g_sfct2[n]) + (1.0-wt)*(g_sfct1[n])) : -999.99;
+//    if(n >= 1000 && n <= 1009)
+//      printf("ti sfc t: %ld %f %f %f %f\n", n, wt, g_sfct1[n], g_sfct2[n], ti_sfct[n]);
+
+      ti_tpw[n] = (g_tpw1[n] >= 0.0 && g_tpw2[n] >= 0.0) ?
+           (wt*(g_tpw2[n]/10.0) + (1.0-wt)*(g_tpw1[n]/10.0)) : -999.99;
+//    if(n >= 1000 && n <= 1009)
+//      printf("ti tpw: %ld %f %f %f %f\n", n, wt, g_tpw1[n], g_tpw2[n], ti_tpw[n]);
+
+    }
+
+/******************************************************************************/
+
+//  Free unneeded memory.
+    free(g_sfct1);
+    free(g_sfct2);
+    free(g_tpw1);
+    free(g_tpw2);
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/get_ti_weights.c b/mvcm/c_tools/get_ti_weights.c
new file mode 100644
index 0000000..607489d
--- /dev/null
+++ b/mvcm/c_tools/get_ti_weights.c
@@ -0,0 +1,69 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_ti_weights_geos.c
+  Calculates time-interpolation weights from L1b start time and the two GEOS
+  file times.
+  Computed weight is for the later of two GEOS grids.  The weight of the earlier
+  one is simply (1.0 - weight).
+  Called from get_GEOS.c
+
+Input arguments:
+  targhr           decimal start time of input L1b data
+  gdashr1          integer hour of earlier GEOS file
+  gdashr2          integer hour of later GEOS file
+
+Output arguments:
+  wt               time-interpolation weight for later GEOS time
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+Revision History:
+  10/2012 R. Frey  Original version
+  06/2021 R. Frey  Modified to use GMAO GEOS files.
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/******************************************************************************/
+
+int get_ti_weights(float targhr, int geoshr1, int geoshr2, float *wt)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+    int return_code = 0;
+
+/******************************************************************************/
+
+//  Calculate time-interpolation weight.
+
+    if(geoshr2 > geoshr1) *(wt) = (targhr-(float)geoshr1) / ((float)geoshr2-(float)geoshr1);
+    if(geoshr2 == geoshr1) *(wt) = 0.0;
+    if(geoshr2 < geoshr1) {
+      geoshr2 += 24;
+      *(wt) = (targhr-(float)geoshr1) / ((float)geoshr2-(float)geoshr1);
+      if( *(wt) < 0.0) {
+        printf("GEOS times are incorrect! T-I weight: %f %d %d %f\n", targhr, geoshr1, geoshr2, *(wt));
+        return_code = -1;
+      }
+    }
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/get_ti_weights_geos.c b/mvcm/c_tools/get_ti_weights_geos.c
new file mode 100644
index 0000000..607489d
--- /dev/null
+++ b/mvcm/c_tools/get_ti_weights_geos.c
@@ -0,0 +1,69 @@
+/*******************************************************************************
+Description:
+
+  Integer function get_ti_weights_geos.c
+  Calculates time-interpolation weights from L1b start time and the two GEOS
+  file times.
+  Computed weight is for the later of two GEOS grids.  The weight of the earlier
+  one is simply (1.0 - weight).
+  Called from get_GEOS.c
+
+Input arguments:
+  targhr           decimal start time of input L1b data
+  gdashr1          integer hour of earlier GEOS file
+  gdashr2          integer hour of later GEOS file
+
+Output arguments:
+  wt               time-interpolation weight for later GEOS time
+
+Function output:
+  int return_code  successful completion is zero, otherwise non-zero
+
+Revision History:
+  10/2012 R. Frey  Original version
+  06/2021 R. Frey  Modified to use GMAO GEOS files.
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/******************************************************************************/
+
+int get_ti_weights(float targhr, int geoshr1, int geoshr2, float *wt)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+    int return_code = 0;
+
+/******************************************************************************/
+
+//  Calculate time-interpolation weight.
+
+    if(geoshr2 > geoshr1) *(wt) = (targhr-(float)geoshr1) / ((float)geoshr2-(float)geoshr1);
+    if(geoshr2 == geoshr1) *(wt) = 0.0;
+    if(geoshr2 < geoshr1) {
+      geoshr2 += 24;
+      *(wt) = (targhr-(float)geoshr1) / ((float)geoshr2-(float)geoshr1);
+      if( *(wt) < 0.0) {
+        printf("GEOS times are incorrect! T-I weight: %f %d %d %f\n", targhr, geoshr1, geoshr2, *(wt));
+        return_code = -1;
+      }
+    }
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/getcoord.c b/mvcm/c_tools/getcoord.c
new file mode 100644
index 0000000..8b5872b
--- /dev/null
+++ b/mvcm/c_tools/getcoord.c
@@ -0,0 +1,650 @@
+/*  
+!C **********************************************************************
+!Description:
+    GIHLS211 transforms latitude, longitude coordinates to line, sample 
+    Renamed GIHLS211 to GETCOORD !!
+    coordinates for an image in the Goode's Interrupted Homolosine 
+    projection.  This routine was complied and run using the C compiler on 
+    SunOS 4.2 (UNIX).  Results were accurate at the time of testing, but they
+    are by no means guaranteed!
+
+!Input Parameters:  
+lt      latitude (double precision)
+ln      longitude (double precision) 
+
+!Output Parameters:
+lne     line number of 1km mask for given latitude/longitude
+smp     element number of 1km maks for given latitude/longitude
+
+!Revision History:
+
+!Team-unique Header:
+
+!References and Credits:
+
+1.  Snyder, John P. and Voxland, Philip M., "An Album of Map Projections",
+    U.S. Geological Survey Professional Paper 1453 , United State Government
+    Printing Office, Washington D.C., 1989.
+
+2.  Snyder, John P., "Map Projections--A Working Manual", U.S. Geological
+    Survey Professional Paper 1395 (Supersedes USGS Bulletin 1532), United
+    State Government Printing Office, Washington D.C., 1987.
+
+3.  Goode, J.P., 1925,  The Homolosine projection:  a new device for
+    portraying the Earth's surface entire:  Assoc. Am. Geographers, Annals,
+    v. 15, p. 119-125
+
+4.  Steinwand, Daniel R., "Mapping Raster Imagery to the Interrupted
+    Goode Homolosine Projection", 1993.  In press--IJRS.
+
+!Design Notes:
+!END************************************************************************/
+#include <stdio.h>
+#include <math.h>
+
+
+/*  Modified by JC Guu  08/27/96             */
+/*  function prototype is given here for the */
+/*  functions in this module.                */
+
+extern void getcoord_(double *lt, double *ln, double *lne, double *smp);
+/*void getcoord_(double *lt, double *ln, double *lne, double *smp);*/
+void bad_input_parms();
+void goode_init(double r); 
+int  goode_forward(double lon, double lat, double* x, double* y);
+/*
+void ptitle(char* A);
+void radius(double A);
+*/
+void giherror(char* what, char* where);
+void getcoord_sincos(double val, double* sin_val, double* cos_val);
+int getcoord_sign(double x);
+double getcoord_adjust_lon(double x);
+double getcoord_e0fn(double x);
+double getcoord_e1fn(double x);
+double getcoord_e2fn(double x);
+double getcoord_mlfn(double e0,double e1,double e2,double phi);
+
+
+#define PI      3.141592653589793238
+#define HALF_PI PI*0.5
+#define TWO_PI  PI*2.0
+#define EPSLN   1.0e-10
+#define R2D     57.2957795131
+#define D2R     0.0174532925199
+
+#define OK      1
+#define ERROR  -1
+#define IN_BREAK -2
+
+/* Variables common to all subroutines in this code file
+  -----------------------------------------------------*/
+static double R;                /* Radius of the earth (sphere) */
+static double lon_center[12];   /* Central meridians, one for each region */
+static double feast[12];        /* False easting, one for each region */
+
+/* Transformation routine
+  -------------------------------*/
+void getcoord_(double *lt, double *ln, double *lne, double *smp)     
+{
+float pixsiz;
+double x,y;
+double lat, lon, line, samp;
+/*
+int nl,ns;
+*/
+double ul_x, ul_y;
+
+/*  Modified by JC Guu  08/30/96             */
+/*  The variable status is added here to     */
+/*  process the return code of               */
+/*  goode_forward().                         */
+
+int status;
+
+/*  Modified by JC Guu  08/26/96             */
+/*  The type of constant is specified.       */
+
+pixsiz = 1000.0F;
+
+/* Report parameters and image geometric characteristics to the user
+  -----------------------------------------------------------------*/
+/*printf("Converting latitude, longitude to line, sample coordinates\n\n"); 
+printf("Pixel size is %f km\n\n", pixsiz/1000.0);  */
+
+/*  Modified by JC Guu  08/26/96             */
+/*  The equality comparision                 */
+/*  "pixsiz == 1000.0" is changed to an      */
+/*  inequality comparision                   */
+/*  "fabs(pixsiz-1000.0F) < 0.000001F".      */
+
+
+if (fabs(pixsiz-1000.0F) < 0.000001F) {
+   ul_x = -20015000.0;
+   ul_y =   8673000.0;
+/*
+   nl = 17347;
+   ns = 40031;
+*/
+   } 
+else {
+   ul_x = -20011500.0;
+   ul_y =   8669500.0;
+/*
+   nl = 2168;
+   ns = 5004;
+*/
+   } 
+/*printf("Image size is %d lines by %d samples with an upper left\n",nl,ns);
+printf("corner of UL_X = %lf and UL_Y = %lf meters.\n", ul_x, ul_y);   */
+
+/* Initialize the Interrupted Goode Homolosine projection
+  ------------------------------------------------------*/
+goode_init(6370997.0);
+
+/* Process point
+  ------------------*/
+   lat = *lt;
+   lon = *ln;
+/* printf("%12.6f %12.6f\n",lat, lon);*/
+   lat *= D2R;
+   lon *= D2R;
+
+/*  Modified by JC Guu  08/30/96           */
+/*  The variable status is used to         */
+/*  capture the return value form          */
+/*  goode_forward().                       */
+/*  Valid return code to process the       */
+/*  return status could be added in the    */
+/*  furture.                               */
+
+   status=goode_forward(lon, lat, &x, &y);
+   if(status==ERROR)
+       /*printf("Newton-Raphson method failed to converge\n");*/
+       ;
+
+   line = (ul_y - y) / pixsiz + 1.0;
+   samp = (x - ul_x) / pixsiz + 1.0;
+/* printf("%12.6f %12.6f\n",lat, lon); 
+   printf("%12.6f %12.6f\n",line, samp);   */
+
+   *lne = line;
+   *smp = samp;
+   *lt = lat;
+   *ln = lon;
+
+   return;
+}          
+
+
+
+/*  Modified by JC Guu  08/26/96              */
+/*  The return type void is given.            */
+
+void bad_input_parms() {
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Function to report bad parameters.
+!Input Parameters: none
+!Output Parameters: none
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+/*  Modified by JC Guu  09/03/96              */
+/*  The prohibited printf() code is           */
+/*  commented out.                            */
+
+/* printf("Syntax: gihll2ls pixsize\n");
+printf("         pixsize in km = 1 or 8\n\n");*/
+}
+
+
+
+/*  Modified by JC Guu  08/26/96              */
+/*  The old K&R style function definition     */
+/*  is changed to the modern form and the     */
+/*  return type is given as void.             */
+
+void goode_init(double r) 
+{
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Initialize the Goode`s Homolosine projection.
+              Place parameters in static storage for common use.
+!Input Parameters:   r                Radius of the earth (sphere)
+!Output Parameters:  lon_center[12]   Central meridians, one for 
+                                      each region
+                     feast[12];       False easting, one for each 
+                                      region
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+/* Place parameters in static storage for common use
+  -------------------------------------------------*/
+
+R = r;
+
+/* Initialize central meridians for each of the 12 regions
+  -------------------------------------------------------*/
+lon_center[0] = -1.74532925199;         /* -100.0 degrees */
+lon_center[1] = -1.74532925199;         /* -100.0 degrees */
+lon_center[2] =  0.523598775598;        /*   30.0 degrees */
+lon_center[3] =  0.523598775598;        /*   30.0 degrees */
+lon_center[4] = -2.79252680319;         /* -160.0 degrees */
+lon_center[5] = -1.0471975512;          /*  -60.0 degrees */
+lon_center[6] = -2.79252680319;         /* -160.0 degrees */
+lon_center[7] = -1.0471975512;          /*  -60.0 degrees */
+lon_center[8] =  0.349065850399;        /*   20.0 degrees */
+lon_center[9] =  2.44346095279;         /*  140.0 degrees */
+lon_center[10] = 0.349065850399;        /*   20.0 degrees */
+lon_center[11] = 2.44346095279;         /*  140.0 degrees */
+
+/* Initialize false eastings for each of the 12 regions
+  ----------------------------------------------------*/
+feast[0] = R * -1.74532925199;
+feast[1] = R * -1.74532925199;
+feast[2] = R * 0.523598775598;
+feast[3] = R * 0.523598775598;
+feast[4] = R * -2.79252680319;
+feast[5] = R * -1.0471975512;
+feast[6] = R * -2.79252680319;
+feast[7] = R * -1.0471975512;
+feast[8] = R * 0.349065850399;
+feast[9] = R * 2.44346095279;
+feast[10] = R * 0.349065850399;
+feast[11] = R * 2.44346095279;
+
+/* Report parameters to the user
+  -----------------------------*/
+/*ptitle("Goode`s Homolosine Equal Area"); 
+radius(r):   */
+
+/* Modified by JC Guu  08/26/96              */
+/* The function should not return anything   */
+/* here.                                     */
+
+}
+
+
+
+/*  Modified by JC Guu  08/26/96              */
+/*  The old K&R style function definition     */
+/*  is changed to the modern form and the     */
+/*  return type is given as void.             */
+
+int goode_forward(double lon, double lat, double* x, double* y)
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description:Goode`s Homolosine forward equations--mapping 
+             lat,long to x,y
+!Input Parameters:  lon      Longitude
+                    lat      Latitude
+!Output Parameters: x        X projection coordinate
+                    y        Y projection coordinate                    
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+{
+
+/*  Modified by JC Guu  08/30/96              */
+/*  Function prototype is provided for        */
+/*  getcoord_adjust_lon() therefore the following      */
+/*  declaration is not needed.                */
+
+/* double getcoord_adjust_lon();*/ /* Function to adjust longitude to -180 - 180 */
+double delta_lon;       /* Delta longitude (Given longitude - center */
+double theta;
+double delta_theta;
+double constant;
+int i;
+int region;
+
+/* Forward equations
+  -----------------*/
+if (lat >= 0.710987989993)                   /* if on or above 40 44' 11.8" */
+   {
+   if (lon <= -0.698131700798) region = 0;   /* If to the left of -40 */
+   else region = 2;
+   }
+else if (lat >= 0.0)                         /* Between 0.0 and 40 44' 11.8" */
+   {
+   if (lon <= -0.698131700798) region = 1;   /* If to the left of -40 */
+   else region = 3;
+   }
+else if (lat >= -0.710987989993)             /* Between 0.0 & -40 44' 11.8" */
+   {
+   if (lon <= -1.74532925199) region = 4;       /* If between -180 and -100 */
+   else if (lon <= -0.349065850399) region = 5; /* If between -100 and -20 */
+   else if (lon <= 1.3962634016) region = 8;    /* If between -20 and 80 */
+   else region = 9;                             /* If between 80 and 180 */
+   }
+else                                            /* Below -40 44' */
+   {
+   if (lon <= -1.74532925199) region = 6;       /* If between -180 and -100 */
+   else if (lon <= -0.349065850399) region = 7;     /* If between -100 and -20 */
+   else if (lon <= 1.3962634016) region = 10;   /* If between -20 and 80 */
+   else region = 11;                            /* If between 80 and 180 */
+   }
+
+if (region==1||region==3||region==4||region==5||region==8||region==9)
+   {
+   delta_lon = getcoord_adjust_lon(lon - lon_center[region]);
+   *x = feast[region] + R * delta_lon * cos(lat);
+   *y = R * lat;
+   }
+else
+   {
+   delta_lon = getcoord_adjust_lon(lon - lon_center[region]);
+   theta = lat;
+   constant = PI * sin(lat);
+
+/* Iterate using the Newton-Raphson method to find theta
+  -----------------------------------------------------*/
+   for (i=0;;i++)
+      {
+      delta_theta = -(theta + sin(theta) - constant) / (1.0 + cos(theta));
+      theta += delta_theta;
+      if (fabs(delta_theta) < EPSLN) break;
+      if (i >= 30) 
+         {giherror("Iteration failed to converge","Goode-forward");return(ERROR);}
+      }
+   theta /= 2.0;
+   *x = feast[region] + 0.900316316158 * R * delta_lon * cos(theta);
+
+/*  Modified by JC Guu                        */
+/*  The explicit cast is provided.            */
+
+   *y = R * (1.4142135623731 * sin(theta) - 0.0528035274542 * ((double) getcoord_sign(lat)));
+   }
+
+
+return(OK);
+}
+
+
+
+/*  Modified by JC Guu  08/27/96              */
+/*  The function definition style is changed  */
+/*  the K&R style to the modern form.         */
+
+/*
+void ptitle(char* A) 
+*/
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Functions to report projection parameter
+!Input Parameters: none
+!Output Parameters: none
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+/*  Modified by JC Guu  09/03/96              */
+/*  printf() is a prohibited function.        */
+
+/* {printf("\n%s Projection Parameters:\n\n",A);} */
+
+
+
+/*
+void radius(double A) 
+*/
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description:
+!Input Parameters: none
+!Output Parameters: none
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+/*  Modified by JC Guu  08/27/96             */
+/*  The non-standard format specification    */
+/*  %lf is changed to %f.                    */
+
+/*  Modified by JC Guu  09/03/96             */
+/*  The prohibited function ptintf() is      */
+/*  commented out.                           */
+
+/*{ printf("   Radius of Sphere:     %f meters\n",A); } */
+
+
+
+/*  Modified by JC Guu  08/26/96              */
+/*  The old K&R style function definition     */
+/*  is changed to the modern form and the     */
+/*  return type is given as void.             */
+
+void giherror(char* what, char* where) 
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Function to report errors
+!Input Parameters: what     cause of the error
+                   where    where the error occurs
+!Output Parameters: none
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+/*  Modified by JC Guu  09/03/96              */
+/*  printf() is a prohibited function.        */
+/* commented out by fhliang on 12/08/97 */
+{
+/* printf("[%s] %s\n",where,what); */
+}
+
+
+#ifndef sun
+
+
+
+/*  Modified by JC Guu  08/26/96              */
+/*  The old K&R style function definition     */
+/*  is changed to the modern form and the     */
+/*  return type is given as void.             */
+
+void getcoord_sincos(double val, double* sin_val, double* cos_val)
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Function to calculate the sine and cosine in 
+              one call.  Some computer systems have implemented 
+              this function, resulting in a faster implementation
+              than calling each function separately.  It is 
+              provided here for those computer systems which 
+              don`t implement this function.
+
+!Input Parameters:  val         the value to be operated on
+!Output Parameters: sin_val     the sine of val
+                    cos_val     the cosine of val
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+/*  Modified by JC Guu  08/26/96              */
+/*  A return statement is not needed here.    */
+
+{ *sin_val = sin(val); *cos_val = cos(val); }
+#endif
+
+
+
+/*  Modified by JC Guu  08/27/96              */
+/*  The old K&R style function definition     */
+/*  is changed to the modern form and the     */
+/*  return type is given as int.              */
+
+int getcoord_sign(double x) 
+{ if (x < 0.0) return(-1); else return(1);}
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Function to return the sign of an argument
+!Input Parameters:  x    a floating point number
+!Output Parameters: the sign of x
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+
+
+/*  Modified by JC Guu  08/27/96              */
+/*  The old K&R style function definition     */
+/*  is changed to the modern.                 */
+
+double getcoord_adjust_lon(double x)
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Function to adjust longitude to -180 to 180
+!Input Parameters: x  
+!Output Parameters:
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+/*  Modified by JC Guu  08/27/96              */
+/*  The explicit cast is provided.            */
+
+{x=(fabs(x)<PI)?x:(x-(((double) getcoord_sign(x))*TWO_PI));return(x);}
+
+
+
+/*  Modified by JC Guu  08/27/96                */
+/*  The style of function definition is changed */
+/*  from the K&R style to the modern form for   */
+/*  the following functions.                    */
+
+double getcoord_e0fn(double x) 
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Function to compute e0
+!Input Parameters: x
+!Output Parameters: 
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+{return(1.0-0.25*x*(1.0+x/16.0*(3.0+1.25*x)));}
+
+
+
+double getcoord_e1fn(double x)
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Function to compute e1
+!Input Parameters: x
+!Output Parameters: 
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+{return(0.375*x*(1.0+0.25*x*(1.0+0.46875*x)));}
+
+ 
+
+double getcoord_e2fn(double x) 
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description: Function to compute e2
+!Input Parameters: x
+!Output Parameters:
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+{return(0.05859375*x*x*(1.0+0.75*x));}
+
+
+
+double getcoord_mlfn(double e0,double e1,double e2,double phi) 
+
+/*  Modified by JC Guu  08/30/96              */
+/*  The required prolog components are added  */
+/*  here.                                     */
+
+/*
+!C
+!Description:
+!Input Parameters: e0, e1, e2 and phi
+!Output Parameters:
+!Revision History:
+!Team-unique Header:
+!END
+*/
+
+{
+return(e0*phi-e1*sin(2.0*phi)+e2*sin(4.0*phi));
+}
diff --git a/mvcm/c_tools/include/ancillary.h b/mvcm/c_tools/include/ancillary.h
new file mode 100644
index 0000000..5c30bf1
--- /dev/null
+++ b/mvcm/c_tools/include/ancillary.h
@@ -0,0 +1,41 @@
+/*******************************************************************************
+Description:
+
+  Header file ancillary.h
+  Contains pointers to all ancillary data necessary for processing granule.
+  Access through structure variable 'grn_anc'.
+
+Revision History:
+  10/2012     R. Frey   Original version
+
+*******************************************************************************/
+
+// For obtaininig and processing ancillary data.
+float *g_sfct1;
+float *g_sfct2;
+float *g_snow;
+float *g_ice;
+float *g_frocn;
+float *g_frlandice;
+float *g_phis;
+float *g_tpw1;
+float *g_tpw2;
+float *g_lst;
+float *ti_sfct;
+float *ti_tpw;
+int nwp_rows;
+int nwp_cols;
+long int nwp_cells;
+
+// Ancillary data for entire granule.
+struct granule_ancillary {
+  float *reynSST;
+  float *ndvibk;
+  unsigned char *eco;
+  float *sfct;
+  float *tpw;
+  float *snowfr;
+  float *landicefr;
+  float *icefr;
+  float *geos_ocnfr;
+} grn_anc;
diff --git a/mvcm/c_tools/include/mask_processing_constants.h b/mvcm/c_tools/include/mask_processing_constants.h
new file mode 100644
index 0000000..bc1b6da
--- /dev/null
+++ b/mvcm/c_tools/include/mask_processing_constants.h
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ Description:
+
+   Header file mask_processing_constants.h
+   Contains definitions for various processing constants.
+
+   Revision History:
+   09/2012     R. Frey   Original version
+   11/2012     R. Frey   Changed 'badsza_data' to 'badgeo_data'.
+   08/2013     R. Frey   Eliminated 'max_vza' and 
+                         added 'MODIS_max_vza' and 'VIIRS_max_vza'.
+   08/2015     R. Frey   Added "min" and "max" macros.
+
+*******************************************************************************/
+
+#define nlcntx 3
+#define necntx 3
+#define lines_in_edge (nlcntx-1)/2
+#define elems_in_edge (necntx-1)/2
+#define num_diffs_surr (nlcntx * necntx) - 1
+#define num_test_groups 5
+#define min(a,b)  ((a) < (b) ? (a) : (b))
+#define MODIS_max_vza 65.49
+#define VIIRS_max_vza 70.13
+#define dtr (3.14159 / 180.0)
+#define rtd (180.0 / 3.14159)
+#define bad_data -999.0
+#define badgeo_data -327.67
+#define conf_clr_thresh 0.99
+#define prob_clr_thresh 0.95
+#define prob_cld_thresh 0.66
+
+#ifndef min
+#define min(a,b)  ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a,b)  ((a) < (b) ? (b) : (a))
+#endif
diff --git a/mvcm/c_tools/include/sst.h b/mvcm/c_tools/include/sst.h
new file mode 100644
index 0000000..2ba3d37
--- /dev/null
+++ b/mvcm/c_tools/include/sst.h
@@ -0,0 +1,26 @@
+/**********************************************************************
+Description:
+
+  Header file sst.h
+  Defines Reynolds "sst" structures.
+
+Revision History:
+  10/2012   R. Frey     Original version
+
+***********************************************************************/
+
+typedef struct {
+  int headerLen1;
+  int startYear;
+  int startMonth;
+  int startDay;
+  int endYear;
+  int endMonth;
+  int endDay;
+  int unknown1;
+  int unknown2;
+  int headerLen2;
+  int numSSTValues1;
+  float values[180][360];
+  int numSSTValues2;
+} SST;
diff --git a/mvcm/c_tools/read_GEOS.c b/mvcm/c_tools/read_GEOS.c
new file mode 100644
index 0000000..5e94a41
--- /dev/null
+++ b/mvcm/c_tools/read_GEOS.c
@@ -0,0 +1,219 @@
+/******************************************************************************
+
+Description:
+
+  Integer function read_GEOS.f
+  Reads desired variables from input file (in calling argument list).
+
+Input arguments:
+  geos_file             input GEOS file name
+  geos_time_index       indicates earlier (=1) or later (=2) GEOS file time
+
+Output arguments:
+  none
+
+Function output:
+  int return_code       successful completion is zero, otherwise non-zero
+
+  Output variables in ancillary.h
+
+Revision History:
+  06/2021     R. Frey   Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+#include <float.h>
+
+//MVCCM includes
+//#include "granule_geolocation.h"
+//#include "granule.h"
+#include "ancillary.h"
+
+//netcdf includes
+#include <netcdf.h>
+
+/******************************************************************************/
+
+int read_GEOS(char *geos_file, int geos_time_index)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+/******************************************************************************/
+
+    size_t lat_len, lon_len, start[3], edge[3];
+    int status, idf, latln_id, lonln_id, nx, ny, nxny, varid;
+    int return_code = 0;
+
+/******************************************************************************/
+
+    start[0] = 0;
+    start[1] = 0;
+    start[2] = 0;
+    edge[0] = 0;
+    edge[1] = 0;
+    edge[2] = 0;
+
+/******************************************************************************/
+
+//  Open file.
+//  printf("Opening GEOS file: %s\n", geos_file);
+    status = nc_open(geos_file, NC_NOWRITE, &idf);
+    if (status != NC_NOERR) {
+      printf("Could not open GEOS file: %s %d %d\n", geos_file, idf, status);
+      return (-1);
+    }
+
+/******************************************************************************/
+
+//  Get data array dimensions.  All meteorologial variables are the same size.
+//  First dimension (time) is 1.
+
+    status = nc_inq_dimid(idf, "lat", &latln_id);
+    if(status < 0) {
+      printf("Could not acquire lat-dimension data: %s\n", geos_file);
+      return_code = -2;
+      return (return_code);
+    }
+    status = nc_inq_dimlen(idf, latln_id, &lat_len);
+
+    status = nc_inq_dimid(idf, "lon", &lonln_id);
+    if(status < 0) {
+      printf("Could not acquire lon-dimension data: %s\n", geos_file);
+      return_code = -3;
+      return (return_code);
+    }
+    status = nc_inq_dimlen(idf, lonln_id, &lon_len);
+
+/******************************************************************************/
+
+    nx = lon_len;
+    ny = lat_len;
+    nxny = lat_len * lon_len;
+    nwp_cells = nxny;
+    nwp_cols = nx;
+    nwp_rows = ny;
+
+//  printf("dims: %d %d %d %d %d %d\n", idf, latln_id, lonln_id, lat_len, lon_len, nxny);
+//  printf("time index: %d\n", geos_time_index);
+
+    if(geos_time_index == 1) {
+
+      if ((g_sfct1 = (float *) malloc(sizeof(float) * nxny)) == NULL) {
+        printf("Could not allocate memory for g_sfct1\n");
+        return(-4);
+      }
+      if ((g_tpw1 = (float *) malloc(sizeof(float) * nxny)) == NULL) {
+        printf("Could not allocate memory for g_tpw1\n");
+        return(-5);
+      }
+
+    }
+    else if (geos_time_index == 2) {
+
+      if ((g_sfct2 = (float *) malloc(sizeof(float) * nxny)) == NULL) {
+        printf("Could not allocate memory for g_sfct2\n");
+        return(-6);
+      }
+      if ((g_tpw2 = (float *) malloc(sizeof(float) * nxny)) == NULL) {
+        printf("Could not allocate memory for g_tpw2\n");
+        return(-7);
+      }
+
+    }
+
+/******************************************************************************/
+
+    edge[0] = 1;
+    edge[1] = lat_len;
+    edge[2] = lon_len;
+
+//  Read skin temperatures.
+
+    status = nc_inq_varid (idf, "TS", &varid);
+    if(status != NC_NOERR) {
+      printf("Could not find TS variable in %s\n", geos_file);
+      return_code = -8;
+      return (return_code);
+    }
+
+    if(geos_time_index == 1) {
+      status = nc_get_vara_float(idf, varid, start, edge, g_sfct1);
+    }
+    else if (geos_time_index == 2) {
+      status = nc_get_vara_float(idf, varid, start, edge, g_sfct2);
+    }
+    if (status != NC_NOERR) {
+      printf("could not get TS data in %s\n", geos_file);
+      printf("status: %d\n", status);
+      return_code = -9;
+      return (return_code);
+    }
+
+//  Read total precipitable water.
+
+    status = nc_inq_varid (idf, "TQV", &varid);
+    if(status != NC_NOERR) {
+      printf("Could not find TQV variable in %s\n", geos_file);
+      return_code = -10;
+      return (return_code);
+    }
+
+    if(geos_time_index == 1) {
+      status = nc_get_vara_float(idf, varid, start, edge, g_tpw1);
+    }
+    else if (geos_time_index == 2) {
+      status = nc_get_vara_float(idf, varid, start, edge, g_tpw2);
+    }
+    if (status != NC_NOERR) {
+      printf("could not get TQV data in %s\n", geos_file);
+      printf("status: %d\n", status);
+      return_code = -11;
+      return (return_code);
+    }
+
+/******************************************************************************/
+
+//  if(geos_time_index == 1) {
+//    printf("GEOS1 data: %f %f %f\n", g_sfct1[0], g_sfct1[9], g_sfct1[nxny-1]);
+//    printf("GEOS1 data: %f %f %f\n", g_tpw1[0], g_tpw1[9], g_tpw1[nxny-1]);
+//    printf("GEOS1 data: %f %f %f %f %f %f %f %f %f\n",
+//           g_sfct1[0], g_lst[0], g_tpw1[0],
+//           g_sfct1[32580], g_lst[32580], g_tpw1[32580],
+//           g_sfct1[65159], g_lst[65159], g_tpw1[65159]);
+//  }
+//  if(geos_time_index == 2) {
+//    printf("GEOS2 data: %f %f %f\n", g_sfct2[0], g_sfct2[9], g_sfct2[nxny-1]);
+//    printf("GEOS2 data: %f %f %f\n", g_tpw2[0], g_tpw2[9], g_tpw2[nxny-1]);
+//    printf("GEOS2 data: %f %f %f %f %f %f\n",
+//           g_sfct2[0], g_tpw2[0],
+//           g_sfct2[32580], g_tpw2[32580],
+//           g_sfct2[65159], g_tpw2[65159]);
+//  }
+//  fflush;
+
+/******************************************************************************/
+
+//  Close file.
+    status = nc_close(idf);
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/read_GEOS_constants.c b/mvcm/c_tools/read_GEOS_constants.c
new file mode 100644
index 0000000..9b8d06d
--- /dev/null
+++ b/mvcm/c_tools/read_GEOS_constants.c
@@ -0,0 +1,200 @@
+/******************************************************************************
+
+Description:
+
+  Integer function read_GEOS_constants.c
+  Reads desired variables from time-invariant (constant) file (calling argument).
+
+Input arguments:
+  geos_file_cnst        input GEOS constants file name
+
+Output arguments:
+  none
+
+Function output:
+  int return_code       successful completion is zero, otherwise non-zero
+
+  Output variables in ancillary.h
+
+Revision History:
+  07/2021     R. Frey   Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+#include <float.h>
+
+//MVCCM includes
+//#include "granule_geolocation.h"
+//#include "granule.h"
+#include "ancillary.h"
+
+//netcdf includes
+#include <netcdf.h>
+
+/******************************************************************************/
+
+int read_GEOS_constants(char *geos_file_cnst)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+/******************************************************************************/
+
+    size_t lat_len, lon_len, start[3], edge[3];
+    int status, idf, latln_id, lonln_id, nxc, nyc, nxnyc, varid;
+    int return_code = 0;
+
+/******************************************************************************/
+
+    start[0] = 0;
+    start[1] = 0;
+    start[2] = 0;
+    edge[0] = 0;
+    edge[1] = 0;
+    edge[2] = 0;
+
+/******************************************************************************/
+
+//  Open file.
+//  printf("Opening GEOS constants file: %s\n", geos_file_cnst);
+    status = nc_open(geos_file_cnst, NC_NOWRITE, &idf);
+    if (status != NC_NOERR) {
+      printf("Could not open GEOS constants file: %s %d %d\n", geos_file_cnst,
+             idf, status);
+      return (-1);
+    }
+
+/******************************************************************************/
+
+//  Get data array dimensions.  All meteorologial variables are the same size.
+//  First dimension (time) is 1.
+
+    status = nc_inq_dimid(idf, "lat", &latln_id);
+    if(status < 0) {
+      printf("Could not acquire lat-dimension data: %s\n", geos_file_cnst);
+      return_code = -2;
+      return (return_code);
+    }
+    status = nc_inq_dimlen(idf, latln_id, &lat_len);
+
+    status = nc_inq_dimid(idf, "lon", &lonln_id);
+    if(status < 0) {
+      printf("Could not acquire lon-dimension data: %s\n", geos_file_cnst);
+      return_code = -3;
+      return (return_code);
+    }
+    status = nc_inq_dimlen(idf, lonln_id, &lon_len);
+
+/******************************************************************************/
+
+    nxc = lon_len;
+    nyc = lat_len;
+    nxnyc = lat_len * lon_len;
+
+    if ((g_frocn = (float *) malloc(sizeof(float) * nxnyc)) == NULL) {
+      printf("Could not allocate memory for g_frocn\n");
+      return(-4);
+    }
+    if ((g_frlandice = (float *) malloc(sizeof(float) * nxnyc)) == NULL) {
+      printf("Could not allocate memory for g_frlandice\n");
+      return(-4);
+    }
+    if ((g_phis = (float *) malloc(sizeof(float) * nxnyc)) == NULL) {
+      printf("Could not allocate memory for g_glst\n");
+      return(-5);
+    }
+
+/******************************************************************************/
+
+    edge[0] = 1;
+    edge[1] = lat_len;
+    edge[2] = lon_len;
+
+/******************************************************************************/
+
+//  Read ocean fractions.
+
+    status = nc_inq_varid (idf, "FROCEAN", &varid);
+    if(status != NC_NOERR) {
+      printf("Could not find FROCEAN variable in %s\n", geos_file_cnst);
+      return_code = -8;
+      return (return_code);
+    }
+
+    status = nc_get_vara_float(idf, varid, start, edge, g_frocn);
+    if (status != NC_NOERR) {
+      printf("could not get FROCEAN data in %s\n", geos_file_cnst);
+      printf("status: %d\n", status);
+      return_code = -9;
+      return (return_code);
+    }
+
+/******************************************************************************/
+
+//  Read permanent land ice fractions.
+
+    status = nc_inq_varid (idf, "FRLANDICE", &varid);
+    if(status != NC_NOERR) {
+      printf("Could not find FRLANDICE variable in %s\n", geos_file_cnst);
+      return_code = -8;
+      return (return_code);
+    }
+
+    status = nc_get_vara_float(idf, varid, start, edge, g_frlandice);
+    if (status != NC_NOERR) {
+      printf("could not get FROCEAN data in %s\n", geos_file_cnst);
+      printf("status: %d\n", status);
+      return_code = -9;
+      return (return_code);
+    }
+
+/******************************************************************************/
+
+//  Read surface geopotential heights.
+
+    status = nc_inq_varid (idf, "PHIS", &varid);
+    if(status != NC_NOERR) {
+      printf("Could not find PHIS variable in %s\n", geos_file_cnst);
+      return_code = -10;
+      return (return_code);
+    }
+
+    status = nc_get_vara_float(idf, varid, start, edge, g_phis);
+    if (status != NC_NOERR) {
+      printf("could not get PHIS data in %s\n", geos_file_cnst);
+      printf("status: %d\n", status);
+      return_code = -11;
+      return (return_code);
+    }
+
+/******************************************************************************/
+
+//  printf("GEOS frc ocn data: %f %f %f\n", g_frocn[0], g_frocn[9], g_frocn[nxnyc-1]);
+//  printf("GEOS PHIS data: %f %f %f\n", g_phis[0], g_phis[9], g_phis[nxnyc-1]);
+//  fflush;
+
+/******************************************************************************/
+
+//  Close files.
+    status = nc_close(idf);
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/read_GEOS_lndocn.c b/mvcm/c_tools/read_GEOS_lndocn.c
new file mode 100644
index 0000000..88dc7ba
--- /dev/null
+++ b/mvcm/c_tools/read_GEOS_lndocn.c
@@ -0,0 +1,187 @@
+/******************************************************************************
+
+Description:
+
+  Integer function read_GEOS_lndocn.c
+  Reads desired variables from input files (in calling argument list).
+
+Input arguments:
+  geos_file_lnd         input GEOS land property file name
+  geos_file_ocn         input GEOS ocean property file name
+
+Output arguments:
+  none
+
+Function output:
+  int return_code       successful completion is zero, otherwise non-zero
+
+  Output variables in ancillary.h
+
+Revision History:
+  06/2021     R. Frey   Original version
+
+Calls:
+  none
+
+*******************************************************************************/
+
+// Includes
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+#include <float.h>
+
+//MVCCM includes
+//#include "granule_geolocation.h"
+//#include "granule.h"
+#include "ancillary.h"
+
+//netcdf includes
+#include <netcdf.h>
+
+/******************************************************************************/
+
+int read_GEOS_lndocn(char *geos_file_lnd, char *geos_file_ocn)
+
+{
+
+/******************************************************************************/
+
+//  Declarations and initializations.
+
+/******************************************************************************/
+
+    size_t lat_len, lon_len, start[3], edge[3];
+    int status, idfl, idfo, latln_id, lonln_id, nx, ny, nxny, varid;
+    int return_code = 0;
+
+/******************************************************************************/
+
+    start[0] = 0;
+    start[1] = 0;
+    start[2] = 0;
+    edge[0] = 0;
+    edge[1] = 0;
+    edge[2] = 0;
+
+/******************************************************************************/
+
+//  Open files.
+//  printf("Opening GEOS land file: %s\n", geos_file_lnd);
+    status = nc_open(geos_file_lnd, NC_NOWRITE, &idfl);
+    if (status != NC_NOERR) {
+      printf("Could not open GEOS land property file: %s %d %d\n", geos_file_lnd,
+             idfl, status);
+      return (-1);
+    }
+//  printf("Opening GEOS ocean file: %s\n", geos_file_ocn);
+    status = nc_open(geos_file_ocn, NC_NOWRITE, &idfo);
+    if (status != NC_NOERR) {
+      printf("Could not open GEOS ocean property file: %s %d %d\n", geos_file_ocn,
+             idfo, status);
+      return (-1);
+    }
+
+/******************************************************************************/
+
+//  Get data array dimensions.  All meteorologial variables are the same size.
+//  First dimension (time) is 1.
+
+    status = nc_inq_dimid(idfl, "lat", &latln_id);
+    if(status < 0) {
+      printf("Could not acquire lat-dimension data: %s\n", geos_file_lnd);
+      return_code = -2;
+      return (return_code);
+    }
+    status = nc_inq_dimlen(idfl, latln_id, &lat_len);
+
+    status = nc_inq_dimid(idfl, "lon", &lonln_id);
+    if(status < 0) {
+      printf("Could not acquire lon-dimension data: %s\n", geos_file_lnd);
+      return_code = -3;
+      return (return_code);
+    }
+    status = nc_inq_dimlen(idfl, lonln_id, &lon_len);
+
+/******************************************************************************/
+
+    nx = lon_len;
+    ny = lat_len;
+    nxny = lat_len * lon_len;
+    nwp_cells = nxny;
+    nwp_cols = nx;
+    nwp_rows = ny;
+
+//  printf("dims: %d %d %d %d %d %d\n", idfl, latln_id, lonln_id, lat_len, lon_len, nxny);
+
+    if ((g_snow = (float *) malloc(sizeof(float) * nxny)) == NULL) {
+      printf("Could not allocate memory for g_snow\n");
+      return(-4);
+    }
+    if ((g_ice = (float *) malloc(sizeof(float) * nxny)) == NULL) {
+      printf("Could not allocate memory for g_ice\n");
+      return(-5);
+    }
+
+/******************************************************************************/
+
+    edge[0] = 1;
+    edge[1] = lat_len;
+    edge[2] = lon_len;
+
+//  Read snow fractions.
+
+    status = nc_inq_varid (idfl, "FRSNO", &varid);
+    if(status != NC_NOERR) {
+      printf("Could not find FRSNO variable in %s\n", geos_file_lnd);
+      return_code = -8;
+      return (return_code);
+    }
+
+    status = nc_get_vara_float(idfl, varid, start, edge, g_snow);
+    if (status != NC_NOERR) {
+      printf("could not get FRSNO data in %s\n", geos_file_lnd);
+      printf("status: %d\n", status);
+      return_code = -9;
+      return (return_code);
+    }
+
+//  Read ice fractions.
+
+    status = nc_inq_varid (idfo, "FRSEAICE", &varid);
+    if(status != NC_NOERR) {
+      printf("Could not find FRSEAICE variable in %s\n", geos_file_ocn);
+      return_code = -10;
+      return (return_code);
+    }
+
+    status = nc_get_vara_float(idfo, varid, start, edge, g_ice);
+    if (status != NC_NOERR) {
+      printf("could not get FRSEAICE data in %s\n", geos_file_ocn);
+      printf("status: %d\n", status);
+      return_code = -11;
+      return (return_code);
+    }
+
+/******************************************************************************/
+
+//  printf("GEOS snow data: %f %f %f\n", g_snow[0], g_snow[9], g_snow[nxny-1]);
+//  printf("GEOS ice data: %f %f %f\n", g_ice[0], g_ice[9], g_ice[nxny-1]);
+//  fflush;
+
+/******************************************************************************/
+
+//  Close files.
+    status = nc_close(idfl);
+    status = nc_close(idfo);
+
+/******************************************************************************/
+
+    return (return_code);
+
+/******************************************************************************/
+
+}
diff --git a/mvcm/c_tools/snow_mask.c b/mvcm/c_tools/snow_mask.c
new file mode 100644
index 0000000..f7c1c21
--- /dev/null
+++ b/mvcm/c_tools/snow_mask.c
@@ -0,0 +1,226 @@
+/**********************************************************************
+Description:
+
+  Function snow_mask
+
+  Subroutine which implements a quick and dirty version of the snow
+  algorithm by Dorothy Hall, George Riggs and Vince Salmonson.
+  Called from get_pxldat.c.
+
+  Version 2 adds snow from NDSI=0.4 to NDSI=0.1 but also as a function
+  of NDVI (NDVI=0.1 to NDVI=1.0).
+
+Input arguments:
+  satname           Instrument/platform
+                      MODIS_Aqua
+                      MODIS_Terra
+                      VIIRS_NPP
+                      VIIRS_N20
+
+  Input values stored in variables found in pixel.h, granule.h,
+  mask_processing_constants.h, and thresholds.h.
+
+Output arguments:
+  none
+
+  Output stored in 'pxin.ndsi_snow' found in pixel.h.
+
+Revision History:
+  10/2007  R. Frey  Converted to C
+  10/2012  R. Frey  Adapted for MVCCM software.
+  11/2012  R. Frey  Adapted for MVCCM algorithm.
+  04/2014  R. Frey  Added Klein et al. NDSI extension as a function of
+                    NDVI
+  10/2014  R. Frey  Added "mountain" snow test from Thompson et al., 2014
+  01/2015  R. Frey  Added surface temperature test to snow detection and
+                    added 'lsf' to calling argument list
+  10/2015  R. Frey  Added 'Grnlnd_ndsi', 'sm85_11hel', 'sm_lsfcdif', 'sm_wsfcdif',
+                          'sm_bt1
+  11/2015  R. Frey  Code clean-up.
+  04/2018  R. Frey  Added 'prd_ndvi_const'.
+  02/2020  R. Frey  Added logic to include NOAA-20.
+
+***********************************************************************/
+
+/* Includes */
+
+#include <stdio.h>
+#include <math.h>
+//#include "granule.h"
+//#include "pixel.h"
+//#include "mask_processing_constants.h"
+//#include "thresholds.h"
+
+void snow_mask(char *satname, unsigned char lsf)
+
+{
+
+// Declarations
+
+   extern float rintf(float);
+   extern int strcmp(char *, char *);
+
+   int I_bad;
+   int hi_elev_snow;
+
+   float m04;
+   float m01;
+   float m02;
+   float m20;
+   float m29;
+   float m31;
+   float m35;
+   float nirband;
+   float ndsi;
+   float ndvi;
+   float pred_ndvi;
+   float diff;
+   float diff2;
+   float sth85_11;
+   float sth37_11;
+   float sfcdif;
+   float bad_data = -999.0;
+
+// Initializations
+   m04 = pxin.rad[pxin.bnd555];
+   m01 = pxin.rad[pxin.bnd065];
+   m02 = pxin.rad[pxin.bnd086];
+   m20 = pxin.rad[pxin.bnd375];
+   m29 = pxin.rad[pxin.bnd855];
+   m31 = pxin.rad[pxin.bnd11];
+
+   diff = 0.0;
+   diff2 = 0.0;
+   hi_elev_snow = 0;
+
+// Select NIR channel for NDSI based on platform name.
+// Band 6 for Terra, band 7 for Aqua.
+//   if(strcmp(satname, "MODIS_Terra") == 0) {
+//     nirband = pxin.rad[pxin.bnd164];
+//   }
+//   else if(strcmp(satname, "MODIS_Aqua") == 0) {
+//     nirband = pxin.rad[pxin.bnd213];
+//   }
+   if(strcmp(satname, "VIIRS_NPP") == 0) {
+     nirband = pxin.rad[pxin.bnd164];
+   }
+   else if(strcmp(satname, "VIIRS_N20") == 0) {
+     nirband = pxin.rad[pxin.bnd164];
+   }
+   else {
+     printf("Platform name not recognized in snow_mask \n");
+   }
+
+   I_bad = rintf(bad_data);
+
+// Begin snow/ice detection.
+
+// First, check the 11 micron brightness temperature.
+   if (rintf(m31) != I_bad && m31 <= sm_bt11[0]) {
+
+//   Get NDSI index
+     if (rintf(m04) != I_bad && rintf(nirband) != I_bad) {
+
+       ndsi  = (m04 - nirband) / (m04 + nirband);
+       ndvi = (m02 - m01) / (m02 + m01);
+       pred_ndvi = prd_ndvi_const[0] + (3.0 * ndsi);
+
+       if(pxin.land && (!pxin.Greenland) && (!pxin.Antarctic) ) {
+         if(pxin.elev > 1000.0) {
+           hi_elev_snow = 1;
+           if(ndsi > 0.0) pxin.ndsi_snow = 1;
+         }
+         else if ( (ndsi > sm_ndsi[0] && m02 > sm_ref2[0]) ||
+              (ndsi > 0.1 && ndsi <= sm_ndsi[0] && ndvi <= pred_ndvi) ) {
+           pxin.ndsi_snow = 1;
+         }
+       }
+       else if( pxin.land && pxin.Greenland) {
+         if( ndsi > Grnlnd_ndsi[0] ) pxin.ndsi_snow = 1;
+       }
+       else {
+         if (ndsi > sm_ndsi[0] && m02 > sm_ref2[0]) pxin.ndsi_snow = 1;
+       }
+
+//     printf("ndsi: %f %f %f %f %f %f %f %f %d\n", m01, m02, m04, nirband, ndsi, ndvi, prd_ndvi_const[0], pred_ndvi, pxin.ndsi_snow);
+
+       if(pxin.ndsi_snow) {
+
+//       If in sunglint region, disregard if between -60 and 50 degrees latitude.
+         if(pxin.water && pxin.sunglint && pxin.lat <= 50.0 && pxin.lat >= -60.0) {
+           pxin.ndsi_snow = 0;
+         }
+
+//       Check for ice clouds mis-identified as snow. Note modified BTD thresholds for Greenland.
+         diff = -99.0;
+         diff2 = -99.0;
+         sth85_11 = -99.0;
+         sth37_11 = -99.0;
+         if(rintf(m29) != I_bad && rintf(m31) != I_bad) {
+           diff = m29 - m31;
+           sth85_11 = sm85_11[0];
+           if(rintf(m20) != I_bad && !(pxin.Greenland && pxin.hi_elev)) {
+             diff2 = m20 - m31;
+             sth37_11 = sm37_11[0];
+             if(diff >= sth85_11 && diff2 >= sth37_11) {
+               pxin.ndsi_snow = 0;
+             }
+           }
+           else {
+             if(pxin.Greenland && pxin.hi_elev) {
+               sth85_11 = sm85_11hel[0];
+             }
+             else {
+               sth85_11 = sm85_11[0];
+             }
+             if(diff >= sth85_11) {
+               pxin.ndsi_snow = 0;
+             }
+           }
+         }
+//       printf("ice chk: %d %d %f %f\n\n", pxin.Greenland, pxin.hi_elev, diff, sth85_11);
+
+//       Check for water clouds mis-identified as snow.
+         diff = -99.0;
+         sth37_11 = -99.0;
+         if(m20 != I_bad && m31 != I_bad) {
+           if( !(pxin.hi_elev && pxin.Greenland) ) {
+             diff = m20 - m31;
+             if(pxin.hi_elev) {
+               sth37_11 = sm37_11hel[0];
+             }
+             else {
+               sth37_11 = sm37_11[0];
+             }
+             if(diff >= sth37_11) {
+               pxin.ndsi_snow = 0;
+             }
+           }
+         }
+
+//       Check value of NIR reflectance.
+         if( !pxin.hi_elev && !hi_elev_snow) {
+           if(nirband > sm_mnir[0]) {
+             pxin.ndsi_snow = 0;
+           }
+
+//         Check 11 um vs. surface temperature.
+           sfcdif = pxin.sfctmp - m31;
+           if(lsf == 1 || lsf == 2) {
+//           Land or coast.
+             if(sfcdif > sm_lsfcdif[0]) pxin.ndsi_snow = 0;
+           }
+           else if(lsf == 0 || lsf > 2) {
+//           Water.
+             if(sfcdif > sm_wsfcdif[0] && pxin.sfctmp > sm_bt1[0]) pxin.ndsi_snow = 0;
+           }
+
+         }
+
+
+       }
+     }
+   }
+
+
+}
diff --git a/mvcm/c_tools/swap_bytes.c b/mvcm/c_tools/swap_bytes.c
new file mode 100644
index 0000000..47fd705
--- /dev/null
+++ b/mvcm/c_tools/swap_bytes.c
@@ -0,0 +1,126 @@
+# include <stdlib.h>
+
+int swapbyte4(void *, int);
+void swbyt4_(void *, int *);
+void fbyte4_(void *, int *);
+
+/* ===== swapbyte4 ================================================== */
+/*
+*| Name:
+*|    swapbyte4 - Flips 4 byte words into network byte order.
+*|
+*| Interface:
+*|    int
+*|    swapbyte4 (void *buf, int n_words)
+*|
+*| Input:
+*|    buf      - Buffer to be flipped.
+*|    n_words  - Number of words to flip.
+*|
+*| Input and Output:
+*|    none
+*|
+*| Output:
+*|    none
+*|
+*| Return values:
+*|    0        - Success.
+*|   -1        - buf pointer is NULL.
+*|
+*| Remarks:
+*|    Network byte order is big endian.
+*|
+*| Categories:
+*|    converter
+*|    utility
+*/
+
+int
+  swapbyte4 (void *buf, int n_words)
+{
+  int          *b_ptr = (int *) buf;
+  int           nw    = (int) n_words;
+
+  if (b_ptr == (int *) NULL)
+  {
+    return (-1);
+  }
+
+  if (n_words > 0)
+  {
+    swbyt4_ (b_ptr, &nw);
+  }
+  return (0);
+}
+
+/*
+*$ Name:
+*$      swbyt4   - Re-orders bytes if internal representation is not
+*$                 big-endian.
+*$
+*$ Interface:
+*$      subroutine
+*$      swbyt4(integer buf(*), integer n)
+*$
+*$ Input:
+*$      n        - Number of 4 byte swaps to be made if order is not
+*$                 big-endian.
+*$
+*$ Input and Output:
+*$      buf      - Array containing bytes to be swapped.
+*$
+*$ Output:
+*$      none
+*$
+*$ Return values:
+*$      none
+*$
+*$ Remarks:
+*$      none
+*$
+*$ Categories:
+*$      utility
+*/
+
+void
+swbyt4_(void *buf, int *n)
+{
+        /* determine byte order from first principles */
+        union
+        {
+                char            bytes[sizeof(int)];
+                int             word;
+        }               q;
+
+        q.word = 1;
+        if (q.bytes[3] == 1)
+                return;
+
+        /* swap bytes */
+        fbyte4_(buf, n);
+}
+
+   /* buffer - INTEGER*4 array to switch bytes  */
+   /* n      - INTEGER number of 4 byte switches */
+
+void
+fbyte4_(void *buffer, int *num)
+{
+  char *cbuf = (char *)buffer;
+  int i, n;
+
+  n = *num;
+  for (i = 0; i < n; i++)
+  {
+    char b;
+
+    b = cbuf[0];
+    cbuf[0] = cbuf[3];
+    cbuf[3] = b;
+    b = cbuf[1];
+    cbuf[1] = cbuf[2];
+    cbuf[2] = b;
+    cbuf += 4;
+  }
+}
+
diff --git a/mvcm/conf.py b/mvcm/conf.py
new file mode 100644
index 0000000..bc586ef
--- /dev/null
+++ b/mvcm/conf.py
@@ -0,0 +1,274 @@
+import numpy as np
+
+
+def test(flipped=False):
+    bt = np.arange(265, 275)
+    if flipped is False:
+        thr = np.array([267, 270, 273, 1, 1])
+    else:
+        thr = np.array([273, 270, 267, 1, 1])
+    c = conf_test(bt, thr)
+    print(c)
+
+
+def test_dble(flipped=False):
+    bt = np.arange(260, 282)
+    if flipped is False:
+        thr = np.array([264, 267, 270, 273, 276, 279, 1, 1])
+    else:
+        thr = np.array([279, 276, 273, 270, 267, 264, 1, 1])
+    c = conf_test_dble(bt, thr)
+    print(c)
+
+
+def conf_test_new(rad: np.ndarray,
+                  thr: np.ndarray) -> np.ndarray:
+    """Assuming a linear function between min and max confidence level, the plot below shows
+    how the confidence (y axis) is computed as function of radiance (x axis).
+    This case illustrates alpha < gamma, obviously in case alpha > gamma, the plot would be
+    flipped.
+                       gamma
+    c  1                 ________
+    o  |                /
+    n  |               /
+    f  |              /
+    i  |     beta    /
+    d 1/2    |....../
+    e  |           /
+    n  |          /
+    c  |         /
+    e  0________/
+       |      alpha
+    --------- radiance ---------->
+    """
+
+    radshape = rad.shape
+    rad = rad.ravel()
+    thr = np.array(thr)
+    if thr.ndim == 1:
+        thr = np.full((rad.shape[0], 4), thr[:4]).T
+
+    coeff = np.power(2, (thr[3, :] - 1))
+    locut = thr[0, :]
+    beta = thr[1, :]
+    hicut = thr[2, :]
+    power = thr[3, :]
+    confidence = np.zeros(rad.shape)
+
+    alpha, gamma = np.empty(rad.shape), np.empty(rad.shape)
+    flipped = np.zeros(rad.shape)
+
+    gamma[hicut > locut] = thr[2, hicut > locut]
+    alpha[hicut > locut] = thr[0, hicut > locut]
+    flipped[hicut > locut] = 0
+    gamma[hicut < locut] = thr[0, hicut < locut]
+    alpha[hicut < locut] = thr[2, hicut < locut]
+    flipped[hicut < locut] = 1
+
+    # Rad between alpha and beta
+    range_ = 2. * (beta - alpha)
+    s1 = (rad - alpha) / range_
+    idx = np.nonzero((rad <= beta) & (flipped == 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+    idx = np.nonzero((rad <= beta) & (flipped == 1))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Rad between beta and gamma
+    range_ = 2. * (beta - gamma)
+    s1 = (rad - gamma) / range_
+    idx = np.nonzero((rad > beta) & (flipped == 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+    idx = np.nonzero((rad > beta) & (flipped == 1))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Rad outside alpha-gamma interval
+    confidence[(rad > gamma) & (flipped is False)] = 1
+    confidence[(rad < alpha) & (flipped is False)] = 0
+    confidence[(rad > gamma) & (flipped is True)] = 0
+    confidence[(rad < alpha) & (flipped is True)] = 1
+
+    confidence = np.clip(confidence, 0, 1)
+
+    return confidence.reshape(radshape)
+
+
+def conf_test(rad, thr):
+    '''
+    Assuming a linear function between min and max confidence level, the plot below shows
+    how the confidence (y axis) is computed as function of radiance (x axis).
+    This case illustrates alpha < gamma, obviously in case alpha > gamma, the plot would be
+    flipped.
+                       gamma
+    c  1                 ________
+    o  |                /
+    n  |               /
+    f  |              /
+    i  |     beta    /
+    d 1/2    |....../
+    e  |           /
+    n  |          /
+    c  |         /
+    e  0________/
+       |      alpha
+    --------- radiance ---------->
+    '''
+
+    radshape = rad.shape
+    rad = rad.reshape(np.prod(radshape))
+
+    if thr.ndim == 1:
+        thr = np.full((rad.shape[0], 4), thr[:4]).T
+
+    coeff = np.power(2, (thr[3] - 1))
+    hicut = thr[2, :]
+    beta = thr[1, :]
+    locut = thr[0, :]
+    power = thr[3, :]
+    confidence = np.zeros(rad.shape)
+
+    alpha, gamma = np.empty(rad.shape), np.empty(rad.shape)
+    flipped = np.zeros(rad.shape)
+
+    gamma[hicut > locut] = thr[2, hicut > locut]
+    alpha[hicut > locut] = thr[0, hicut > locut]
+    flipped[hicut > locut] = 0
+    gamma[hicut < locut] = thr[0, hicut < locut]
+    alpha[hicut < locut] = thr[2, hicut < locut]
+    flipped[hicut < locut] = 1
+
+    # Rad between alpha and beta
+    range_ = 2. * (beta - alpha)
+    s1 = (rad - alpha) / range_
+    idx = np.nonzero((rad <= beta) & (flipped == 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+    idx = np.nonzero((rad <= beta) & (flipped == 1))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Rad between beta and gamma
+    range_ = 2. * (beta - gamma)
+    s1 = (rad - gamma) / range_
+    idx = np.nonzero((rad > beta) & (flipped == 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+    idx = np.nonzero((rad > beta) & (flipped == 1))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Rad outside alpha-gamma interval
+    confidence[(rad > gamma) & (flipped is False)] = 1
+    confidence[(rad < alpha) & (flipped is False)] = 0
+    confidence[(rad > gamma) & (flipped is True)] = 0
+    confidence[(rad < alpha) & (flipped is True)] = 1
+
+    confidence[confidence > 1] = 1
+    confidence[confidence < 0] = 0
+
+    return confidence
+
+
+def conf_test_dble(rad, coeffs):
+    # '''
+    #            gamma1                         gamma2
+    #    c  1_______                               ________
+    #    o  |       \                             /
+    #    n  |        \                           /
+    #    f  |         \                         /
+    #    i  |          \   beta1       beta2   /
+    #    d 1/2          \....|          |...../
+    #    e  |            \                   /
+    #    n  |             \                 /
+    #    c  |              \               /
+    #    e  0               \_____________/
+    #       |             alpha1       alpha2
+    #    --------------------- radiance ------------------------->
+    # '''
+
+    coeffs = np.array(coeffs)
+    radshape = rad.shape
+    rad = rad.ravel()
+    confidence = np.zeros(rad.shape)
+
+    alpha1, gamma1 = np.empty(rad.shape), np.empty(rad.shape)
+    alpha2, gamma2 = np.empty(rad.shape), np.empty(rad.shape)
+
+    if coeffs.ndim == 1:
+        coeffs = np.full((rad.shape[0], 7), coeffs[:7]).T
+
+    gamma1 = coeffs[0, :]
+    beta1 = coeffs[1, :]
+    alpha1 = coeffs[2, :]
+    alpha2 = coeffs[3, :]
+    beta2 = coeffs[4, :]
+    gamma2 = coeffs[5, :]
+    power = coeffs[6, :]
+
+    coeff = np.power(2, (power - 1))
+    # radshape = rad.shape
+    # rad = rad.reshape((rad.shape[0]*rad.shape[1]))
+
+    # ## Find if interval between inner cutoffs passes or fails test
+
+    # Inner region fails test
+
+    # Value is within range of lower set of limits
+    range_ = 2. * (beta1 - alpha1)
+    s1 = (rad - alpha1) / range_
+    idx = np.nonzero((rad <= alpha1) & (rad >= beta1) & (alpha1 - gamma1 > 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    range_ = 2. * (beta1 - gamma1)
+    s1 = (rad - gamma1) / range_
+    idx = np.nonzero((rad >= gamma1) & (rad < beta1) & (alpha1 - gamma1 > 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Value is within range of upper set of limits
+    range_ = 2. * (beta2 - alpha2)
+    s1 = (rad - alpha2) / range_
+    idx = np.nonzero((rad > alpha1) & (rad <= beta2) & (alpha1 - gamma1 > 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    range_ = 2. * (beta2 - gamma2)
+    s1 = (rad - gamma2) / range_
+    idx = np.nonzero((rad > alpha1) & (rad > beta2) & (alpha1 - gamma1 > 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Check for value beyond function range
+    confidence[(alpha1 - gamma1 > 0) & (rad > alpha1) & (rad < alpha2)] = 0
+    confidence[(alpha1 - gamma1 > 0) & ((rad < gamma1) | (rad > gamma2))] = 1
+
+    ###
+
+    # Inner region passes test
+    print("I NEED TO REVIEW THIS TO WRITE IT MORE CLEARLY")
+    # FOR NOW ALPHA AND GAMMA ARE SWITCHED BECAUSE OF HOW THE ARRAYS ARE DEFINED.
+    # THINK ON HOW THIS COULD BE WRITTEN SO THAT IT'S EASIER TO UNDERSTAND (AND DEBUG)
+    # Value is within range of lower set of limits
+    range_ = 2 * (beta1 - alpha1)
+    s1 = (rad - alpha1) / range_
+    idx = np.nonzero((rad > alpha1) & (rad <= gamma1) & (rad <= beta1) & (alpha1 - gamma1 <= 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    range_ = 2 * (beta1 - gamma1)
+    s1 = (rad - gamma1) / range_
+    idx = np.nonzero((rad > alpha1) & (rad <= gamma1) & (rad > beta1) & (alpha1 - gamma1 <= 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    # Values is within range of upper set of limits
+    range_ = 2 * (beta2 - alpha2)
+    s1 = (rad - alpha2) / range_
+    idx = np.nonzero((rad > gamma2) & (rad < alpha2) & (rad >= beta2) & (alpha1 - gamma1 <= 0))
+    confidence[idx] = 1.0 - coeff[idx] * np.power(s1[idx], power[idx])
+
+    range_ = 2 * (beta2 - gamma2)
+    s1 = (rad - gamma2) / range_
+    idx = np.nonzero((rad > gamma2) & (rad < alpha2) & (rad < beta2) & (alpha1 - gamma1 <= 0))
+    confidence[idx] = coeff[idx] * np.power(s1[idx], power[idx])
+
+    confidence[(alpha1 - gamma1 <= 0) & ((rad > gamma1) | (rad < gamma2))] = 0
+    confidence[(alpha1 - gamma1 <= 0) & (rad <= alpha1) & (rad >= alpha2)] = 1
+
+    # confidence = np.clip(confidence, 0, 1)
+
+    return confidence.reshape(radshape)
+
+
+if __name__ == "__main__":
+    test_dble()
diff --git a/mvcm/main.py b/mvcm/main.py
new file mode 100644
index 0000000..27fa0ed
--- /dev/null
+++ b/mvcm/main.py
@@ -0,0 +1,532 @@
+try:
+    from ruamel import yaml as yml
+except ImportError:
+    import ruamel_yaml as yml
+
+import numpy as np
+
+from glob import glob
+
+import read_data as rd
+import spectral_tests as tst
+import scene as scn
+import restoral
+
+# #################################################################### #
+# TEST CASE
+# data:
+_datapath = '/ships19/hercules/pveglio/mvcm_viirs_hires'
+_fname_mod02 = glob(f'{_datapath}/VNP02MOD.A2022173.1454.001.*.uwssec_bowtie_restored.nc')[0]
+_fname_mod03 = glob(f'{_datapath}/VNP03MOD.A2022173.1454.001.*.uwssec.nc')[0]
+_fname_img02 = glob(f'{_datapath}/VNP02IMG.A2022173.1454.001.*.uwssec_bowtie_restored.nc')[0]
+_fname_img03 = glob(f'{_datapath}/VNP03IMG.A2022173.1454.001.*.uwssec.nc')[0]
+
+# thresholds:
+_threshold_file = '/home/pveglio/mvcm/thresholds.mvcm.snpp.v0.0.1.yaml'
+
+# ancillary files:
+_geos_atm_1 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1200.V01.nc4'
+_geos_atm_2 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1500.V01.nc4'
+_geos_land = 'GEOS.fpit.asm.tavg1_2d_lnd_Nx.GEOS5124.20220622_1430.V01.nc4'
+_geos_ocean = 'GEOS.fpit.asm.tavg1_2d_ocn_Nx.GEOS5124.20220622_1430.V01.nc4'
+_geos_constants = 'GEOS.fp.asm.const_2d_asm_Nx.00000000_0000.V01.nc4'
+_ndvi_file = 'NDVI.FM.c004.v2.0.WS.00-04.177.hdf'
+_sst_file = 'oisst.20220622'
+_eco_file = 'goge1_2_img.v1'
+
+# #################################################################### #
+
+
+def main(satellite: str = 'snpp',
+         sensor: str = 'viirs',
+         data_path: str = _datapath,
+         mod02: str = _fname_mod02,
+         mod03: str = _fname_mod03,
+         img02: str = None,
+         img03: str = None,
+         threshold_file: str = _threshold_file,
+         geos_atm_1: str = _geos_atm_1,
+         geos_atm_2: str = _geos_atm_2,
+         geos_land: str = _geos_land,
+         geos_ocean: str = _geos_ocean,
+         geos_constants: str = _geos_constants,
+         ndvi_file: str = _ndvi_file,
+         sst_file: str = _sst_file) -> None:
+    """Main function for the MVCM.
+
+    Parameters
+    ----------
+    satellite: str
+        satellite name, not case-sensitive. Available options: [snpp, ]
+    sensor: str
+        sensor name, not case-sensitive. Available options: [viirs, ]
+    data_path: str
+        path where the data is stored
+    mod02: str
+        VIIRS MOD02 file name
+    mod03: str
+        VIIRS MOD03 file name
+    img02: [optional] str
+        VIIRS IMG02 file name
+    img03: [optional] str
+        VIIRS IMG03 file name
+    threshold_file: str
+        thresholds file name
+    geos_atm_1: str
+        file name of first GEOS5 file for atmospheric parameters
+    goes_atm_2: str
+        file name of second GEOS5 file for atmospheric parameters
+    geos_land: str
+        file name of GEOS5 land parameters
+    geos_ocean: str
+        file name of GEOS5 ocean parameters
+    geos_constants: str
+        file name of GEOS5 constants
+    ndvi_file: str
+        NDVI file name
+    sst_file:
+        Sea surface temperature file name
+
+    Returns
+    -------
+    None
+    """
+
+    file_names = {'MOD02': f'{mod02}',
+                  'MOD03': f'{mod03}',
+                  'IMG02': f'{img02}',
+                  'IMG03': f'{img03}',
+                  'GEOS_atm_1': f'{geos_atm_1}',
+                  'GEOS_atm_2': f'{geos_atm_2}',
+                  'GEOS_land': f'{geos_land}',
+                  'GEOS_ocean': f'{geos_ocean}',
+                  'GEOS_constants': f'{geos_constants}',
+                  'NDVI': f'{ndvi_file}',
+                  'SST': f'{sst_file}',
+                  'ANC_DIR': f'{data_path}/ancillary'
+                  }
+
+    with open(threshold_file) as f:
+        text = f.read()
+    thresholds = yml.safe_load(text)
+
+    sunglint_angle = thresholds['Sun_Glint']['bounds'][3]
+
+    viirs_data = rd.get_data(satellite, sensor, file_names, sunglint_angle)
+
+    # Initialize the confidence arrays for the various test groups
+    cmin_G1 = np.ones(viirs_data.M01.shape)
+    cmin_G2 = np.ones(viirs_data.M01.shape)
+    cmin_G3 = np.ones(viirs_data.M01.shape)
+    cmin_G4 = np.ones(viirs_data.M01.shape)
+    cmin_G5 = np.ones(viirs_data.M01.shape)
+
+    ##########################################################
+
+    """
+    for scene_name in ['Land_Day', 'Land_Day_Coast', 'Land_Day_Desert_Coast',
+                       'Ocean_Day', 'Ocean_Night', 'Polar_Day_Ocean', 'Polar_Night_Ocean',
+                       'Polar_Day_Land', 'Polar_Day_Coast', 'Polar_Day_Desert',
+                       'Polar_Day_Desert_Coast', 'Polar_Day_Snow', 'Land_Night',
+                       'Polar_Night_Land', 'Polar_Night_Snow', 'Day_Snow', 'Night_Snow']:
+    """
+    for scene_name in ['Ocean_Day']:
+        MyScene = tst.CloudTests(viirs_data, scene_name, thresholds)
+
+        cmin_G1, bit1 = MyScene.test_11um('M15', cmin_G1, test_name='11um_Test')
+        cmin_G1, bit2 = MyScene.sst_test('M15', 'M16', cmin_G1, test_name='SST_Test')
+
+        cmin_G2, bit3 = MyScene.bt_diff_86_11um('M14-M15', cmin_G2, test_name='8.6-11um_Test')
+        cmin_G2, bit4 = MyScene.test_11_12um_diff('M15-M16', cmin_G2, test_name='11-12um_Cirrus_Test')
+        cmin_G2, bit5 = MyScene.oceanic_stratus_11_4um_test('M15-M12', cmin_G2,
+                                                            test_name='11-4um_Oceanic_Stratus_Test')
+
+        cmin_G3, bit6 = MyScene.nir_reflectance_test('M07', cmin_G3, test_name='NIR_Reflectance_Test')
+        cmin_G3, bit7 = MyScene.vis_nir_ratio_test('M07-M05ratio', cmin_G3, test_name='Vis/NIR_Ratio_Test')
+        cmin_G3, bit8 = MyScene.nir_reflectance_test('M10', cmin_G3, test_name='1.6_2.1um_NIR_Reflectance_Test')
+        cmin_G3, bit9 = MyScene.visible_reflectance_test('M128', cmin_G3, test_name='Visible_Reflectance_Test')
+        cmin_G3, bit10 = MyScene.gemi_test('GEMI', cmin_G3, test_name='GEMI_Test')
+
+        cmin_G4, bit11 = MyScene.test_1_38um_high_clouds('M09', cmin_G4, test_name='1.38um_High_Cloud_Test')
+
+        cmin_G5, bit12 = MyScene.thin_cirrus_4_12um_BTD_test('M13-M16', cmin_G5,
+                                                             test_name='4-12um_BTD_Thin_Cirrus_Test')
+
+        cmin = cmin_G1 * cmin_G2 * cmin_G3 * cmin_G4 * cmin_G5
+
+        if scene_name in ['Ocean_Day']:
+            # total_bit = np.full(viirs_data.M01.shape, 4)
+            total_bit = bit1 + bit2 + bit4 + bit11
+            sunglint_angle = thresholds['Sun_Glint']['bounds'][3]
+            scene_flags = scn.find_scene(viirs_data, sunglint_angle)
+
+            # idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['ice'] == 0) &
+            #                  (scene_flags['uniform'] == 1) & (cmin <= 0.99) & (cmin >= 0.05))
+            # cmin[idx] = restoral.spatial(viirs_data, thresholds['Sun_Glint'], scene_flags, cmin)[idx]
+
+            idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['sunglint'] == 1) &
+                             (scene_flags['uniform'] == 1) & (cmin <= 0.95))
+            cmin[idx] = restoral.sunglint(viirs_data, thresholds['Sun_Glint'], total_bit, cmin)[idx]
+
+        # MVCM = tst.ComputeTests(viirs_data, scene_name, thresholds)
+        # cmin = MVCM.clear_sky_restoral(cmin)
+
+    # return cmin
+    np.savez('test_ams', confidence=cmin, lat=viirs_data.latitude.values, lon=viirs_data.longitude.values)
+
+    ##########################################################
+
+    """
+    # ------------------- #
+    # ### Testing Setup ###
+    # ------------------- #
+
+    perform = {'11um BT Test': True,
+               'CO2 High Clouds Test': False,
+               'Water Vapor High Clouds Test': False,
+               'Surface Temperature Test': False,
+               'SST Test': False,
+               '8.6-11um BT Difference Test': False,
+               '11-12um BTD Thin Cirrus Test': False,
+               '11-4um BT Difference Test': False,
+               '7.3-11um BT Difference Mid-level Clouds': False,
+               'Water Vapor Cloud Test': False,
+               '11um BT Variability Test': False,
+               '11-4um BTD Oceanic Stratus': False,
+               'NIR Reflectance Test': False,
+               'Vis/NIR Ratio Test': False,
+               '1.6um or 2.1um NIR Reflectance Test': False,
+               'Visible Reflectance Test': False,
+               'GEMI Test': False,
+               '1.38um High Cloud Test': False,
+               '4-12um BTD Thin Cirrus Test': False
+               }
+
+    # --------------------- #
+    # ### Group 1 Tests ### #
+    # --------------------- #
+
+    if perform['11um BT Test'] is True:
+        # 11um BT Test
+        for scene_name in ['Ocean_Day', 'Ocean_Night', 'Polar_Ocean_Day', 'Polar_Ocean_Night']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G1 = SceneType.single_threshold_test('11um_Test', 'M15', cmin_G1)
+
+    if perform['CO2 High Clouds Test'] is True:
+        # CO2 High Clouds Test
+        for scene_name in ['Land_Day', 'Land_Night', 'Land_Day_Coast', 'Land_Day_Desert',
+                           'Land_Day_Desert_Coast', 'Ocean_Day', 'Ocean_Night', 'Day_Snow', 'Night_Snow']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G1 = SceneType.single_threshold_test('CO2_High_Clouds_tests', 'bad_data', cmin_G1)
+
+    if perform['Water Vapor High Clouds Test'] is True:
+        # Water Vapor High Clouds Test
+        for scene_name in ['Land_Day', 'Land_Night', 'Land_Day_Coast', 'Land_Day_Desert',
+                           'Land_Day_Desert_Coast', 'Ocean_Day', 'Ocean_Night', 'Polar_Day_Land',
+                           'Polar_Night_Land', 'Polar_Day_Coast', 'Polar_Day_Desert',
+                           'Polar_Day_Desert_Coast', 'Polar_Day_Snow', 'Polar_Night_Snow', 'Polar_Ocean_Day',
+                           'Polar_Ocean_Night', 'Day_Snow', 'Night_Snow', 'Antarctic_Day']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G1 = SceneType.single_threshold_test('Water_Vapor_High_Clouds_tests', 'bad_data', cmin_G1)
+
+    if perform['Surface Temperature Test'] is True:
+        # Surface Temperature Test
+        # NOTE: this test requires the thresholds to be preprocessed
+        for scene_name in ['Land_Night', 'Polar_Night_Land']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G1 = SceneType.single_threshold_test('Surface_Temperature_Test', 'M15-M16', cmin_G1)
+
+    if perform['SST Test'] is True:
+        # SST Test
+        for scene_name in ['Ocean_Day', 'Ocean_Night', 'Polar_Ocean_Day', 'Polar_Ocean_Night']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G1 = SceneType.single_threshold_test('SST_Test', 'M15-M16', cmin_G1)
+
+    # --------------------- #
+    # ### Group 2 tests ### #
+    # --------------------- #
+
+    if perform['8.6-11um BT Difference Test'] is True:
+        # 8.6-11um BT Difference Test
+        for scene_name in ['Ocean_Day', 'Ocean_Night', 'Polar_Ocean_Day', 'Polar_Ocean_Night']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G2 = SceneType.single_threshold_test('8.6-11um_Test', 'M15-M14', cmin_G2)
+
+    if perform['11-12um BTD Thin Cirrus Test'] is True:
+        # 11-12um BT BTD Transmissive Cirrus Test
+        # NOTE: some of the tests have some differences in how the thresholds are derived
+        # The commented list scene_name is the complete list, the one currently in use is to test that
+        # the template code works at least with a subset of scenes that have the same way of deriving the
+        # thresholds
+        # for scene_name in ['Land_Day', 'Land_Day_Coast', 'Land_Day_Desert', 'Land_Day_Desert_Coast',
+        #                    'Ocean_Day', 'Ocean_Night', 'Polar_Day_Land', 'Polar_Day_Coast',
+        #                    'Polar_Day_Desert', 'Polar_Day_Desert_Coast', 'Polar_Day_Snow', 'Polar_Night_Snow',
+        #                    'Polar_Ocean_Day', 'Polar_Ocean_Night', 'Day_Snow', 'Night_Snow']:
+        for scene_name in ['Land_Day', 'Land_Day_Coast', 'Land_Day_Desert', 'Land_Day_Desert_Coast',
+                           'Ocean_Day', 'Ocean_Night', 'Polar_Ocean_Day', 'Polar_Ocean_Night']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G2 = SceneType.single_threshold_test('11-12um_Cirrus_Test', 'M15-M16', cmin_G2)
+
+    if perform['11-4um BT Difference Test'] is True:
+        # 11-4um BT Difference Test
+        for scene_name in ['Ocean_Night', 'Polar_Ocean_Night']:
+            pass
+
+    if perform['7.3-11um BT Difference Mid-level Clouds'] is True:
+        for scene_name in ['Land_Night', 'Polar_Night_Land', 'Polar_Night_Snow', 'Night_Snow']:
+            pass
+
+    if perform['Water Vapor Cloud Test'] is True:
+        for scene_name in ['Polar_Night_Ocean']:
+            pass
+
+    if perform['11um BT Variability Test'] is True:
+        for scene_name in ['Ocean_Night', 'Polar_Night_Ocean']:
+            pass
+
+    if perform['11-4um BTD Oceanic Stratus'] is True:
+        # 11-4um BT Difference for oceanic stratus (low emissivity water cloud) Test
+        for scene_name in ['Land_Day', 'Land_Day_Coast', 'Land_Day_Desert', 'Land_Day_Desert_Coast',
+                           'Ocean_Day', 'Ocean_Night', 'Polar_Day_Land', 'Polar_Day_Coast',
+                           'Polar_Day_Desert', 'Polar_Day_Desert_Coast', 'Polar_Day_Snow', 'Polar_Night_Snow',
+                           'Polar_Ocean_Day', 'Polar_Ocean_Night', 'Day_Snow', 'Night_Snow']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G2 = SceneType.single_threshold_test('11-4um_Oceanic_Stratus_Test', 'M15-M13', cmin_G2)
+
+    # --------------------- #
+    # ### Group 3 tests ### #
+    # --------------------- #
+
+    if perform['NIR Reflectance Test'] is True:
+        for scene_name in ['Ocean_Day', 'Polar_Ocean_Day']:
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G3 = SceneType.single_thredhold_test('NIR_Reflectance_Test', 'M07', cmin_G3)
+
+    if perform['Vis/NIR Ratio Test'] is True:
+        for scene_name in ['Ocean_Day', 'Polar_Ocean_Day']:
+            pass
+
+    if perform['1.6um or 2.1um NIR Reflectance Test'] is True:
+        for scene_name in ['Ocean_Day', 'Polar_Ocean_Day']:
+            pass
+
+    if perform['Visible Reflectance Test'] is True:
+        for scene_name in ['Land_Day', 'Land_Day_Coast', 'Land_Day_Desert', 'Land_Day_Desert_Coast',
+                           'Polar_Day_Land', 'Polar_Day_Coast', 'Polar_Day_Desert', 'Polar_Day_Desert_Coast']:
+            pass
+
+    if perform['GEMI Test'] is True:
+        for scene_name in ['Land_Day_Desert', 'Polar_Day_Desert']:
+            pass
+
+    # --------------------- #
+    # ### Group 4 tests ### #
+    # --------------------- #
+
+    if perform['1.38um High Cloud Test'] is True:
+        # for scene_name in ['Land_Day', 'Land_Day_Coast', 'Land_Day_Desert', 'Land_Day_Desert_Coast',
+        #                    'Ocean_Day', 'Polar_Day_Land', 'Polar_Day_Coast', 'Polar_Day_Desert',
+        #                    'Polar_Day_Desert_Coast', 'Polar_Ocean_Day', 'Day_Snow']:
+        for scene_name in ['Land_Day', 'Land_Day_Coast', 'Land_Day_Desert', 'Land_Day_Desert_Coast',
+                           'Polar_Day_Land', 'Polar_Day_Coast', 'Polar_Day_Desert',
+                           'Polar_Day_Desert_Coast', 'Day_Snow']:
+            # The current loop is missing Ocean_Day and Polar_Ocean_Day because they need some
+            # preprocessing to compute the thresholds. Once things are implemented I can use the commented
+            # loop
+            SceneType = CloudTests(viirs_data, scene_name, thresholds)
+            cmin_G4 = SceneType.single_threshold_test('1.38um_High_Cloud_Test', 'M09', cmin_G4)
+
+    # --------------------- #
+    # ### Group 5 tests ### #
+    # --------------------- #
+
+    if perform['4-12um BTD Thin Cirrus Test'] is True:
+        for scene_name in ['Land_Night', 'Polar_Night_Land', 'Polar_Night_Snow', 'Night_Snow']:
+            pass
+
+    cmin_total = cmin_G1 * cmin_G2 * cmin_G3 * cmin_G4 * cmin_G5
+    return cmin_total
+    """
+
+    '''
+    Land_Day = CloudTests(viirs_data, 'Land_Day', thresholds)
+    Land_Night = CloudTests(viirs_data, 'Land_Night', thresholds)
+    Land_Day_Coast = CloudTests(viirs_data, 'Land_Day_Coast', thresholds)
+    Land_Day_Desert = CloudTests(viirs_data, 'Land_Day_Desert', thresholds)
+    Land_Day_Desert_Coast = CloudTests(viirs_data, 'Land_Day_Desert_Coast', thresholds)
+    Ocean_Day = CloudTests(viirs_data, 'Ocean_Day', thresholds)
+    Ocean_Night = CloudTests(viirs_data, 'Ocean_Night', thresholds)
+    Polar_Day_Land = CloudTests(viirs_data, 'Polar_Day_Land', thresholds)
+    Polar_Night_Land = CloudTests(viirs_data, 'Polar_Night_Land', thresholds)
+    Polar_Day_Coast = CloudTests(viirs_data, 'Polar_Day_Coast', thresholds)
+    Polar_Day_Desert = CloudTests(viirs_data, 'Polar_Day_Desert', thresholds)
+    Polar_Day_Desert_Coast = CloudTests(viirs_data, 'Polar_Day_Desert_Coast', thresholds)
+    Polar_Day_Snow = CloudTests(viirs_data, 'Polar_Day_Snow', thresholds)
+    Polar_Night_Snow = CloudTests(viirs_data, 'Polar_Night_Snow', thresholds)
+    Polar_Ocean_Day = CloudTests(viirs_data, 'Polar_Ocean_Day', thresholds)
+    Polar_Ocean_Night = CloudTests(viirs_data, 'Polar_Ocean_Night', thresholds)
+    Day_Snow = CloudTests(viirs_data, 'Day_Snow', thresholds)
+    Night_Snow = CloudTests(viirs_data, 'Night_Snow', thresholds)
+    Antarctic_Day = CloudTests(viirs_data, 'Antarctic_Day', thresholds)
+
+    # 11um BT Test
+    cmin_G1 = Ocean_Day.single_threshold_test('11um_Test', 'M15', cmin_G1)
+    cmin_G1 = Polar_Ocean_Day.single_threshold_test('11um_Test', 'M15', cmin_G1)
+    cmin_G1 = Polar_Ocean_Night.single_threshold_test('11um_Test', 'M15', cmin_G1)
+
+    # CO2 High Clouds Test
+    # NOTE: VIIRS doesn't have the MODIS equivalent of B35 so this test is not performed
+    cmin_G1 = Land_Day.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Land_Night.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Land_Day_Coast.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Land_Day_Desert.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Land_Day_Desert_Coast.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Ocean_Day.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Ocean_Night.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Day_Snow.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Night_Snow.single_threshold_test('CO2_High_Clouds_Test', 'bad_data', cmin_G1)
+
+    # Water Vapor High Clouds Test
+    # NOTE: VIIRS doesn't have the MODIS equivalent of B27 so this test is not performed
+    cmin_G1 = Land_Day.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Land_Night.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Land_Day_Coast.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Land_Day_Desert.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Land_Day_Desert_Coast.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Ocean_Day.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Ocean_Night.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Day_Land.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Night_Land.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Day_Coast.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Day_Desert.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Day_Desert_Coast.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Day_Snow.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Night_Snow.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Ocean_Day.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Polar_Ocean_Night.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Day_Snow.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Night_Snow.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+    cmin_G1 = Antarctic_Day.single_threshold_test('Water_Vapor_High_Clouds_Test', 'bad_data', cmin_G1)
+
+    # Surface Temperature Test
+    # ## NOTE: This requires some calculations for the thresholds.
+    # Moreover this test is using the 11um - 12um difference instead of a single channel
+    # Also, look at the test carefully for these two cases. Polar_Night_Land uses some hardcoded coeffs
+    # (i.e. *_df1 and *_df2) that might be worth moving in the thresholds file for consistency
+    cmin_G1 = Land_Night.single_threshold_test('Surface_Temperature_Test', 'M15-M16', cmin_G1)
+    cmin_G1 = Polar_Night_Land.single_threshold_test('Surface_Temperature_Test', 'M15-M16', cmin_G1)
+
+    # SST Test
+    # ## NOTE: This requires some calculations for the thresholds.
+    # Moreover this test is using the 11um - 12um difference instead of a single channel
+    cmin_G1 = Ocean_Day.single_threshold_test('SST_Test', 'M15-M16', cmin_G1)
+    cmin_G1 = Polar_Ocean_Day.single_threshold_test('SST_Test', 'M15-M16', cmin_G1)
+    cmin_G1 = Polar_Ocean_Night.single_threshold_test('SST_Test', 'M15-M16', cmin_G1)
+    # the following test uses a different set of coefficients compared to the others above
+    cmin_G1 = Ocean_Night.single_threshold_test('SST_Test', 'M15-M16', cmin_G1)
+
+    # 11-8.6um BT Difference Test
+    cmin_G2 = Ocean_Day.single_threshold_test('11-8.6um_Test', 'M15-M14', cmin_G2)
+    cmin_G2 = Ocean_Night.single_threshold_test('11-8.6um_Test', 'M15-M14', cmin_G2)
+    cmin_G2 = Polar_Ocean_Day.single_threshold_test('11-8.6um_Test', 'M15-M14', cmin_G2)
+    cmin_G2 = Polar_Ocean_Night.single_threshold_test('11-8.6um_Test', 'M15-M14', cmin_G2)
+
+    # 11-12um BT Difference
+    cmin_G1 = Ocean_Day.single_threshold_test('11-12BT_diff',
+                                              viirs_data.M15.values-viirs_data.M16.values,
+                                              cmin_G1)
+
+    Ocean_Night = CloudTests(viirs_data, 'Ocean_Night', thresholds)
+
+    cmin_G1 = Ocean_Night.single_threshold_test('11um_test', viirs_data.M15.values, cmin_G1)
+    cmin_G1 = Ocean_Night.single_threshold_test('11-12um_diff',
+                                                viirs_data.M15.values-viirs_data.M16.values,
+                                                cmin_G1)
+
+    c = np.ones((9, viirs_data['M01'].shape[0], viirs_data['M01'].shape[1]))
+    cmin1, c[0, :, :], bit1 = odt.simple_test(viirs_data.M15.values, thresholds['Daytime_Ocean']['bt11'], cmin1)
+    cmin1, c[1, :, :], bit2 = odt.sst_test(viirs_data.M15.values, viirs_data.M16.values,
+                                           viirs_data.sensor_zenith.values, viirs_data.geos_sfct.values,
+                                           thresholds['Daytime_Ocean']['sst'], cmin1)
+    cmin2, c[2, :, :], bit3 = odt.simple_test(viirs_data.M14.values-viirs_data.M15.values,
+                                              thresholds['Daytime_Ocean']['diff_11_86um'], cmin2)
+    cmin2, c[3, :, :], bit4 = odt.test_11_12_diff(viirs_data, thresholds['Daytime_Ocean']['diff11_12um'], cmin2)
+    cmin2, c[4, :, :] = odt.test_11_4_diff(viirs_data.M15.values, viirs_data.M12.values,
+                                           thresholds['Daytime_Ocean']['test11_4lo'], scene_flags, cmin2)
+    cmin3, c[5, :, :] = odt.nir_refl_test(viirs_data.M07.values, thresholds['Daytime_Ocean'],
+                                          thresholds['Sun_Glint'], viirs_data, cmin3)
+    cmin3, c[6, :, :] = odt.vis_nir_ratio_test(viirs_data.M05.values, viirs_data.M07.values,
+                                               thresholds, scene_flags, cmin3)
+    cmin3, c[7, :, :] = odt.nir_refl_test(viirs_data.M10.values, thresholds['Daytime_Ocean'],
+                                          thresholds['Sun_Glint'], viirs_data, cmin3)
+#
+#    confidence[0, :, :] = tests.test_11um(viirs_data.M15.values, thresholds['Daytime_Ocean'])
+#    confidence[1, :, :] = tests.test_11_4diff(viirs_data.M15.values, viirs_data.M13.values,
+#                                              thresholds['Daytime_Ocean'], viirs_data,
+#                                              thresholds['Sun_Glint']['bounds'][3])
+#
+#    confidence[2, :, :] = tests.nir_refl_test(viirs_data.M07.values, thresholds['Daytime_Ocean'],
+#                                              thresholds['Sun_Glint'], viirs_data)
+#
+#    # Note that here I'm using M05/M07 but the corresponding hi-res channels are I1/I2
+#    # IMPORTANT: conf_test_dble() needs to be verified. I don't think it's working as intended at the moment
+#    confidence[3, :, :] = tests.vis_nir_ratio_test(viirs_data.M05.values, viirs_data.M07.values,
+#                                                   thresholds['Daytime_Ocean'], thresholds['Sun_Glint'])
+#
+#    # This test needs to be verified, for the granule I'm running everything is zero
+#    confidence[4, :, :] = tests.test_11um_var(viirs_data.M15.values, thresholds['Nighttime_Ocean'],
+#                                              thresholds['Daytime_Ocean_Spatial_Variability'])
+
+    total_bit = bit1 + bit2 + bit4
+    temp_confidence = cmin1 * cmin2 * cmin3 * cmin4
+    confidence = cmin1 * cmin2 * cmin3 * cmin4
+    # idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['ice'] == 0) & (scene_flags['uniform'] == 1) &
+    #                  (confidence <= 0.99) & (confidence >= 0.05))
+    # confidence[idx] = restoral.spatial(viirs_data, thresholds['Sun_Glint'], scene_flags, confidence)[idx]
+
+    idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['sunglint'] == 1) &
+                     (scene_flags['uniform'] == 1) & (confidence <= 0.95))
+    confidence[idx] = restoral.sunglint(viirs_data, thresholds['Sun_Glint'], total_bit, temp_confidence)[idx]
+
+    temp = np.zeros((viirs_data.M01.shape[0], viirs_data.M01.shape[1]))
+    temp[idx] = 1
+    c[8, :, :] = temp
+
+    np.savez('test_confidence', confidence=confidence, conf_test=c,
+             lat=viirs_data.latitude.values, lon=viirs_data.longitude.values)
+
+    return confidence
+    '''
+
+
+def test_main():
+
+    rad1 = [[255, 260, 265, 248, 223],
+            [278, 285, 270, 268, 256],
+            [275, 273, 266, 254, 259]]
+    rad2 = [[270, 273, 271, 268, 265],
+            [277, 286, 275, 277, 269],
+            [280, 281, 272, 270, 267]]
+    thresh_file = '/home/pveglio/mvcm_leo/thresholds/new_thresholds.mvcm.snpp.v1.0.0.yaml'
+
+    with open(thresh_file) as f:
+        text = f.read()
+
+    rad1 = np.array(rad1)
+    rad2 = np.array(rad2)
+
+    confidence = np.zeros((2, rad1.shape[0], rad1.shape[1]))
+
+    thresholds = yml.safe_load(text)
+
+    confidence[0, :, :] = tests.test_11um(rad1, thresholds['Daytime_Ocean'])
+    confidence[1, :, :] = tests.test_11_4diff(rad1, rad2, thresholds['Daytime_Ocean'])
+
+    print(f'Confidence[0,:,:]: \n {confidence[0, :, :]}')
+    print(f'Confidence[1,:,:]: \n {confidence[1, :, :]}')
+
+    return confidence
+
+
+if __name__ == "__main__":
+    main()
diff --git a/mvcm/preprocess_thresholds.py b/mvcm/preprocess_thresholds.py
new file mode 100644
index 0000000..56d12a1
--- /dev/null
+++ b/mvcm/preprocess_thresholds.py
@@ -0,0 +1,567 @@
+import numpy as np
+import xarray as xr
+
+import ancillary_data as anc
+import utils
+
+from numpy.lib.stride_tricks import sliding_window_view
+from typing import Dict
+
+_dtr = np.pi/180
+_DTR = np.pi/180
+
+
+def prepare_11_12um_thresholds(thresholds: np.ndarray,
+                               dim1: int) -> Dict:
+
+    coeff_values = np.empty((dim1, 2))
+    coeff_values[:, 0] = np.full(dim1, thresholds['coeffs'][0])
+    coeff_values[:, 1] = np.full(dim1, thresholds['coeffs'][1])
+    cmult_values = np.full(dim1, thresholds['cmult'])
+    adj_values = np.full(dim1, thresholds['adj'])
+    if 'bt1' in list(thresholds):
+        bt1 = np.full(dim1, thresholds['bt1'])
+    else:
+        bt1 = np.full(dim1, -999)
+    if 'lat' in list(thresholds):
+        lat = np.full(dim1, thresholds['lat'])
+    else:
+        lat = np.full(dim1, -999)
+    thr_dict = {'coeffs': coeff_values,
+                'cmult': cmult_values,
+                'adj': adj_values,
+                'bt1': bt1,
+                'lat': lat,
+                }
+
+    return thr_dict
+
+
+# function was called preproc
+def thresholds_11_12um(data: xr.Dataset,
+                       thresholds: np.ndarray,
+                       scene: str,
+                       scene_idx: np.ndarray) -> np.ndarray:
+    cosvza = np.cos(data.sensor_zenith.values[scene_idx].ravel() * _DTR)
+    schi = np.full(cosvza.shape, 99.0)
+    schi[cosvza > 0] = 1/cosvza
+    schi = np.array(schi, dtype=np.float32)  # this is because the C function expects a float
+
+    m15 = data.M15.values[scene_idx].ravel()
+    latitude = data.latitude.values[scene_idx].ravel()
+    thr = anc.py_cithr(1, schi, m15)
+    thr_dict = prepare_11_12um_thresholds(thresholds, m15.shape[0])
+
+    midpt = np.full(m15.shape[0], thr)
+    idx = np.nonzero((thr < 0.1) | (np.abs(schi-99) < 0.0001))
+    midpt[idx] = thr_dict['coeffs'][idx, 0]
+    locut = midpt + (thr_dict['cmult'] * midpt)
+    if scene in ['Land_Day', 'Land_Day_Coast', 'Land_Day_Desert', 'Land_Day_Desert_Coast',
+                 'Ocean_Day', 'Ocean_Night', 'Polar_Day_Ocean', 'Polar_Night_Ocean']:
+        hicut = midpt - thr_dict['adj']
+    elif scene in ['Polar_Day_Land', 'Polar_Day_Coast', 'Polar_Day_Desert',
+                   'Polar_Day_Desert_Coast', 'Polar_Day_Snow']:
+        hicut = midpt - (thr_dict['adj'] * midpt)
+    elif scene in ['Land_Night', 'Polar_Night_Land', 'Polar_Night_Snow', 'Day_Snow', 'Night_Snow']:
+        _coeffs = {'Land_Night': 0.3, 'Polar_Night_Land': 0.3, 'Polar_Night_Snow': 0.3,
+                   'Day_Snow': 0.0, 'Night_Snow': 0.3}
+        midpt = midpt - (_coeffs[scene] * locut)
+        if scene in ['Polar_Night_Land', 'Polar_Night_Snow', 'Night_Snow']:
+            hicut = np.full(m15.shape, midpt - 1.25)
+            idx = np.nonzero(m15 < thr_dict['bt1'])
+            hicut[idx] = midpt[idx] - (0.2 * locut[idx])
+        elif scene in ['Land_Night']:
+            hicut = np.full(m15.shape, 1.25)
+            idx = np.nonzero((m15 < thr_dict['bt1']) & (latitude > thr_dict['lat']))
+            hicut[idx] = -0.1 - np.power(90.0 - np.abs(latitude[idx])/60, 4) * 1.15
+        elif scene in ['Day_Snow']:
+            hicut = locut - (thr_dict['cmult'] * locut)
+        else:
+            print('Scene not recognized\n')
+    else:
+        print('Scene not recognized\n')
+
+    thr_out = np.dstack((locut, midpt, hicut, np.ones(locut.shape), np.ones(locut.shape)))
+    return np.squeeze(thr_out.T)
+
+
+def thresholds_NIR(data, thresholds, scene, test_name, scene_idx):
+
+    sza = data.solar_zenith.values[scene_idx].ravel()
+    band_n = 2
+    # NOTE: the visud condition in the C code is equivalent to having sza <= 85
+    # For the time being the visud filtering is not implemented
+
+    c = np.array(thresholds[scene][test_name]['coeffs'])
+    vzcpow = thresholds['VZA_correction']['vzcpow'][0]
+    refang = data.sunglint_angle.values[scene_idx].ravel()
+    sunglint_thresholds = thresholds['Sun_Glint']
+    sunglint_flag = utils.sunglint_scene(refang, sunglint_thresholds)
+    nir_thresh = thresholds[scene][test_name]
+
+    if test_name == 'NIR_Reflectance_Test':
+        hicut = c[0] + c[1]*sza + c[2]*np.power(sza, 2) + c[3]*np.power(sza, 3)
+    elif test_name == '1.6_2.1um_NIR_Reflectance_Test':
+        hicut = c[0] + c[1]*sza + c[2]*np.power(sza, 2) + c[3]*np.power(sza, 3) + c[4]*np.power(sza, 4)
+    else:
+        pass
+    hicut = (hicut * 0.01) + nir_thresh['adj']
+    hicut = (hicut * nir_thresh['bias'])
+    midpt = hicut + (nir_thresh['midpt_coeff'] * nir_thresh['bias'])
+    locut = midpt + (nir_thresh['locut_coeff'] * nir_thresh['bias'])
+    thr = np.array([locut, midpt, hicut, nir_thresh['thr'][3]*np.ones(refang.shape)])
+
+    cosvza = np.cos(data.sensor_zenith.values[scene_idx]*_DTR).ravel()
+
+    corr_thr = np.zeros((4, refang.shape[0]))
+    corr_thr[:3, sunglint_flag == 0] = thr[:3, sunglint_flag == 0] * (1./np.power(cosvza[sunglint_flag == 0],
+                                                                                  vzcpow))
+    corr_thr[3, sunglint_flag == 0] = thr[3, sunglint_flag == 0]
+    for flag in range(1, 4):
+        if len(refang[sunglint_flag == flag]) > 0:
+            dosgref = utils.get_sunglint_thresholds(refang, sunglint_thresholds, band_n, flag, thr)
+            corr_thr[:3, sunglint_flag == flag] = dosgref[:3, sunglint_flag == flag] * \
+                (1./np.power(cosvza[sunglint_flag == flag], vzcpow))
+            corr_thr[3, sunglint_flag == flag] = dosgref[3, sunglint_flag == flag]
+
+    return corr_thr
+
+
+def thresholds_surface_temperature(data, thresholds, scene_idx):
+    # def preproc_surf_temp(data, thresholds):
+    thr_sfc1 = thresholds['desert_thr']
+    thr_sfc2 = thresholds['regular_thr']
+    thr_df1 = thresholds['channel_diff_11-12um_thr']
+    thr_df2 = thresholds['channel_diff_11-4um_thr']
+    max_vza = 70.13  # This values is set based on sensor. Check mask_processing_constants.h for MODIS value
+
+    df1 = (data.M15 - data.M16).values[scene_idx].ravel()
+    df2 = (data.M15 - data.M13).values[scene_idx].ravel()
+    desert_flag = data.Desert.values[scene_idx].ravel()
+    thresh = np.ones(df1.shape) * thr_sfc1
+
+    idx = np.where((df1 >= thr_df1[0]) | ((df1 < thr_df1[0]) & ((df2 <= thr_df2[0]) | (df2 >= thr_df2[1]))))
+    thresh[idx] = thr_sfc2
+    idx = np.where(desert_flag == 1)
+    thresh[idx] == thr_sfc1
+
+    midpt = thresh
+    idx = np.where(df1 >= thr_df1[1])
+    midpt[idx] = thresh[idx] + 2.0*df1[idx]
+
+    corr = np.power(data.sensor_zenith.values[scene_idx].ravel()/max_vza, 4) * 3.0
+    midpt = midpt + corr
+    locut = midpt + 2.0
+    hicut = midpt - 2.0
+
+    thr_out = np.dstack((locut, midpt, hicut, np.ones(locut.shape), np.ones(locut.shape)))
+
+    return np.squeeze(thr_out.T)
+
+
+# This function is currently not used
+def preproc_sst(data, thresholds):
+    m31c = data.M15 - 273.16
+    m32c = data.M16 - 273.16
+    m31c_m32c = m31c - m32c
+    sstc = data.geos_sfct - 273.16
+    cosvza = np.cos(data.sensor_zenith*_DTR)
+
+    a = thresholds['coeffs']
+
+    modsst = 273.16 + a[0] + a[1]*m31c + a[2]*m31c_m32c*sstc + a[3]*m31c_m32c*((1/cosvza) - 1)
+    sfcdif = data.geos_sfct - modsst
+
+    return sfcdif
+
+
+def var_11um(data, thresholds):
+    rad = data.M15.values
+    var = np.zeros((rad.shape[0], rad.shape[1], 9))
+    var_thr = thresholds['Daytime_Ocean_Spatial_Variability']['dovar11']
+
+    test = sliding_window_view(np.pad(rad, [1, 1], mode='constant'), (3, 3)) - np.expand_dims(rad, (2, 3))
+
+    var[np.abs(test).reshape(rad.shape[0], rad.shape[1], 9) < var_thr] = 1
+    var = var.sum(axis=2)
+
+    return var
+
+
+def get_b1_thresholds(data, thresholds, scene_idx):
+    ndvi = data.ndvi.values[scene_idx].ravel()
+    sctang = data.scattering_angle.values[scene_idx].ravel()
+
+    # this is hardcoded in the function
+    delta_ndvi_bin = 0.1
+
+    des_ndvi = thresholds['Misc']['des_ndvi']
+    thr_adj_fac_desert = thresholds['Misc']['adj_fac_desert']
+    thr_adj_fac_land = thresholds['Misc']['adj_fac_land']
+    ndvi_bnd1 = thresholds['Misc']['ndvi_bnd1']
+    ndvi_bnd2 = thresholds['Misc']['ndvi_bnd2']
+    fill_ndvi = thresholds['Misc']['fill_ndvi']
+
+    coeff1 = np.array(thresholds['Coeffs_Band1_land_thresh']).reshape(10, 3, 4)
+    coeff2 = np.zeros((10, 3, 4))
+    coeff2[:3, :, :] = np.array(thresholds['Coeffs_Band8_land_thresh']).reshape(3, 3, 4)
+    coeff = np.stack((coeff1, coeff2))
+
+    indvi = np.zeros(ndvi.shape)
+    indvi[ndvi >= ndvi_bnd2] = 9
+
+    x, y2 = np.zeros(ndvi.shape), np.zeros(ndvi.shape)
+
+    # this is equivalent to interp=1 in the C code
+    idx = np.nonzero((ndvi >= ndvi_bnd1) & (ndvi < ndvi_bnd2))
+
+    indvi[idx] = (ndvi[idx]/delta_ndvi_bin) - 0.5
+    indvi[ndvi < 0] = 0
+
+    x1 = delta_ndvi_bin*indvi + delta_ndvi_bin/2.0
+    x2 = x1 + delta_ndvi_bin
+    x[idx] = (ndvi[idx] - x1[idx])/(x2[idx] - x1[idx])
+    x = np.clip(x, 0, 1)
+
+    indvi = np.array(indvi, dtype=np.int)
+    thr = np.empty((ndvi.shape[0], 4))
+    thr_adj = np.empty((ndvi.shape[0], 4))
+
+    for i in range(3):
+        y1 = coeff[0, indvi, i, 0] + coeff[0, indvi, i, 1]*sctang + \
+             coeff[0, indvi, i, 2]*sctang**2 + coeff[0, indvi, i, 3]*sctang**3
+        des = np.nonzero(ndvi < des_ndvi)
+        y1[des] = coeff[1, indvi[des], i, 0] + coeff[1, indvi[des], i, 1]*sctang[des] + \
+            coeff[1, indvi[des], i, 2]*sctang[des]**2 + coeff[1, indvi[des], i, 3]*sctang[des]**3
+
+        y2[idx] = coeff[0, indvi[idx], i, 0] + \
+            coeff[0, indvi[idx], i, 1]*sctang[idx] + \
+            coeff[0, indvi[idx], i, 2]*sctang[idx]**2 + \
+            coeff[0, indvi[idx], i, 3]*sctang[idx]**3
+        idxdes = np.nonzero((ndvi >= ndvi_bnd1) & (ndvi < ndvi_bnd2) & (ndvi < des_ndvi))
+        y2[idxdes] = coeff[0, indvi[idxdes], i, 0] + \
+            coeff[0, indvi[idxdes], i, 1]*sctang[idxdes] + \
+            coeff[0, indvi[idxdes], i, 2]*sctang[idxdes]**2 + \
+            coeff[0, indvi[idxdes], i, 3]*sctang[idxdes]**3
+
+        thr[:, i] = (1.0 - x) + (x + y2)
+        thr_adj[:, i] = thr[:, i] * thr_adj_fac_desert
+        thr_adj[ndvi >= des_ndvi, i] = thr[ndvi >= des_ndvi, i] * thr_adj_fac_land
+
+    hicut = ((thr[:, 0] + thr_adj[:, 0])/100)  # .reshape(data.ndvi.shape)
+    midpt = ((thr[:, 1] + thr_adj[:, 1])/100)  # .reshape(data.ndvi.shape)
+    locut = ((thr[:, 2] + thr_adj[:, 2])/100)  # .reshape(data.ndvi.shape)
+
+    idx = np.nonzero((ndvi >= fill_ndvi[0]) | (ndvi <= fill_ndvi[1]))
+    hicut[idx] = -999
+    midpt[idx] = -999
+    locut[idx] = -999
+
+#    out_thr = xr.DataArray(data=np.dstack((locut, midpt, hicut, np.ones(data.ndvi.shape),
+#                                           np.full(data.ndvi.shape, 2))),
+#                           dims=('number_of_lines', 'number_of_pixels', 'z'))
+#
+#    return out_thr
+    return locut, midpt, hicut
+
+
+def polar_night_thresholds(data, thresholds, scene, test_name, scene_idx):
+
+    thresholds = thresholds[scene]
+    if ((test_name == '4-12um_BTD_Thin_Cirrus_Test') and (scene in ['Land_Night', 'Night_Snow']) or
+       (test_name == '7.3-11um_BTD_Mid_Level_Cloud_Test') and (scene == 'Land_Night')):
+        locut = thresholds[test_name]['thr'][0] * np.ones(data.M15.values[scene_idx].ravel())
+        midpt = thresholds[test_name]['thr'][1] * np.ones(data.M15.values[scene_idx].ravel())
+        hicut = thresholds[test_name]['thr'][2] * np.ones(data.M15.values[scene_idx].ravel())
+        power = thresholds[test_name]['thr'][3] * np.ones(data.M15.values[scene_idx].ravel())
+
+        # out_thr = xr.DataArray(data=np.dstack((locut, midpt, hicut, np.ones(data.ndvi.shape), power)),
+        #                        dims=('number_of_lines', 'number_of_pixels', 'z'))
+        out_thr = np.dstack((locut, midpt, hicut, np.ones(locut.shape), power))
+
+        return out_thr.T
+
+    rad = data.M15.values[scene_idx].ravel()
+    bt_bounds = thresholds[test_name]['bt11_bounds']
+
+    locut, midpt = np.empty(rad.shape), np.empty(rad.shape)
+    hicut, power = np.empty(rad.shape), np.empty(rad.shape)
+    lo, hi = np.empty(rad.shape), np.empty(rad.shape)
+    lo_thr, hi_thr = np.empty(rad.shape), np.empty(rad.shape)
+    conf_range = np.empty(rad.shape)
+
+    idx = np.nonzero(rad < bt_bounds[0])
+    locut[idx] = thresholds[test_name]['low'][0]
+    midpt[idx] = thresholds[test_name]['low'][1]
+    hicut[idx] = thresholds[test_name]['low'][2]
+    power[idx] = thresholds[test_name]['low'][3]
+
+    idx = np.nonzero(rad > bt_bounds[3])
+    locut[idx] = thresholds[test_name]['high'][0]
+    midpt[idx] = thresholds[test_name]['high'][1]
+    hicut[idx] = thresholds[test_name]['high'][2]
+    power[idx] = thresholds[test_name]['high'][3]
+
+    # # # # #
+    idx = np.nonzero((rad >= bt_bounds[0]) & (rad <= bt_bounds[3]) &
+                     (bt_bounds[1] == 0) & (bt_bounds[2] == 0))
+    lo[idx] = thresholds[test_name]['bt11_bounds'][0]
+    hi[idx] = thresholds[test_name]['bt11_bounds'][3]
+    lo_thr[idx] = thresholds[test_name]['mid1'][0]
+    hi_thr[idx] = thresholds[test_name]['mid1'][1]
+    power[idx] = thresholds[test_name]['mid1'][3]
+    conf_range[idx] = thresholds[test_name]['mid1'][2]
+
+    idx = np.nonzero((rad >= bt_bounds[0]) & (rad < bt_bounds[1]))
+    lo[idx] = thresholds[test_name]['bt11_bounds'][0]
+    hi[idx] = thresholds[test_name]['bt11_bounds'][1]
+    lo_thr[idx] = thresholds[test_name]['mid1'][0]
+    hi_thr[idx] = thresholds[test_name]['mid1'][1]
+    power[idx] = thresholds[test_name]['mid1'][3]
+    conf_range[idx] = thresholds[test_name]['mid1'][2]
+
+    idx = np.nonzero((rad >= bt_bounds[1]) & (rad < bt_bounds[2]))
+    lo[idx] = thresholds[test_name]['bt11_bounds'][1]
+    hi[idx] = thresholds[test_name]['bt11_bounds'][2]
+    lo_thr[idx] = thresholds[test_name]['mid2'][0]
+    hi_thr[idx] = thresholds[test_name]['mid2'][1]
+    power[idx] = thresholds[test_name]['mid2'][3]
+    conf_range[idx] = thresholds[test_name]['mid2'][2]
+
+    idx = np.nonzero((rad >= bt_bounds[2]) & (rad < bt_bounds[3]))
+    lo[idx] = thresholds[test_name]['bt11_bounds'][2]
+    hi[idx] = thresholds[test_name]['bt11_bounds'][3]
+    lo_thr[idx] = thresholds[test_name]['mid3'][0]
+    hi_thr[idx] = thresholds[test_name]['mid3'][1]
+    power[idx] = thresholds[test_name]['mid3'][3]
+    conf_range[idx] = thresholds[test_name]['mid3'][2]
+
+    idx = np.nonzero(((rad >= bt_bounds[0]) & (rad < bt_bounds[3])) |
+                     (bt_bounds[1] == 0.0) | (bt_bounds[2] == 0))
+
+    a = (rad[idx] - lo[idx])/(hi[idx] - lo[idx])
+    midpt[idx] = lo_thr[idx] + (a*(hi_thr[idx] - lo_thr[idx]))
+    hicut[idx] = midpt[idx] - conf_range[idx]
+    locut[idx] = midpt[idx] + conf_range[idx]
+
+    # locut = locut.reshape(data.M15.shape)
+    # midpt = midpt.reshape(data.M15.shape)
+    # hicut = hicut.reshape(data.M15.shape)
+    # power = power.reshape(data.M15.shape)
+
+    # out_thr = xr.DataArray(data=np.dstack((locut, midpt, hicut, np.ones(data.ndvi.shape), power)),
+    #                        dims=('number_of_lines', 'number_of_pixels', 'z'))
+    out_thr = np.dstack((locut, midpt, hicut, np.ones(locut.shape), power))
+
+    return out_thr.T
+
+
+# get_nl_thresholds
+def land_night_thresholds(data, threshold, coast=True):
+    if coast is False:
+        lo_val = threshold['bt_diff_bounds'][0]
+        hi_val = threshold['bt_diff_bounds'][1]
+        lo_val_thr = threshold['thr_mid'][0]
+        hi_val_thr = threshold['thr_mid'][1]
+        conf_range = threshold['thr_mid'][2]
+
+        a = (data['M15-M16'].values - lo_val) / (hi_val - lo_val)
+        midpt = lo_val_thr + a*(hi_val_thr - lo_val_thr)
+        hicut = midpt - conf_range
+        locut = midpt + conf_range
+
+        power = np.full(midpt.shape, threshold['thr_mid'][3])
+
+        idx = np.nonzero(data['M15-M16'].values > threshold['bt_diff_bounds'][0])
+        locut[idx] = threshold['thr_low'][0]
+        midpt[idx] = threshold['thr_low'][1]
+        hicut[idx] = threshold['thr_low'][2]
+        power[idx] = threshold['thr_low'][3]
+
+        idx = np.nonzero(data['M15-M16'].values < threshold['bt_diff_bounds'][1])
+        locut[idx] = threshold['thr_hi'][0]
+        midpt[idx] = threshold['thr_hi'][1]
+        hicut[idx] = threshold['thr_hi'][2]
+        power[idx] = threshold['thr_hi'][3]
+
+        out_thr = np.dstack((locut.ravel(), midpt.ravel(), hicut.ravel(),
+                             np.ones(locut.ravel().shape), power.ravel()))
+        return np.squeeze(out_thr.T)
+    else:
+        b0 = threshold['coeffs'][0]
+        b1 = threshold['coeffs'][1]
+        b2 = threshold['coeffs'][2]
+
+        thr = b0 + threshold['int_adj'] + b1*data.geos_tpw.values + b2*data.geos_tpw.values**2
+        hicut = np.empty((2, thr.shape[0], thr.shape[1]))
+        midpt = np.empty((2, thr.shape[0], thr.shape[1]))
+        locut = np.empty((2, thr.shape[0], thr.shape[1]))
+        hicut[0, :] = thr - 1
+        hicut[1, :] = thr + 1
+        midpt[0, :] = hicut[0, :] - 0.5
+        midpt[1, :] = hicut[1, :] + 0.5
+        locut[0, :] = hicut[0, :] - 1
+        locut[1, :] = hicut[1, :] + 1
+
+        out_thr = np.dstack((hicut[0, :], midpt[0, :], locut[0, :], locut[1, :], midpt[1, :], hicut[1, :],
+                             np.ones(locut[0, :].shape), np.ones(locut[0, :].shape)))
+        return np.moveaxis(out_thr, -1, 0)
+
+
+def vis_refl_thresholds(data, thresholds, scene, scene_idx):
+
+    locut, midpt, hicut = get_b1_thresholds(data, thresholds, scene_idx)
+    bias_adj = thresholds[scene]['Visible_Reflectance_Test']['adj']
+    ndvi = data.ndvi.values[scene_idx].ravel()
+    m01 = data.M05.values[scene_idx].ravel()
+    m02 = data.M07.values[scene_idx].ravel()
+    m08 = data.M01.values[scene_idx].ravel()
+
+    m128 = m01
+
+    b1_locut = locut * bias_adj
+    b1_midpt = midpt * bias_adj
+    b1_hicut = hicut * bias_adj
+
+    if ((scene == 'Land_Day_Desert') | (scene == 'Land_Day_Desert_Coast')):
+        ndvi_desert_thr = thresholds[scene]['Visible_Reflectance_Test']['ndvi_thr']
+        idx = np.nonzero(ndvi < ndvi_desert_thr)
+
+        b1_locut[idx] = locut[idx]
+        b1_midpt[idx] = midpt[idx]
+        b1_hicut[idx] = hicut[idx]
+        m128[idx] = m08[idx]
+
+    b1_power = np.full(b1_locut.shape, 2)
+
+    idx = np.nonzero(locut == -999)
+    b1_locut[idx] = thresholds[scene]['Visible_Reflectance_Test']['thr'][0]
+    b1_midpt[idx] = thresholds[scene]['Visible_Reflectance_Test']['thr'][1]
+    b1_hicut[idx] = thresholds[scene]['Visible_Reflectance_Test']['thr'][2]
+    b1_power[idx] = thresholds[scene]['Visible_Reflectance_Test']['thr'][3]
+    m128[idx] = m02[idx]
+
+    cosvza = np.cos(data.sensor_zenith.values[scene_idx] * _DTR).ravel()
+
+    vzcpow = thresholds['VZA_correction']['vzcpow'][0]
+    b1_locut = (b1_locut * (1.0 / np.power(cosvza, vzcpow)))
+    b1_midpt = (b1_midpt * (1.0 / np.power(cosvza, vzcpow)))
+    b1_hicut = (b1_hicut * (1.0 / np.power(cosvza, vzcpow)))
+
+    # out_thr = xr.DataArray(data=np.dstack((b1_locut, b1_midpt, b1_hicut, np.ones(data.ndvi.shape),
+    #                                        b1_power.reshape(data.ndvi.shape))),
+    #                        dims=('number_of_lines', 'number_of_pixels', 'z'))
+    # out_rad = xr.DataArray(data=m128.reshape(data.M01.shape), dims=('number_of_lines', 'number_of_pixels'))
+    out_thr = np.dstack((b1_locut, b1_midpt, b1_hicut, b1_power))
+    out_rad = m128
+    return np.squeeze(out_thr.T), out_rad
+
+
+def gemi_thresholds(data, thresholds, scene_name, scene_idx):
+    ndvi = data.ndvi.values[scene_idx].ravel()
+    gemi_thr = np.ones((ndvi.shape[0], 5))
+
+    idx = np.nonzero(ndvi < 0.1)
+    gemi_thr[idx, :3] = thresholds['gemi0'][:3]
+    idx = np.nonzero((ndvi >= 0.1) & (ndvi < 0.2))
+    gemi_thr[idx, :3] = thresholds['gemi1'][:3]
+    idx = np.nonzero((ndvi >= 0.2) & (ndvi < 0.3))
+    gemi_thr[idx, :3] = thresholds['gemi2'][:3]
+
+    # thr_out = xr.DataArray(data=np.dstack((gemi_thr[:, :, 0], gemi_thr[:, :, 1], gemi_thr[:, :, 2],
+    #                                        np.ones(gemi_thr[:, :, 0].shape),
+    #                                        np.ones(gemi_thr[:, :, 0].shape))),
+    #                        dims=('number_of_lines', 'number_of_pixels', 'z'))
+
+    return gemi_thr.T
+
+
+def bt_diff_11_4um_thresholds(data, threshold, scene_idx):
+    c = threshold['coeffs']
+    tpw = data.geos_tpw.values[scene_idx].ravel()
+
+    thr = c[0] + threshold['corr'] + c[1]*tpw + c[2]*np.power(tpw, 2)
+
+    hicut0 = thr + threshold['hicut_coeff'][0]
+    hicut1 = thr + threshold['hicut_coeff'][1]
+    midpt0 = hicut0 + threshold['midpt_coeff'][0]
+    midpt1 = hicut1 + threshold['midpt_coeff'][1]
+    locut0 = hicut0 + threshold['locut_coeff'][0]
+    locut1 = hicut1 + threshold['locut_coeff'][1]
+
+    thr_out = np.dstack([locut0, midpt0, hicut0, hicut1, midpt1, locut1,
+                         np.ones(hicut0.shape), np.ones(hicut0.shape)])
+    return np.squeeze(thr_out.T)
+
+
+def thresholds_1_38um_test(data, thresholds, scene_name, scene_idx):
+    sza = data.solar_zenith.values[scene_idx]
+    vza = data.sensor_zenith.values[scene_idx]
+    thresh = thresholds[scene_name]['1.38um_High_Cloud_Test']
+    c = thresh['coeffs']
+    vzcpow = thresholds['VZA_correction']['vzcpow'][1]
+
+    hicut = c[0] + c[1]*sza + c[2]*np.power(sza, 2) + c[3]*np.power(sza, 3) + \
+        c[4]*np.power(sza, 4) + c[5]*np.power(sza, 5)
+    hicut = hicut*0.01 + (np.maximum((sza/90.)*thresh['szafac']*thresh['adj'], thresh['adj']))
+    midpt = hicut + 0.001
+    locut = midpt + 0.001
+
+    cosvza = np.cos(vza*_DTR)
+
+    locut = locut * (1/np.power(cosvza, vzcpow))
+    midpt = midpt * (1/np.power(cosvza, vzcpow))
+    hicut = hicut * (1/np.power(cosvza, vzcpow))
+
+    # out_thr = xr.DataArray(data=np.dstack((locut, midpt, hicut, np.ones(data.ndvi.shape),
+    #                                        np.ones(data.ndvi.shape))),
+    #                        dims=('number_of_lines', 'number_of_pixels', 'z'))
+    out_thr = np.dstack((locut, midpt, hicut, np.ones(locut.shape), np.ones(locut.shape)))
+
+    return np.squeeze(out_thr.T)
+
+# NOTE: 11-12um Cirrus Test
+# hicut is computed in different ways depending on the scene
+# 1. midpt - adj
+# - Land_Day
+# - Land_Day_Coast
+# - Land_Day_Desert
+# - Land_Day_Desert_Coast
+# - Ocean_Day
+# - Ocean_Night
+# - Polar_Day_Ocean
+# - Polar_Night_Ocean
+#
+# 2. midpt - (btd_thr * adj)
+# - Polar_Day_Land
+# - Polar_Day_Coast
+# - Polar_Day_Desert
+# - Polar_Day_Desert_Coast
+# - Polar_Day_Snow
+#
+# 3. Others
+# - Land_Night
+# - Polar_Night_Land
+# - Polar_Night_Snow
+# - Day_Snow
+# - Night_Snow
+
+# NOTE: 1.38um High Cloud Test
+# thresholds are not always computed the same way. In group 1 there's no preprocessing required,
+# in group 2 some calcuations are needed
+# 1.
+# - Land_Day
+# - Land_Day_Coast
+# - Land_Day_Desert
+# - Land_Day_Desert_Coast
+# - Polar_Day_Land
+# - Polar_Day_Coast
+# - Polar_Day_Desert
+# - Polar_Day_Desert_Coast
+# - Polar_Day_Snow
+# - Day_Snow
+#
+# 2.
+# - Ocean_Day
+# - Polar_Ocean_Day
+
diff --git a/mvcm/read_data.py b/mvcm/read_data.py
new file mode 100644
index 0000000..4e95231
--- /dev/null
+++ b/mvcm/read_data.py
@@ -0,0 +1,581 @@
+import xarray as xr
+import numpy as np
+import numpy.typing as npt
+
+import ancillary_data as anc
+
+import os
+import logging
+from datetime import datetime as dt
+from typing import Dict
+from attrs import define, field, validators, Factory
+
+_DTR = np.pi/180.
+_RTD = 180./np.pi
+_bad_data = -999.0
+_datapath = '/ships19/hercules/pveglio/mvcm_test_data'
+
+logging.basicConfig(level=logging.INFO, format='%(name)s - %(levelname)s - %(message)s')
+# logging.basicConfig(level=logging.INFO, filename='logfile.log', 'filemode='w',
+#                       format='%(name)s %(levelname)s %(message)s')
+
+
+@define(kw_only=True, slots=True)
+class CollectInputs(object):
+    """Class that collects all input files
+
+    Parameters
+    ----------
+    file_name_geo: str
+        name of VIIRS geolocation file
+    file_name_l1b: str
+        name of VIIRS L1b file
+    ancillary_dir: str
+        path for the ancillary files. (this is necessary because the C functions
+        still require path and file name separately)
+    sst_file: str
+        file name for the Reynolds SST
+    ndvi_file: str
+        file name for the NDVI hdf4 file
+    geos_file_before: str
+        file name of the GEOS-5 containing atmospheric profiles before the VIIRS timestamp
+    geos_file_after: str
+        file name of the GEOS-5 containing atmospheric profiles after the VIIRS timestamp
+    geos_land: str
+        file name of the GEOS-5 file that has the land ice/snow information
+    geos_ocean: str
+        file name of the GEOS-5 file that has the ocean information
+    geos_constants: str
+        file name for the GEOS-5 constans
+    dims [optional]: str
+        name of the dimensions for the arrays in the xarray.Dataset output
+    """
+    file_name_geo: str = field(default=f'{_datapath}/VNP03MOD.A2022173.1312.001.2022174012746.uwssec.nc',
+                               validator=[validators.instance_of(str), ])
+    file_name_l1b: str = field(default=f'{_datapath}/VNP02MOD.A2022173.1312.001.2022174011547.uwssec.nc',
+                               validator=[validators.instance_of(str), ])
+    ancillary_dir: str = field(default=f'{_datapath}/ancillary',
+                               validator=[validators.instance_of(str), ])
+    sst_file: str = field(default='oisst.20220622',
+                          validator=[validators.instance_of(str), ])
+    eco_file: str = field(default='goge1_2_img.v1',
+                          validator=[validators.instance_of(str), ])
+    ndvi_file: str = field(default='NDVI.FM.c004.v2.0.WS.00-04.177.hdf',
+                           validator=[validators.instance_of(str), ])
+    geos_file_1: str = field(default='GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1200.V01.nc4',
+                             validator=[validators.instance_of(str), ])
+    geos_file_2: str = field(default='GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1500.V01.nc4',
+                             validator=[validators.instance_of(str), ])
+    geos_land: str = field(default='GEOS.fpit.asm.tavg1_2d_lnd_Nx.GEOS5124.20220622_1330.V01.nc4',
+                           validator=[validators.instance_of(str), ])
+    geos_ocean: str = field(default='GEOS.fpit.asm.tavg1_2d_ocn_Nx.GEOS5124.20220622_1330.V01.nc4',
+                            validator=[validators.instance_of(str), ])
+    geos_constants: str = field(default='GEOS.fp.asm.const_2d_asm_Nx.00000000_0000.V01.nc4',
+                                validator=[validators.instance_of(str), ])
+
+    dims: tuple = field(default=('number_of_lines', 'number_of_pixels'),
+                        validator=[validators.instance_of(tuple), ])
+
+
+@define(slots=True, kw_only=True)
+class ReadData(CollectInputs):
+    """Class that reads the L1b/geolocation data from VIIRS. Inherits file names from CollectInputs
+
+    Parameters
+    ----------
+    satellite: str
+        satellite name.
+    sensor: str
+        sensor name
+    """
+    satellite: str = field(validator=[validators.instance_of(str),
+                                      validators.in_(['snpp'])])
+    sensor: str = field(validator=[validators.instance_of(str),
+                                   validators.in_(['viirs'])])
+
+    logging.debug('Instance of ReadData created')
+
+    def read_viirs_geo(self) -> xr.Dataset:
+        """Read VIIRS geolocation data and generate additional angles
+
+        Parameters
+        ----------
+        self.file_name_geo: str
+            file name of the geolocation file
+
+        Returns
+        -------
+        geo_data xarray.Dataset
+            dataset containing all geolocation data
+        """
+        logging.debug(f'Reading {self.file_name_geo}')
+        geo_data = xr.open_dataset(self.file_name_geo, group='geolocation_data')
+
+        relazi = self.relative_azimuth_angle(geo_data.sensor_azimuth.values, geo_data.solar_azimuth.values)
+        sunglint = self.sun_glint_angle(geo_data.sensor_zenith.values, geo_data.solar_zenith.values, relazi)
+        scatt_angle = self.scattering_angle(geo_data.solar_zenith.values, geo_data.sensor_zenith.values,
+                                            relazi)
+
+        geo_data['relative_azimuth'] = (self.dims, relazi)
+        geo_data['sunglint_angle'] = (self.dims, sunglint)
+        geo_data['scattering_angle'] = (self.dims, scatt_angle)
+
+        logging.debug('Geolocation file read correctly')
+
+        return geo_data
+
+    def read_viirs_l1b(self,
+                       solar_zenith: npt.NDArray[float]) -> xr.Dataset:
+        """Read VIIRS L1b data
+
+        Parameters
+        ----------
+        self.file_name_l1b: str
+            file name of the L1b file
+        solar_zenith: np.ndarray
+            solar zenith angle derived from the geolocation file
+        """
+        logging.debug(f'Reading {self.file_name_l1b}')
+        l1b_data = xr.open_dataset(self.file_name_l1b, group='observation_data', decode_cf=False)
+
+        rad_data = xr.Dataset()
+        for band in list(l1b_data.variables):
+            if 'reflectance' in l1b_data[band].long_name:
+                if hasattr(l1b_data[band], 'VCST_scale_factor'):
+                    scale_factor = l1b_data[band].VCST_scale_factor * l1b_data[band].bias_correction
+                else:
+                    scale_factor = l1b_data[band].scale_factor
+                rad_data[band] = (self.dims, l1b_data[band].values * scale_factor / np.cos(solar_zenith*_DTR))
+
+            elif 'radiance' in l1b_data[band].long_name:
+                bt_lut = f'{band}_brightness_temperature_lut'
+                rad_data[band] = (self.dims,
+                                  l1b_data[bt_lut].values[l1b_data[band].values])
+            else:
+                pass
+
+        logging.debug('L1b file read correctly')
+
+        return rad_data
+
+    def preprocess_viirs(self,
+                         geo_data: xr.Dataset,
+                         viirs: xr.Dataset) -> xr.Dataset:
+        """Create band combinations (e.g. ratios, differences) that are used by some spectral tests.
+
+        Parameters
+        ----------
+        geo_data: xarray.Dataset
+            dataset containing all geolocation data
+        viirs: xr.Dataset
+            VIIRS L1b data
+
+        Returns
+        -------
+        viirs: xr.Dataset
+            VIIRS L1b data plus band combinations for the spectral tests
+        """
+        if (('M05' in viirs) and ('M07' in viirs)):
+            m01 = viirs.M05.values
+            m02 = viirs.M07.values
+            r1 = 2.0 * (np.power(m02, 2.0) - np.power(m01, 2.0)) + (1.5 * m02) + (0.5 * m01)
+            r2 = m01 + m02 + 0.5
+            r3 = r1 / r2
+            gemi = r3 * (1.0 - 0.25*r3) - ((m01 - 0.125) / (1.0 - m01))
+        else:
+            gemi = np.full((viirs.M15.shape), _bad_data)
+
+        """
+        if 'M05' in viirs:
+            idx = np.nonzero((viirs.M05.values < -99) | (viirs.M05.values > 2))
+            viirs['M05'].values[idx] = _bad_data
+        else:
+            viirs['M05'] = (self.dims, np.full(viirs.M15.shape, _bad_data))
+        if 'M07' in viirs:
+            idx = np.nonzero((viirs.M07.values < -99) | (viirs.M07.values > 2))
+            viirs['M07'].values[idx] = _bad_data
+        else:
+            viirs['M07'] = (self.dims, np.full(viirs.M15.shape, _bad_data))
+
+        idx = np.nonzero((viirs.M12.values < 0) | (viirs.M12.values > 1000))
+        viirs['M12'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M13.values < 0) | (viirs.M13.values > 1000))
+        viirs['M13'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M14.values < 0) | (viirs.M14.values > 1000))
+        viirs['M14'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M15.values < 0) | (viirs.M15.values > 1000))
+        viirs['M15'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M16.values < 0) | (viirs.M16.values > 1000))
+        viirs['M16'].values[idx] = _bad_data
+        """
+
+        # Compute channel differences and ratios that are used in the tests
+        if (('M05' in viirs) and ('M07' in viirs)):
+            viirs['M07-M05ratio'] = (self.dims, viirs.M07.values / viirs.M05.values)
+        else:
+            viirs['M07-M05ratio'] = (self.dims, np.full(viirs.M15.shape, _bad_data))
+        viirs['M13-M16'] = (self.dims, viirs.M13.values - viirs.M16.values)
+        viirs['M14-M15'] = (self.dims, viirs.M14.values - viirs.M15.values)
+        viirs['M15-M12'] = (self.dims, viirs.M15.values - viirs.M12.values)
+        viirs['M15-M13'] = (self.dims, viirs.M15.values - viirs.M13.values)
+        viirs['M15-M16'] = (self.dims, viirs.M15.values - viirs.M16.values)
+        viirs['GEMI'] = (self.dims, gemi)
+
+        # temp value to force the code to work
+        viirs['M128'] = (self.dims, np.zeros(viirs.M15.shape))
+
+        viirs.update(geo_data)
+        viirs = viirs.set_coords(['latitude', 'longitude'])
+
+        logging.debug('Viirs preprocessing completed successfully.')
+        return viirs
+
+    def relative_azimuth_angle(self,
+                               sensor_azimuth: npt.NDArray[float],
+                               solar_azimuth: npt.NDArray[float]) -> npt.NDArray[float]:
+        """Computation of the relative azimuth angle
+
+        Parameters
+        ----------
+        sensor_azimuth: np.ndarray
+            sensor azimuth angle from the geolocation file
+        solar_azimuth: np.ndarray
+            solar azimuth angle from the geolocation file
+
+        Returns
+        -------
+        relative_azimuth: np.ndarray
+        """
+        rel_azimuth = np.abs(180. - np.abs(sensor_azimuth - solar_azimuth))
+
+        logging.debug('Relative azimuth calculated successfully.')
+
+        return rel_azimuth
+
+    def sun_glint_angle(self,
+                        sensor_zenith: npt.NDArray[float],
+                        solar_zenith: npt.NDArray[float],
+                        rel_azimuth: npt.NDArray[float]) -> npt.NDArray[float]:
+        """Computation of the sun glint angle
+
+        Parameters
+        ----------
+        sensor_zenith: np.ndarray
+            sensor zenith angle from the geolocation file
+        solar_zenith: np.ndarray
+            solar zenith angle from the geolocation file
+        relative_azimuth: np.ndarray
+            relative azimuth computed from function relative_azimuth_angle()
+
+        Returns
+        -------
+        sunglint_angle: np.ndarray
+        """
+        cossna = (np.sin(sensor_zenith*_DTR) * np.sin(solar_zenith*_DTR) * np.cos(rel_azimuth*_DTR) +
+                  np.cos(sensor_zenith*_DTR) * np.cos(solar_zenith*_DTR))
+        cossna[cossna > 1] = 1
+        sunglint_angle = np.arccos(cossna) * _RTD
+
+        logging.debug('Sunglint generated')
+
+        return sunglint_angle
+
+    def scattering_angle(self,
+                         solar_zenith: npt.NDArray[float],
+                         sensor_zenith: npt.NDArray[float],
+                         relative_azimuth: npt.NDArray[float]) -> npt.NDArray[float]:
+        """Computation of the scattering angle
+
+        Parameters
+        ----------
+        solar_zenith: np.ndarray
+            solar zenith angle from the geolocation file
+        sensor_zenith: np.ndarray
+            sensor zenith angle angle from the geolocation file
+        relative_azimuth: np.ndarray
+            relative azimuth computed from function relative_azimuth_angle()
+
+        Returns
+        -------
+        scattering_angle: np.ndarray
+        """
+        cos_scatt_angle = -1. * (np.cos(solar_zenith*_DTR) * np.cos(sensor_zenith*_DTR) -
+                                 np.sin(solar_zenith*_DTR) * np.sin(sensor_zenith*_DTR) *
+                                 np.cos(relative_azimuth*_DTR))
+
+        scatt_angle = np.arccos(cos_scatt_angle) * _RTD
+
+        logging.debug('Scattering angle calculated successfully')
+
+        return scatt_angle
+
+
+@define(kw_only=True, slots=True)
+class ReadAncillary(CollectInputs):
+    """Class tha processes all the ancillary files and makes them available for the MVCM"
+
+    Parameters
+    ----------
+    latitude: npt.NDArray[float]
+        array of latitudes for the granule that is being processed
+    longitude: npt.NDArray[float]
+        array of longitudes for the granule that is being processed
+    resolution: int
+        flag to switch between MOD (1) and IMG (2) resolution
+    """
+    latitude: npt.NDArray[float] = field(validator=[validators.instance_of(np.ndarray), ])
+    longitude: npt.NDArray[float] = field(validator=[validators.instance_of(np.ndarray), ])
+    resolution: int = field(validator=[validators.instance_of(int),
+                            validators.in_([1, 2]), ])
+
+    out_shape: tuple = field(init=False,
+                             default=Factory(lambda self: self.latitude.shape, takes_self=True))
+
+    def get_granule_time(self):
+        """Get granule timestamp and format it for MVCM"""
+        vnp_time = '.'.join(os.path.basename(self.file_name_l1b).split('.')[1:3])
+        return dt.strftime(dt.strptime(vnp_time, 'A%Y%j.%H%M'), '%Y-%m-%d %H:%M:%S.000')
+
+    def get_sst(self) -> npt.NDArray[float]:
+        """Read Reynolds SST file
+
+        Parameters
+        ----------
+
+        Returns
+        -------
+        sst: npt.NDArray[float]
+            array containing the Reynolds SST interpolated at the sensor's resolution
+        """
+        if not os.path.isfile(os.path.join(self.ancillary_dir, self.sst_file)):
+            logging.error('SST file not found')
+        sst = np.empty(self.out_shape, dtype=np.float32).ravel()
+        sst = anc.py_get_Reynolds_SST(self.latitude.ravel(), self.longitude.ravel(), self.resolution,
+                                      self.ancillary_dir, self.sst_file, sst)
+        logging.debug('SST file read successfully')
+        return sst.reshape(self.out_shape)
+
+    def get_ndvi(self) -> npt.NDArray[float]:
+        """Read NDVI file
+
+        Parameters
+        ----------
+
+        Returns
+        -------
+        ndvi: npt.NDArray[float]
+            NDVI interpolated at the sensor's resolution
+        """
+        if not os.path.isfile(os.path.join(self.ancillary_dir, self.ndvi_file)):
+            logging.error('NDVI file not found')
+        ndvi = np.empty(self.out_shape, dtype=np.float32).ravel()
+        ndvi = anc.py_get_NDVI_background(self.latitude.ravel(), self.longitude.ravel(), self.resolution,
+                                          self.ancillary_dir, self.ndvi_file, ndvi)
+        logging.debug('NDVI file read successfully')
+        return ndvi.reshape(self.out_shape)
+
+    def get_eco(self) -> npt.NDArray[float]:
+        """Read ECO file
+
+        Parameters
+        ----------
+
+        Returns
+        -------
+        eco: npt.NDArray[float]
+            Olson ecosystem type interpolated at the sensor's resolution
+        """
+        eco = np.empty(self.out_shape, dtype=np.ubyte).ravel()
+        eco = anc.py_get_Olson_eco(self.latitude.ravel(), self.longitude.ravel(), self.resolution,
+                                   self.ancillary_dir, eco)
+        logging.debug('Olson ecosystem file read successfully')
+        return eco.reshape(self.out_shape)
+
+    def get_geos(self) -> Dict:
+        """Read GEOS-5 data and interpolate the fields to the sensor resolution
+
+        Parameters
+        ----------
+
+        Returns
+        -------
+        geos_data: Dict
+            dictionary containing all quantities required by MVCM (see geos_variables here below)
+        """
+        if not os.path.isfile(os.path.join(self.ancillary_dir, self.geos_file_1)):
+            logging.error('GEOS-5 file 1 not found')
+        if not os.path.isfile(os.path.join(self.ancillary_dir, self.geos_file_2)):
+            logging.error('GEOS-5 file 2 not found')
+        if not os.path.isfile(os.path.join(self.ancillary_dir, self.geos_land)):
+            logging.error('GEOS-5 land file not found')
+        if not os.path.isfile(os.path.join(self.ancillary_dir, self.geos_ocean)):
+            logging.error('GEOS-5 ocean file not found')
+        if not os.path.isfile(os.path.join(self.ancillary_dir, self.geos_constants)):
+            logging.error('GEOS-5 constants file not found')
+
+        geos_variables = ['tpw', 'snow_fraction', 'ice_fraction', 'ocean_fraction',
+                          'land_ice_fraction', 'surface_temperature']
+        geos_data = {var: np.empty(self.out_shape, dtype=np.float32).ravel() for var in geos_variables}
+
+        geos_data = anc.py_get_GEOS(self.latitude.ravel(), self.longitude.ravel(), self.resolution,
+                                    self.get_granule_time(), self.ancillary_dir, self.geos_file_1,
+                                    self.geos_file_2, self.geos_land, self.geos_ocean,
+                                    self.geos_constants, geos_data)
+
+        for var in list(geos_variables):
+            geos_data[var] = geos_data[var].reshape(self.out_shape)
+
+        logging.debug('GEOS data read successfully')
+        return geos_data
+
+    def pack_data(self) -> xr.Dataset:
+        """Pack all the ancillary data into a single dataset
+
+        Parameters
+        ----------
+
+        Returns
+        -------
+        ancillary_data: xr.Dataset
+            dataset containing all the ancillary data
+        """
+        ancillary_data = xr.Dataset()
+        ancillary_data['sst'] = (self.dims, self.get_sst())
+        ancillary_data['ecosystem'] = (self.dims, self.get_eco())
+        ancillary_data['ndvi'] = (self.dims, self.get_ndvi())
+
+        geos_tmp = self.get_geos()
+        for var in list(geos_tmp.keys()):
+            ancillary_data[f'geos_{var}'] = (self.dims, geos_tmp[var])
+
+        return ancillary_data
+
+
+"""
+    scene_flags = scn.find_scene(viirs, sunglint_angle)
+    scene = scn.scene_id(scene_flags)
+
+    scene_xr = xr.Dataset()
+    for s in scn._scene_list:
+        scene_xr[s] = (('number_of_lines', 'number_of_pixels'), scene[s])
+
+    scene['lat'] = viirs.latitude
+    scene['lon'] = viirs.longitude
+
+    data = xr.Dataset(viirs, coords=scene_xr)
+    data.drop_vars(['latitude', 'longitude'])
+
+
+
+def get_data(satellite: str,
+             sensor: str,
+             file_names: Dict[str, str],
+             sunglint_angle: float,
+             hires: bool = False) -> xr.Dataset:
+
+    mod02 = file_names['MOD02']
+    mod03 = file_names['MOD03']
+
+    if hires is True:
+        img02 = file_names['IMG02']
+        img03 = file_names['IMG03']
+
+    if hires is False:
+        viirs = read_viirs(sensor, f'{mod02}', f'{mod03}')
+        viirs = read_ancillary_data(file_names, viirs)
+
+        if (('M05' in viirs) and ('M07' in viirs)):
+            m01 = viirs.M05.values
+            m02 = viirs.M07.values
+            r1 = 2.0 * (np.power(m02, 2.0) - np.power(m01, 2.0)) + (1.5 * m02) + (0.5 * m01)
+            r2 = m02 + m01 + 0.5
+            r3 = r1 / r2
+            gemi = r3 * (1.0 - 0.25*r3) - ((m01 - 0.125) / (1.0 - m01))
+        else:
+            gemi = np.full((viirs.M15.shape), _bad_data)
+
+        if 'M05' in viirs:
+            idx = np.nonzero((viirs.M05.values < -99) | (viirs.M05.values > 2))
+            viirs['M05'].values[idx] = _bad_data
+        else:
+            viirs['M05'] = (('number_of_lines', 'number_of_pixels'),
+                                 np.full(viirs.M15.shape, _bad_data))
+        if 'M07' in viirs:
+            idx = np.nonzero((viirs.M07.values < -99) | (viirs.M07.values > 2))
+            viirs['M07'].values[idx] = _bad_data
+        else:
+            viirs['M07'] = (('number_of_lines', 'number_of_pixels'),
+                                 np.full(viirs.M15.shape, _bad_data))
+
+        idx = np.nonzero((viirs.M12.values < 0) | (viirs.M12.values > 1000))
+        viirs['M12'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M13.values < 0) | (viirs.M13.values > 1000))
+        viirs['M13'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M14.values < 0) | (viirs.M14.values > 1000))
+        viirs['M14'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M15.values < 0) | (viirs.M15.values > 1000))
+        viirs['M15'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M16.values < 0) | (viirs.M16.values > 1000))
+        viirs['M16'].values[idx] = _bad_data
+
+        # Compute channel differences and ratios that are used in the tests
+        viirs['M15-M13'] = (('number_of_lines', 'number_of_pixels'),
+                                 viirs.M15.values - viirs.M13.values)
+        viirs['M14-M15'] = (('number_of_lines', 'number_of_pixels'),
+                                 viirs.M14.values - viirs.M15.values)
+        viirs['M15-M16'] = (('number_of_lines', 'number_of_pixels'),
+                                 viirs.M15.values - viirs.M16.values)
+        viirs['M15-M12'] = (('number_of_lines', 'number_of_pixels'),
+                                 viirs.M15.values - viirs.M12.values)
+        viirs['M13-M16'] = (('number_of_lines', 'number_of_pixels'),
+                                 viirs.M13.values - viirs.M16.values)
+        viirs['M07-M05ratio'] = (('number_of_lines', 'number_of_pixels'),
+                                      viirs.M07.values / viirs.M05.values)
+        viirs['GEMI'] = (('number_of_lines', 'number_of_pixels'), gemi)
+
+        # temp value to force the code to work
+        viirs['M128'] = (('number_of_lines', 'number_of_pixels'), np.zeros(viirs.M15.shape))
+
+    else:
+        viirs = read_viirs('viirs', f'{img02}', f'{img03}')
+        viirs['M05'] = viirs.I01
+        viirs['M07'] = viirs.I02
+
+        idx = np.nonzero((viirs.M05.values < -99) | (viirs.M05.values > 2))
+        viirs['M05'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.M07.values < -99) | (viirs.M07.values > 2))
+        viirs['M07'].values[idx] = _bad_data
+
+        idx = np.nonzero((viirs.I01.values < 0) | (viirs.I01.values > 1000))
+        viirs['I01'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.I02.values < 0) | (viirs.I02.values > 1000))
+        viirs['I02'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.I03.values < 0) | (viirs.I03.values > 1000))
+        viirs['I03'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.I04.values < 0) | (viirs.I04.values > 1000))
+        viirs['I04'].values[idx] = _bad_data
+        idx = np.nonzero((viirs.I05.values < 0) | (viirs.I05.values > 1000))
+        viirs['I05'].values[idx] = _bad_data
+
+        viirs = read_ancillary_data(file_names, viirs, resolution=2)
+
+        viirs['I05-I04'] = (('number_of_lines_2', 'number_of_pixels_2'),
+                                 viirs.I05.values - viirs.I04.values)
+        viirs['I02-I01ratio'] = (('number_of_lines_2', 'number_of_pixels_2'),
+                                      viirs.I02.values / viirs.I01.values)
+
+    scene_flags = scn.find_scene(viirs, sunglint_angle)
+    scene = scn.scene_id(scene_flags)
+
+    scene_xr = xr.Dataset()
+    for s in scn._scene_list:
+        scene_xr[s] = (('number_of_lines', 'number_of_pixels'), scene[s])
+
+    scene['lat'] = viirs.latitude
+    scene['lon'] = viirs.longitude
+
+    data = xr.Dataset(viirs, coords=scene_xr)
+    data.drop_vars(['latitude', 'longitude'])
+
+    return data
+"""
diff --git a/mvcm/restoral.py b/mvcm/restoral.py
new file mode 100644
index 0000000..e89e3b5
--- /dev/null
+++ b/mvcm/restoral.py
@@ -0,0 +1,127 @@
+import numpy as np
+
+from numpy.lib.stride_tricks import sliding_window_view
+
+_bad_data = -999.0
+
+
+def spatial_var(rad, threshold):
+
+    test = sliding_window_view(np.pad(rad, [1, 1], mode='constant'), (3, 3)) - np.expand_dims(rad, (2, 3))
+    test = np.abs(test.reshape(rad.shape[0], rad.shape[1], 9))
+
+    var = np.ones(test.shape)
+    var[test < threshold] = 0
+
+    return var.sum(axis=2)
+
+
+def sunglint(viirs_data, threshold, bit, conf):
+
+    m09 = viirs_data.M02.values
+    m20 = viirs_data.M12.values
+    m31 = viirs_data.M15.values
+    m02 = viirs_data.M07.values
+    m01 = viirs_data.M05.values
+    irclr = np.ones(viirs_data.M02.shape)
+
+    idx = np.nonzero((m02 > 1.3) & (m01 <= 2.0))
+    m02[idx] = 1.3
+    idx = np.nonzero((m02 < -99) | (m02 > 1.3))
+    m02[idx] = _bad_data
+
+    irclr[bit < 3] = 0
+
+    var = spatial_var(m31, 0.4)
+    reg_var_mean = sliding_window_view(np.pad(m02, [1, 1], mode='constant'),
+                                       (3, 3)).reshape(m02.shape[0], m02.shape[1], 9).mean(axis=2)
+    reg_std = sliding_window_view(np.pad(m02, [1, 1], mode='constant'),
+                                  (3, 3)).reshape(m02.shape[0], m02.shape[1], 9).std(axis=2)
+    d37_11 = m20 - m31
+
+    idx = np.nonzero((var == 0) & (m09 != _bad_data) & (m20 != _bad_data) & (m31 != _bad_data) & (irclr == 1) &
+                     (m02 != _bad_data) & (m09 < threshold['sngm09']) & (d37_11 >= threshold['sg_tbdfl']))
+    conf[idx] = 0.67
+
+    idx = np.nonzero((var == 0) & (m09 != _bad_data) & (m20 != _bad_data) & (m31 != _bad_data) & (irclr == 1) &
+                     (m02 != _bad_data) & (m09 < threshold['sngm09']) & (d37_11 >= threshold['sg_tbdfl']) &
+                     (reg_var_mean != _bad_data) & (reg_var_mean*reg_std < threshold['sngm02vm']))
+    conf[idx] = 0.96
+
+    return conf
+
+
+def spatial(viirs_data, threshold, scene, conf):
+
+    m02 = viirs_data.M07.values
+    m31 = viirs_data.M15.values
+
+    var_m31 = spatial_var(m31, 0.40)
+    var_m02 = spatial_var(m02, 0.0020)
+
+    idx = np.nonzero((conf > 0.95) & (scene['day'] == 1) & (var_m31 == 0) & (var_m02 == 0))
+    conf[idx] = 1
+
+    idx = np.nonzero((conf > 0.66) & (conf <= 0.95) & (scene['day'] == 1) & (var_m31 == 0) & (var_m02 == 0))
+    conf[idx] = 0.96
+
+    idx = np.nonzero((conf <= 0.66) & (scene['day'] == 1) & (var_m31 == 0))
+    conf[idx] = 0.67
+
+    return conf
+
+
+def land(viirs_data, threshold, scene, conf):
+    m04 = viirs_data.M04.values.ravel()
+    m05 = viirs_data.M08.values.ravel()
+    m20 = viirs_data.M12.values.ravel()
+    m22 = viirs_data.M13.values.ravel()
+    m31 = viirs_data.M15.values.ravel()
+    eco = viirs_data.eco.values.ravel()
+    desert = scene['desert'].ravel()
+    conf = conf.ravel()
+    tbadj = 0
+    ldsbt11bd = np.array(threshold['Land_Restoral']['ldsbt11bd'])
+    ldsbt11 = np.array(threshold['Land_Restoral']['ldsbt11bd'])
+
+    irclr = 1
+    hds11 = np.ones((eco.shape[0], 3)) * (ldsbt11 - tbadj)
+    hds11[eco == 8, :] = ldsbt11bd - tbadj
+
+    if irclr == 1:
+        conf[m31 > hds11[:, 2]] = 1
+        conf[(m31 > hds11[:, 1]) & (m31 <= hds11[:, 2])] = 0.96
+        conf[m31 <= hds11[:, 1]] = 0.5
+
+    m5_4_thr = np.full(eco.shape, threshold['Land_Restoral']['ldr5_4_thr'])
+    m5_4_thr[desert == 1] = threshold['Land_Restoral']['ldsr5_4_thr']
+
+    m5_4 = m05/m04
+    md1 = m20 - m22
+    md2 = m22 - m31
+
+    idx = np.nonzero((md1 < threshold['Land_Restoral']['ld20m22']) &
+                     (md2 < threshold['Land_Restoral']['ld22m31']) &
+                     (m5_4 > m5_4_thr) &
+                     (conf <= 0.95))
+    conf[idx] = 0.96
+
+    conf = conf.reshape(viirs_data.M01.shape)
+    return conf
+
+
+def coast(viirs_data, threshold, scene, conf):
+
+    m01 = viirs_data.M05.values
+    m02 = viirs_data.M07.values
+    coast_ndvi = threshold['Coastal_NDVI_Thresholds']['coast_ndvi']
+
+    irclr = 1
+
+    if irclr == 1:
+        ndvi = (m02 - m01)/(m01 + m02)
+
+        idx = np.nonzero((ndvi <= coast_ndvi[0]) | (ndvi >= coast_ndvi[1]))
+        conf[idx] = 1
+
+    return conf
diff --git a/mvcm/scene.py b/mvcm/scene.py
new file mode 100644
index 0000000..5e03b43
--- /dev/null
+++ b/mvcm/scene.py
@@ -0,0 +1,446 @@
+import numpy as np
+try:
+    from ruamel import yaml as yml
+except ImportError:
+    import ruamel_yaml as yml
+
+# from glob import glob
+
+import read_data as rd
+import ancillary_data as anc
+
+# lsf: land sea flag
+_scene_list = ['Ocean_Day', 'Ocean_Night', 'Land_Day', 'Land_Night', 'Day_Snow', 'Night_Snow', 'Coast_Day',
+               'Land_Day_Desert', 'Antarctic_Day', 'Polar_Day_Snow', 'Polar_Day_Desert', 'Polar_Day_Ocean',
+               'Polar_Day_Desert_Coast', 'Polar_Day_Coast', 'Polar_Day_Land', 'Polar_Night_Snow',
+               'Polar_Night_Land', 'Polar_Night_Ocean', 'Land_Day_Desert_Coast', 'Land_Day_Coast', 'Desert',
+               'Australia']
+_flags = ['day', 'night', 'land', 'coast', 'sh_lake', 'sh_ocean', 'water', 'polar', 'sunglint',
+          'greenland', 'high_elevation', 'antarctica', 'desert', 'visusd', 'vrused', 'map_snow', 'map_ice',
+          'ndsi_snow', 'snow', 'ice', 'new_zealand', 'uniform', 'australia']
+
+# temp value, need to verify what the actual bad_data value is in the C code
+_bad_data = -999.0
+
+_dtr = np.pi/180.
+_rtd = 180./np.pi
+
+# I'm defining here the flags for difference scenes. Eventually I want to find a better way of doing this
+land = 1
+# coast = .2
+sh_lake = .3
+sh_ocean = .4
+water = 5
+polar = 60
+sunglint = 70
+day = 100
+night = 200
+
+# #################################################################### #
+# TEST CASE
+# data:
+#datapath = '/ships19/hercules/pveglio/mvcm_viirs_hires'
+#fname_mod02 = glob(f'{datapath}/VNP02MOD.A2022173.1448.001.*.uwssec*.nc')[0]
+#fname_mod03 = glob(f'{datapath}/VNP03MOD.A2022173.1448.001.*.uwssec.nc')[0]
+#fname_img02 = glob(f'{datapath}/VNP02IMG.A2022173.1448.001.*.uwssec*.nc')[0]
+#fname_img03 = glob(f'{datapath}/VNP03IMG.A2022173.1448.001.*.uwssec.nc')[0]
+
+# thresholds:
+#threshold_file = '/home/pveglio/mvcm_leo/thresholds/new_thresholds.mvcm.snpp.v1.0.0.yaml'
+
+# ancillary files:
+geos_atm_1 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1200.V01.nc4'
+geos_atm_2 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1500.V01.nc4'
+geos_land = 'GEOS.fpit.asm.tavg1_2d_lnd_Nx.GEOS5124.20220622_1430.V01.nc4'
+geos_ocean = 'GEOS.fpit.asm.tavg1_2d_ocn_Nx.GEOS5124.20220622_1430.V01.nc4'
+geos_constants = 'GEOS.fp.asm.const_2d_asm_Nx.00000000_0000.V01.nc4'
+ndvi_file = 'NDVI.FM.c004.v2.0.WS.00-04.177.hdf'
+sst_file = 'oisst.20220622'
+eco_file = 'goge1_2_img.v1'
+
+# #################################################################### #
+
+
+def test_scene():
+
+    ancillary_file_names = {'GEOS_atm_1': geos_atm_1,
+                            'GEOS_atm_2': geos_atm_2,
+                            'GEOS_land': geos_land,
+                            'GEOS_ocean': geos_ocean,
+                            'GEOS_constants': geos_constants,
+                            'NDVI': ndvi_file,
+                            'SST': sst_file,
+                            'ANC_DIR': f'{datapath}/ancillary'
+                            }
+
+    viirs_data = rd.read_data('viirs', f'{fname_mod02}', f'{fname_mod03}')
+
+    viirs_data = rd.read_ancillary_data(ancillary_file_names, viirs_data)
+
+    with open(threshold_file) as f:
+        text = f.read()
+    thresholds = yml.safe_load(text)
+
+    sunglint_angle = thresholds['Sun_Glint']['bounds'][3]
+
+    scene = find_scene(viirs_data, sunglint_angle)
+
+    return scene
+
+
+def find_scene(data, sunglint_angle):
+    eco = np.array(data['ecosystem'].values, dtype=np.uint8)
+    snowfr = data['geos_snowfr'].values
+    icefr = data['geos_icefr'].values
+    lsf = np.array(data['land_water_mask'].values, dtype=np.uint8)
+    lat = data['latitude'].values
+    lon = data['longitude'].values
+    sza = data['solar_zenith'].values
+    vza = data['sensor_zenith'].values
+    raz = data['relative_azimuth'].values
+    b065 = data['M05'].values
+    b086 = data['M07'].values
+    elev = data['height'].values
+    ndvibk = data['ndvi'].values
+    sfctmp = data['geos_sfct'].values
+
+    dim1 = data.latitude.shape[0]
+    dim2 = data.latitude.shape[1]
+
+    day = np.zeros((dim1, dim2))
+    day[sza <= 85] = 1
+    cos_refang = np.sin(vza*_dtr) * np.sin(sza*_dtr) * np.cos(raz*_dtr) + np.cos(vza*_dtr) * np.cos(sza*_dtr)
+    refang = np.arccos(cos_refang) * _rtd
+
+    # tmp = np.ones((dim1, dim2))
+    # tmp[day == 1] = day
+    # tmp[day == 0] = night
+
+    scene_flag = {flg: np.zeros((dim1, dim2)) for flg in _flags}
+
+    scene_flag['day'][sza <= 85] = 1
+    scene_flag['visusd'][sza <= 85] = 1
+    scene_flag['night'][sza > 85] = 1
+
+    scene_flag['polar'][np.abs(lat) > 60] = 1
+    # ################# need to pass refang (once I figure out what it is) and sunglint_angle. The latter
+    # comes from the thresholds file. In the C code is snglnt_bounds[3]
+    idx = np.nonzero((scene_flag['day'] == 1) & (refang <= sunglint_angle))
+    scene_flag['sunglint'][idx] = 1
+
+    # Force consistency between lsf and ecosystem type for water
+    idx = np.nonzero((lsf == 0) | (lsf >= 5) & (lsf < 7))
+    eco[idx] = 14
+
+    # start by defining anythings as land
+    scene_flag['land'] = np.ones((dim1, dim2))
+    scene_flag['water'] = np.zeros((dim1, dim2))
+
+    # Fix-up for missing ecosystem data in eastern Greenland and north-eastern Siberia.
+    # Without this, these regions become completely "coast".
+    idx = np.nonzero((lsf != 255) & (lsf == 1) | (lsf == 4))
+    scene_flag['land'][idx] = 1
+
+    # idx = np.nonzero((lsf != 255) & (eco == 14))
+
+    idx = np.nonzero((lsf != 255) & (eco == 14) & (lsf == 1) | (lsf == 4) & (lat < 64.0))
+    scene_flag['coast'][idx] = 1
+
+    idx = np.nonzero((lsf != 255) & (eco == 14) & (lsf == 1) | (lsf == 4) &
+                     (lat >= 67.5) & (lon < -40.0) & (lon > -168.6))
+    scene_flag['coast'][idx] = 1
+    idx = np.nonzero((lsf != 255) & (eco == 14) & (lsf == 1) | (lsf == 4) &
+                     (lat >= 67.5) & (lon > -12.5))
+    scene_flag['coast'][idx] = 1
+
+    idx = np.nonzero((lsf != 255) & (eco == 14) & (lsf == 1) | (lsf == 4) &
+                     (lat >= 64.0) & (lat < 67.5) & (lon < -40.0) & (lon > -168.5))
+    scene_flag['coast'][idx] = 1
+    idx = np.nonzero((lsf != 255) & (eco == 14) & (lsf == 1) | (lsf == 4) &
+                     (lat >= 64.0) & (lat < 67.5) & (lon > -30.0))
+    scene_flag['coast'][idx] = 1
+
+    idx = np.nonzero(lsf == 2)
+    scene_flag['coast'][idx] = 1
+    scene_flag['land'][idx] = 1
+
+    idx = np.nonzero(lsf == 3)
+    scene_flag['land'][idx] = 1
+    scene_flag['sh_lake'][idx] = 1
+
+    idx = np.nonzero((lsf == 0) | (lsf >= 5) & (lsf <= 7))
+    scene_flag['water'][idx] = 1
+    scene_flag['land'][idx] = 0
+
+    scene_flag['sh_ocean'][lsf == 0] = 1
+
+    # Need shallow lakes to be processed as "coast" for day, but not night
+    idx = np.nonzero((lsf == 3) & (day == 1))
+    scene_flag['coast'][idx] = 1
+
+    # if land/sea flag is missing, then calculate visible ratio to determine if land or water.
+    idx = np.nonzero((lsf == 255) & (b065 != _bad_data) & (b086 != _bad_data) & (b086/b065 > 0.9))
+    scene_flag['land'][idx] = 1
+
+    idx = np.nonzero((lsf == 255) & (b065 != _bad_data) & (b086 != _bad_data) & (b086/b065 <= 0.9))
+    scene_flag['land'][idx] = 0
+    scene_flag['water'][idx] = 1
+
+    # Check surface elevation
+    # First, define "Greenland".
+    idx = np.nonzero((scene_flag['land'] == 1) &
+                     (lat >= 60.0) & (lat < 67.0) & (lon >= -60.0) & (lon < -30.0))
+    scene_flag['greenland'][idx] = 1
+
+    idx = np.nonzero((scene_flag['land'] == 1) &
+                     (lat >= 67.0) & (lat < 75.0) & (lon >= -60.0) & (lon < -10.0))
+    scene_flag['greenland'][idx] = 1
+
+    idx = np.nonzero((scene_flag['land'] == 1) &
+                     (lat >= 75.0) & (lon >= -70.0) & (lon < -10.0))
+    scene_flag['greenland'][idx] = 1
+
+    scene_flag['high_elevation'][elev > 2000] = 1
+    idx = np.nonzero((elev > 200) & (scene_flag['greenland'] == 1) & (scene_flag['land'] == 1))
+    scene_flag['high_elevation'][idx] = 1
+
+    idx = np.nonzero((lat >= 75.0) & (lat <= 79.0) & (lon >= -73.0) & (lon <= -50.0) &
+                     (scene_flag['land'] == 1))
+    scene_flag['high_elevation'][idx] = 1
+
+    scene_flag['antarctica'][lat < -60.0] = 1
+
+    ##################################
+    #  somewhere here I need to add  #
+    #  the 11um elevation correction #
+    ##################################
+
+    # this is a temporary variable for the 11um elevation correction
+    elev_correction = elev/1000.0 * 5.0
+
+    ## Get surface temperature from NWP and SST fields
+    ## if it's land get it from GDAS/GEOS5
+    #sfctmp[scene_flag['land'] == 1] = sfct
+    ## otherwise use the ReynoldsSST
+    #sfctmp[scene_flag['land'] == 0] = reynSST
+
+    # Use background NDVI to define "desert"
+    idx = np.nonzero((scene_flag['land'] == 1) & (ndvibk < 0.3))
+    scene_flag['desert'][idx] = 1
+    idx = np.nonzero((scene_flag['land'] == 1) & (lat < -69.0))
+    scene_flag['desert'][idx] = 1
+
+    idx = np.nonzero((eco == 2) | (eco == 8) | (eco == 11) | (eco == 40) | (eco == 41) | (eco == 46) |
+                     (eco == 51) | (eco == 52) | (eco == 59) | (eco == 71) | (eco == 50))
+    scene_flag['vrused'] = np.ones((dim1, dim2))
+    scene_flag['vrused'][idx] = 0
+
+    snow_fraction = data['geos_snowfr']
+    perm_ice_fraction = data['geos_landicefr']
+    ice_fraction = data['geos_icefr']
+
+    idx = tuple(np.nonzero((snow_fraction > 0.10) & (snow_fraction <= 1.0)))
+    scene_flag['map_snow'][idx] = 1
+
+    idx = tuple(np.nonzero((perm_ice_fraction > 0.10) & (perm_ice_fraction <= 1.0)))
+    scene_flag['map_snow'][idx] = 1
+
+    idx = tuple(np.nonzero((ice_fraction > 0.10) & (ice_fraction <= 1.0)))
+    scene_flag['map_ice'][idx] = 1
+
+    # need to define this function and write this block better
+    # if day == 1:
+    #    # Run quick version of D. Hall's snow detection algorithm
+    #    # Temporarily disabled because this function does not exist and I need to decide how to implement it
+    #    # scene_flag['ndsi_snow'] = run_snow_mask()
+    #     scene_flag['ndsi_snow'] = np.zeros((dim1, dim2))
+
+    idx = np.nonzero((day == 1) & (water == 1) & (lat >= -60.0) & (lat <= 25.0) &
+                     (scene_flag['map_snow'] == 1) & (scene_flag['ndsi_snow'] == 1))
+    scene_flag['ice'][idx] = 1
+
+    idx = np.nonzero((day == 1) & (water == 1) & (lat < -60.0) &
+                     (scene_flag['ndsi_snow'] == 1))
+    scene_flag['ice'][idx] = 1
+
+    idx = np.nonzero((day == 1) & (water == 1) & (lsf == 3) | (lsf == 5) &
+                     (scene_flag['ndsi_snow'] == 1))
+    scene_flag['ice'][idx] = 1
+
+    idx = np.nonzero((day == 1) & (water == 1) &
+                     (scene_flag['map_ice'] == 1) & (scene_flag['ndsi_snow'] == 1))
+    scene_flag['ice'][idx] = 1
+
+    # Define New Zealand region which receives snow but snow map does not show it.
+    idx = np.nonzero((day == 1) & (land == 1) &
+                     (lat >= 48.0) & (lat <= -34.0) & (lon >= 165.0) & (lon <= 180.0))
+    scene_flag['new_zealand'][idx] = 1
+
+    idx = np.nonzero((day == 1) & (land == 1) & (lat >= -60.0) & (lat <= 25.0) &
+                     (scene_flag['map_snow'] == 1) & (scene_flag['ndsi_snow'] == 1) |
+                     (scene_flag['new_zealand'] == 1))
+    scene_flag['snow'][idx] = 1
+
+    idx = np.nonzero((day == 1) & (land == 1) & (lat < -60.0))
+    scene_flag['snow'][idx] = 1
+
+    idx = np.nonzero((day == 1) & (land == 1) & (scene_flag['ndsi_snow'] == 1))
+    scene_flag['snow'][idx] = 1
+
+    idx = np.nonzero((day == 0) & (scene_flag['map_snow'] == 1) &
+                     (sfctmp > 280.0) & (elev < 500.0))
+    scene_flag['snow'][idx] = 0
+
+    idx = np.nonzero((day == 0) & (scene_flag['map_snow'] == 1) &
+                     (sfctmp > 280.0) & (elev < 500.0))
+    scene_flag['ice'][idx] = 0
+
+    idx = np.nonzero((day == 0) & (lat > 86.0))
+    scene_flag['ice'][idx] = 1
+
+    idx = np.nonzero((lat <= -11) & (lat > -40) & (lon <= 155) & (lon > 110))
+    scene_flag['australia'][idx] = 1
+
+    # Check regional uniformity
+    # Check for data border pixels
+    scene_flag['uniform'] = anc.py_check_reg_uniformity(eco, eco, snowfr, icefr, lsf)['loc_uniform']
+    print(scene_flag['uniform'][:10, :10])
+
+    # TEMP VALUES FOR DEBUGGING
+    scene_flag['lat'] = lat
+    scene_flag['lon'] = lon
+
+    return scene_flag
+
+
+def scene_id(scene_flag):
+
+    dim1, dim2 = scene_flag['day'].shape[0], scene_flag['day'].shape[1]
+    scene = {scn: np.zeros((dim1, dim2)) for scn in _scene_list}
+
+    # Ocean Day
+    idx = np.nonzero((scene_flag['water'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0) &
+                     (scene_flag['polar'] == 0) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['coast'] == 0) & (scene_flag['desert'] == 0))
+    scene['Ocean_Day'][idx] = 1
+
+    # Ocean Night
+    idx = np.nonzero((scene_flag['water'] == 1) & (scene_flag['night'] == 1) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0) &
+                     (scene_flag['polar'] == 0) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['coast'] == 0) & (scene_flag['desert'] == 0))
+    scene['Ocean_Night'][idx] = 1
+
+    # Land Day
+    idx = np.nonzero((scene_flag['land'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0) &
+                     (scene_flag['polar'] == 0) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['coast'] == 0) & (scene_flag['desert'] == 0))
+    scene['Land_Day'][idx] = 1
+
+    # Land Night
+    idx = np.nonzero((scene_flag['land'] == 1) & (scene_flag['night'] == 1) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0) &
+                     (scene_flag['polar'] == 0) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['coast'] == 0))
+    scene['Land_Night'][idx] = 1
+
+    # Snow Day
+    idx = np.nonzero((scene_flag['day'] == 1) &
+                     ((scene_flag['ice'] == 1) | (scene_flag['snow'] == 1)) &
+                     (scene_flag['polar'] == 1) & (scene_flag['antarctica'] == 1))
+    scene['Day_Snow'][idx] = 1
+
+    # Snow Night
+    idx = np.nonzero((scene_flag['night'] == 1) &
+                     ((scene_flag['ice'] == 1) | (scene_flag['snow'] == 1)) &
+                     (scene_flag['polar'] == 1) & (scene_flag['antarctica'] == 1))
+    scene['Night_Snow'][idx] = 1
+
+    # Land Day Coast
+    idx = np.nonzero((scene_flag['land'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['coast'] == 1) & (scene_flag['desert'] == 0) &
+                     (scene_flag['polar'] == 0) & (scene_flag['antarctica'] == 0))
+    scene['Land_Day_Coast'][idx] = 1
+
+    # Land Day Desert
+    idx = np.nonzero((scene_flag['land'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['desert'] == 1) & (scene_flag['coast'] == 0) &
+                     (scene_flag['polar'] == 0) & (scene_flag['antarctica'] == 0))
+    scene['Land_Day_Desert'][idx] = 1
+
+    # Land Day Desert Coast
+    idx = np.nonzero((scene_flag['land'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['desert'] == 1) & (scene_flag['coast'] == 1) &
+                     (scene_flag['polar'] == 0) & (scene_flag['antarctica'] == 0))
+    scene['Land_Day_Desert_Coast'][idx] = 1
+
+    # Antarctic Day
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['antarctica'] == 1) & (scene_flag['land'] == 1))
+    scene['Antarctic_Day'][idx] = 1
+
+    # Polar Day Snow
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['day'] == 1) &
+                     ((scene_flag['snow'] == 1) | (scene_flag['ice'] == 1)) &
+                     (scene_flag['antarctica'] == 0))
+    scene['Polar_Day_Snow'][idx] = 1
+
+    # Polar Day Desert
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['land'] == 1) & (scene_flag['desert'] == 1) &
+                     (scene_flag['coast'] == 0) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0))
+    scene['Polar_Day_Desert'][idx] = 1
+
+    # Polar Day Desert Coast
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['land'] == 1) & (scene_flag['desert'] == 1) &
+                     (scene_flag['coast'] == 1) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0))
+    scene['Polar_Day_Desert_Coast'][idx] = 1
+
+    # Polar Day Coast
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['land'] == 1) & (scene_flag['coast'] == 1) &
+                     (scene_flag['desert'] == 0) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0))
+    scene['Polar_Day_Coast'][idx] = 1
+
+    # Polar Day Land
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['land'] == 1) & (scene_flag['coast'] == 0) &
+                     (scene_flag['desert'] == 0) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0))
+    scene['Polar_Day_Land'][idx] = 1
+
+    # Polar Day Ocean
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['day'] == 1) &
+                     (scene_flag['water'] == 1) & (scene_flag['coast'] == 0) &
+                     (scene_flag['desert'] == 0) & (scene_flag['antarctica'] == 0) &
+                     (scene_flag['ice'] == 0) & (scene_flag['snow'] == 0))
+    scene['Polar_Day_Ocean'][idx] = 1
+
+    # Polar Night Snow
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['night'] == 1) &
+                     ((scene_flag['snow'] == 1) | (scene_flag['ice'] == 1)))
+    scene['Polar_Night_Snow'][idx] = 1
+
+    # Polar Night Land
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['night'] == 1) &
+                     (scene_flag['land'] == 1))
+    scene['Polar_Night_Land'][idx] = 1
+
+    # Polar Night Ocean
+    idx = np.nonzero((scene_flag['polar'] == 1) & (scene_flag['night'] == 1) &
+                     (scene_flag['water'] == 1))
+    scene['Polar_Night_Ocean'][idx] = 1
+
+    idx = np.nonzero(scene_flag['desert'] == 1)
+    scene['Desert'][idx] = 1
+
+    idx = np.nonzero(scene_flag['australia'] == 1)
+    scene['Australia'][idx] = 1
+
+    return scene
diff --git a/mvcm/spectral_tests.py b/mvcm/spectral_tests.py
new file mode 100644
index 0000000..183efb5
--- /dev/null
+++ b/mvcm/spectral_tests.py
@@ -0,0 +1,826 @@
+import numpy as np
+import xarray as xr
+
+from numpy.lib.stride_tricks import sliding_window_view
+from typing import Dict
+
+import functools
+
+# import utils
+import conf
+import conf_xr
+import scene as scn
+import preprocess_thresholds as preproc
+import restoral
+
+import importlib
+
+_RTD = 180./np.pi
+_DTR = np.pi/180
+
+# this is used for testing, eventually we want to remove it
+importlib.reload(preproc)
+importlib.reload(conf)
+importlib.reload(restoral)
+
+
+class CloudTests(object):
+
+    def __init__(self,
+                 data: xr.Dataset,
+                 scene_name: str,
+                 thresholds: Dict) -> None:
+        self.data = data
+        self.scene_name = scene_name
+        self.thresholds = thresholds
+        self.scene_idx = tuple(np.nonzero(data[scene_name] == 1))
+        if self.scene_idx[0].shape[0] == 0:
+            self.pixels_in_scene = False
+        else:
+            self.pixels_in_scene = True
+
+    def run_if_test_exists_for_scene(func):
+        @functools.wraps(func)
+        def wrapper(self, *args, test_name, **kwargs):
+            if test_name not in self.thresholds[self.scene_name]:
+                print('Not running test for this scene')
+                # returns cmin. This could be changed into a keyworded argument for readability
+                test_bit = np.zeros(args[-1].shape)
+                return args[-1], test_bit
+            return func(self, *args, test_name, **kwargs)
+        return wrapper
+
+    @run_if_test_exists_for_scene
+    def test_11um(self,
+                  band: str,
+                  cmin: np.ndarray,
+                  test_name: str = '11um_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data[band].shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            idx = np.nonzero((self.data[band].values >= threshold['thr'][1]) &
+                             (self.data[self.scene_name] == 1))
+            test_bit[idx] = 1
+            confidence[self.scene_idx] = conf.conf_test_new(rad, threshold['thr'])
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def surface_temperature_test(self,
+                                 band: str,
+                                 viirs_data: xr.Dataset,
+                                 cmin: np.ndarray,
+                                 test_name: str = 'Surface_Temperature_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data[band].shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            sfcdif = viirs_data.geos_sfct.values[self.scene_idx] - rad
+            # need to write the test_bit here
+            thr = preproc.thresholds_surface_temperature(viirs_data, threshold, self.scene_idx)
+            confidence[self.scene_idx] = conf.conf_test_new(sfcdif, thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def sst_test(self,
+                 band31: str,
+                 band32: str,
+                 cmin: np.ndarray,
+                 test_name: str = 'SST_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data[band31].shape)
+        qa_bit = np.zeros(self.data[band31].shape)
+        test_bit = np.zeros(self.data[band31].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            m31 = self.data[band31].values - 273.16
+            bt_diff = self.data[band31].values - self.data[band32].values
+            sst = self.data.sst.values - 273.16
+            cosvza = np.cos(self.data.sensor_zenith.values*_DTR)
+
+            c = threshold['coeffs']
+
+            modsst = 273.16 + c[0] + c[1]*m31 + c[2]*bt_diff*sst + c[3]*bt_diff*((1.0/cosvza) - 1.0)
+            sfcdif = self.data.sst.values - modsst
+
+            idx = np.nonzero((sfcdif < threshold['thr'][1]) &
+                             (self.data[self.scene_name] == 1))
+            test_bit[idx] = 1
+            print(f'Testing "{self.scene_name}"\n')
+            confidence[self.scene_idx] = conf.conf_test_new(sfcdif[self.scene_idx], threshold['thr'])
+
+        cmin = np.fmin(cmin, confidence)
+
+        # return cmin, np.abs(1-test_bit)*qa_bit
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def bt_diff_86_11um(self,
+                        band: str,
+                        cmin: np.ndarray,
+                        test_name: str = '8.6-11um_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data[band].shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            idx = np.nonzero((self.data[band].values < threshold['thr'][1]) &
+                             (self.data[self.scene_name] == 1))
+            test_bit[idx] = 1
+            confidence[self.scene_idx] = conf.conf_test_new(rad, threshold['thr'])
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit  # np.abs(1-test_bit)*qa_bit
+
+    @run_if_test_exists_for_scene
+    def test_11_12um_diff(self,
+                          band: str,
+                          cmin: np.ndarray,
+                          test_name: str = '11-12um_Cirrus_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M15.shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            thr = preproc.thresholds_11_12um(self.data, threshold, self.scene_name, self.scene_idx)
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            idx = np.nonzero((rad <= thr[1, :]) &
+                             (self.data[self.scene_name].values[self.scene_idx] == 1))
+            tmp_bit = test_bit[self.scene_idx]
+            tmp_bit[idx] = 1
+            test_bit[self.scene_idx] = tmp_bit
+            confidence[self.scene_idx] = conf.conf_test_new(rad, thr)
+
+        cmin = np.fmin(cmin, confidence)
+        test_bit = test_bit.reshape(self.data[band].shape)
+        # return cmin, np.abs(1-test_bit)*qa_bit
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def bt_difference_11_4um_test(self,
+                                  band: str,
+                                  cmin: np.ndarray,
+                                  test_name: str = '11-4um_BT_Difference_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M15.shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            print(f'Testing "{self.scene_name}"\n')
+            thr = preproc.bt_diff_11_4um_thresholds(self.data, threshold, self.scene_idx)
+            rad = self.data[band].values[self.scene_idx]
+            test_bit[self.scene_idx] = 1  # THIS NEEDS TO BE CALCULATED PROPERLY
+            confidence[self.scene_idx] = conf.conf_test_dble(rad, thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def bt_difference_11_4um_test_land(self,
+                                       band1: str,
+                                       band2: str,
+                                       cmin: np.ndarray,
+                                       test_name: str = '11-4um_BT_Difference_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M15.shape)
+        qa_bit = np.zeros(self.data[band1].shape)
+        test_bit = np.zeros(self.data[band1].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        scene_flag = scn.find_scene(self.data, self.thresholds['Sun_Glint']['bounds'][3])
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            print(f'Testing "{self.scene_name}"\n')
+            if self.scene_name == 'Land_Night':
+                australia = self.data.Australia.values
+                ndvi = self.data.ndvi.values
+                coast = scene_flag['coast']
+                sh_lake = scene_flag['sh_lake']
+                idx = np.where(((australia == 0) & (ndvi < threshold['ndvi']) & (coast == 0)) |
+                               ((australia == 1) & (ndvi < threshold['ndvi_australia']) & (coast == 0)))
+                not_idx = np.ones(australia.shape, dtype=bool)
+                not_idx[idx] = False
+
+                rad11_12 = self.data[band1].values
+                rad11_4 = self.data[band2].values
+                thr = preproc.get_nl_thresholds(self.data, threshold, coast=False)
+                idx_lake = np.where((sh_lake == 1) | (rad11_12 < 0))
+                # test_bit[self.scene_idx] = 1  # THIS NEEDS TO BE CALCULATED PROPERLY
+                thr[:3, idx_lake] = thr[:3, idx_lake] + 2
+                temp_thr = np.squeeze(thr[:, idx[0]])
+                temp_rad = rad11_4[idx]
+                confidence[idx] = conf.conf_test_new(temp_rad, temp_thr)
+
+                thr = preproc.land_night_thresholds(self.data, threshold, coast=True)
+                temp_thr = np.squeeze(thr[:, not_idx])
+                temp_rad = rad11_4[not_idx]
+                thr[0:3, idx_lake[0], idx_lake[1]] = thr[0:3, idx_lake[0], idx_lake[1]] - 1
+                thr[3:6, idx_lake[0], idx_lake[1]] = thr[3:6, idx_lake[0], idx_lake[1]] + 1
+
+                confidence[not_idx] = conf.conf_test_dble(rad11_4[not_idx], thr[:, not_idx])
+
+            if self.scene_name in ['Polar_Night_Land', 'Polar_Day_Snow', 'Day_Snow']:
+                rad11_12 = self.data[band1].values[self.scene_idx]
+                rad11_4 = self.data[band2].values[self.scene_idx]
+                thr = preproc.polar_night_thresholds(self.data, threshold, self.scene_name,
+                                                     test_name, self.scene_idx)
+                confidence[self.scene_idx] = conf.conf_test_new(rad11_4, thr)
+
+            if self.scene_name in ['Polar_Night_Snow']:
+                rad11_12 = self.data[band1].values
+                rad11_4 = self.data[band2].values
+                idx_top = np.nonzero(self.data.M15.values > threshold['bt_thr'])
+                thr_top = preproc.polar_night_thresholds(self.data, threshold['top'], self.scene_name,
+                                                         test_name, idx_top)
+                idx_bottom = np.nonzero((self.data.M15.values > threshold['bt_thr']) &
+                                        (self.data.geos_tpw > threshold['tpw_thr']))
+                thr_bottom = preproc.polar_night_thresholds(self.data, threshold['bottom'], self.scene_name,
+                                                            test_name, idx_top)
+
+                confidence[idx_top] = conf.conf_test_new(rad11_4[idx_top], thr_top)
+                confidence[idx_bottom] = conf.conf_test_new(rad11_4[idx_bottom], thr_bottom)
+
+            if self.scene_name in ['Night_Snow']:
+                rad11_12 = self.data[band1].values
+                rad11_4 = self.data[band2].values
+                idx_top = np.nonzero(self.data.M15.values > threshold['bt_thr'])
+                thr_top = threshold['thr']
+                idx_bottom = np.nonzero((self.data.M15.values > threshold['bt_thr']) &
+                                        (self.data.geos_tpw > threshold['tpw_thr']))
+                thr_bottom = preproc.polar_night_thresholds(self.data, threshold['bottom'], self.scene_name,
+                                                            test_name, idx_top)
+
+                confidence[idx_top] = conf.conf_test_new(rad11_4[idx_top], thr_top)
+                confidence[idx_bottom] = conf.conf_test_new(rad11_4[idx_bottom], thr_bottom)
+
+            cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    # This test requires 7.3um band that is not available for VIIRS
+    @run_if_test_exists_for_scene
+    def midlevel_cloud_test():
+        pass
+
+    # This test requires 7.3um band that is not available for VIIRS
+    @run_if_test_exists_for_scene
+    def water_vapor_cloud_test():
+        pass
+
+    @run_if_test_exists_for_scene
+    def variability_11um_test(self,
+                              band: str,
+                              cmin: np.ndarray,
+                              test_name: str = '11um_Variability_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M15.shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+        var = np.zeros([self.data.M15.shape[0], self.data.M15.shape[1], 9])
+        radshape = self.data.M15.shape
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values
+
+            test = (sliding_window_view(np.pad(rad, [1, 1], mode='constant'), (3, 3)) -
+                    np.expand_dims(rad, (2, 3)))
+            var[np.abs(test).reshape(radshape[0], radshape[1], 9) < threshold['variability']] = 1
+            var = var.sum(axis=2)
+
+            idx = np.where(var == 9)
+            test_bit[idx] = 1
+            thr = np.array(threshold['thr'])
+            confidence[idx] = conf.conf_test_new(rad[idx], thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def oceanic_stratus_11_4um_test(self,
+                                    band: str,
+                                    cmin: np.ndarray,
+                                    test_name: str = '11-4um_Oceanic_Stratus_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M15.shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            thr = threshold['thr']
+            if self.scene_name in ['Land_Day_Desert', 'Land_Day_Desert_Coast', 'Polar_Day_Desert',
+                                   'Polar_Day_Desert_Coast']:
+                confidence[self.scene_idx] = conf.conf_test_dble(rad, thr)
+
+            # these scenes have not been implemented yet
+            # elif self.scene_name in ['Land_Night', 'Polar_Night_Land', 'Polar_Day_Snow', 'Polar_Night_Snow',
+            #                          'Day_Snow', 'Night_Snow', 'Antarctic_Day']:
+            #     pass
+            else:
+                scene_flag = scn.find_scene(self.data, self.thresholds['Sun_Glint']['bounds'][3])
+                idx = np.nonzero((self.data[band].values >= threshold['thr'][1]) &
+                                 (self.data[self.scene_name] == 1) &
+                                 (scene_flag['sunglint'] == 0))
+                test_bit[idx] = 1
+                idx = np.nonzero((self.data[self.scene_name] == 1) &
+                                 (scene_flag['sunglint'] == 0))
+                confidence[idx] = conf.conf_test_new(self.data[band].values[idx], thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def nir_reflectance_test(self,
+                             band: str,
+                             cmin: np.ndarray,
+                             test_name: str = 'NIR_Reflectance_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            thr = preproc.thresholds_NIR(self.data, self.thresholds, self.scene_name,
+                                         test_name, self.scene_idx)
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            idx = np.nonzero((rad <= thr[1, :]) &
+                             (self.data[self.scene_name].values[self.scene_idx] == 1))
+            tmp_bit = test_bit[self.scene_idx]
+            tmp_bit[idx] = 1
+            test_bit[self.scene_idx] = tmp_bit
+            confidence[self.scene_idx] = conf.conf_test_new(rad, thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, np.abs(1-test_bit)*qa_bit
+
+    @run_if_test_exists_for_scene
+    def vis_nir_ratio_test(self,
+                           band: str,
+                           cmin: np.ndarray,
+                           test_name: str = 'Vis/NIR_Reflectance_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            # I'm not using the self.scene_idx here because it messes up with the indexing of the
+            # confidence array and I don't want to think of a solution at the moment. I will need
+            # to figure out the logic to make it work once I'm at the stage where I want to optimize
+            # the code
+            qa_bit[self.scene_idx] = 1
+            rad = self.data[band].values  # [self.scene_idx]
+            solar_zenith = self.data.solar_zenith.values  # [self.scene_idx]
+            sunglint = scn.find_scene(self.data, self.thresholds['Sun_Glint']['bounds'][3])['sunglint']
+
+            idx = np.nonzero((solar_zenith <= 85) & (sunglint == 1))
+
+            thr_no_sunglint = np.array([threshold['thr'][i] for i in range(8)])
+            thr_sunglint = np.array([self.thresholds['Sun_Glint']['snglnt'][i] for i in range(8)])
+            print(thr_no_sunglint)
+            print(thr_sunglint)
+            # tmp = self.thresholds['Sun_Glint']['snglnt']
+            # thr_sunglint = np.array([tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], 1])
+            confidence[sunglint == 0] = conf.conf_test_dble(rad[sunglint == 0], thr_no_sunglint)
+            confidence[idx] = conf.conf_test_dble(rad[idx], thr_sunglint)
+
+            idx = np.nonzero(((rad < thr_no_sunglint[1]) | (rad > thr_no_sunglint[4])) &
+                             (self.data[self.scene_name].values == 1) & (sunglint == 0))
+            test_bit[tuple(idx)] = 1
+
+            idx = np.nonzero(((rad < thr_sunglint[1]) | (rad > thr_sunglint[4])) &
+                             ((self.data[self.scene_name].values == 1) &
+                              (solar_zenith <= 85) & (sunglint == 1)))
+            test_bit[tuple(idx)] = 1
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, np.abs(1-test_bit)*qa_bit
+
+    @run_if_test_exists_for_scene
+    def test_16_21um_Reflectance(self,
+                                 band: str,
+                                 cmin: np.ndarray,
+                                 test_name: str = '1.6_2.1um_NIR_Reflectance_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            thr = preproc.thresholds_NIR(self.data, self.thresholds, self.scene_name,
+                                         test_name, self.scene_idx)
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            idx = np.nonzero((rad <= thr[1, :]) &
+                             (self.data[self.scene_name].values[self.scene_idx] == 1))
+            tmp_bit = test_bit[self.scene_idx]
+            tmp_bit[idx] = 1
+            test_bit[self.scene_idx] = tmp_bit
+            confidence[self.scene_idx] = conf.conf_test_new(rad, thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, np.abs(1-test_bit)*qa_bit
+
+    @run_if_test_exists_for_scene
+    def visible_reflectance_test(self,
+                                 band: str,
+                                 cmin: np.ndarray,
+                                 test_name: str = 'Visible_Reflectance_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            print(f'Testing "{self.scene_name}"\n')
+            thr, rad = preproc.vis_refl_thresholds(self.data, self.thresholds, self.scene_name,
+                                                   self.scene_idx)
+            confidence[self.scene_idx] = conf.conf_test_new(rad, thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def gemi_test(self,
+                  band: str,
+                  cmin: np.ndarray,
+                  test_name: str = 'GEMI_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            thr = preproc.gemi_thresholds(self.data, threshold, self.scene_name,
+                                          self.scene_idx)
+            rad = self.data[band].values[self.scene_idx]
+            confidence[self.scene_idx] = conf.conf_test_new(rad, thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    @run_if_test_exists_for_scene
+    def test_1_38um_high_clouds(self,
+                                band: str,
+                                cmin: np.ndarray,
+                                test_name: str = '1.38um_High_Cloud_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        qa_bit = np.zeros(self.data[band].shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            qa_bit[self.scene_idx] = 1
+            if self.scene_name in ['Ocean_Day', 'Polar_Day_Ocean']:
+                thr = preproc.thresholds_1_38um_test(self.data, self.thresholds, self.scene_name,
+                                                     self.scene_idx)
+            else:
+                return cmin, test_bit
+                thr = threshold['thr']
+
+            print(f'Testing "{self.scene_name}"\n')
+            rad = self.data[band].values[self.scene_idx]
+            idx = np.nonzero((rad <= thr[1, :]) &
+                             (self.data[self.scene_name].values[self.scene_idx] == 1))
+            tmp_bit = test_bit[self.scene_idx]
+            tmp_bit[idx] = 1
+            test_bit[self.scene_idx] = tmp_bit
+            confidence[self.scene_idx] = conf.conf_test_new(rad, thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, np.abs(1-test_bit)*qa_bit
+
+    @run_if_test_exists_for_scene
+    def thin_cirrus_4_12um_BTD_test(self,
+                                    band: str,
+                                    cmin: np.ndarray,
+                                    test_name: str = '4-12um_BTD_Thin_Cirrus_Test') -> np.ndarray:
+
+        confidence = np.ones(self.data.M01.shape)
+        test_bit = np.zeros(self.data[band].shape)
+        threshold = self.thresholds[self.scene_name][test_name]
+
+        if (threshold['perform'] is True and self.pixels_in_scene is True):
+            thr = preproc.polar_night_thresholds(self.data, self.thresholds, self.scene_name,
+                                                 test_name, self.scene_idx)
+            rad = self.data[band].values[self.scene_idx]
+            confidence[self.scene_idx] = conf.conf_test_new(rad, thr)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin, test_bit
+
+    def single_threshold_test(self, test_name, band, cmin):
+
+        if band == 'bad_data':
+            return cmin
+        print(f'Running test "{test_name}" for "{self.scene_name}"')
+
+        # preproc_thresholds()
+        if 'thr' in self.thresholds[self.scene_name][test_name]:
+            thr = np.array(self.thresholds[self.scene_name][test_name]['thr'])
+        else:
+            thr = np.array(self.thresholds[self.scene_name][test_name])
+        thr_xr = xr.Dataset()
+
+        if test_name == '11-12um_Cirrus_Test':
+            thr_xr['threshold'] = pt.preproc(self.data, self.thresholds[self.scene_name], self.scene_name)
+            thr = np.ones((5,))  # This is only temporary to force the logic of the code
+                                 # I need to find a better solution at some point
+        elif test_name == 'SST_Test':
+            thr_xr['threshold'] = (('number_of_lines', 'number_of_pixels', 'z'),
+                                   np.ones((self.data[band].shape[0], self.data[band].shape[1], 5))*thr)
+        elif test_name == '7.3-11um_BTD_Mid_Level_Cloud_Test':
+            thr_xr['threshold'] = pt.get_pn_thresholds(self.data, self.thresholds, self.scene_name,
+                                                       '7.3-11um_BTD_Mid_Level_Cloud_Test')
+            thr = np.ones((5,))
+        elif test_name == 'Surface_Temperature_Test':
+            thr_xr['threshold'] = pt.preproc_surf_temp(self.data, self.thresholds[self.scene_name])
+            thr = np.ones((5,))
+        elif (test_name == '11-4um_Oceanic_Stratus_Test' and
+              self.scene_name in ['Land_Day_Desert', 'Land_Day_Desert_Coast', 'Polar_Day_Desert',
+                                  'Polar_Day_Desert_Coast']):
+            thr = np.array([self.thresholds[self.scene_name][test_name][i] for i in range(8)])
+        elif test_name == 'NIR_Reflectance_Test':
+            corr_thr = pt.preproc_nir(self.data, self.thresholds, self.scene_name)
+            thr_xr['threshold'] = (('number_of_lines', 'number_of_pixels', 'z'), corr_thr)
+        elif test_name == 'Visible_Reflectance_Test':
+            thr_xr['threshold'], self.data['M128'] = pt.vis_refl_thresholds(self.data,
+                                                                            self.thresholds,
+                                                                            self.scene_name)
+        elif test_name == '1.6_2.1um_NIR_Reflectance_Test':
+            corr_thr = pt.nir_refl(self.data, self.thresholds, self.scene_name)
+            thr_xr['threshold'] = (('number_of_lines', 'number_of_pixels', 'z'), corr_thr)
+            thr = np.ones((5,))
+        elif test_name == '4-12um_BTD_Thin_Cirrus_Test':
+            thr_xr['threshold'] = pt.get_pn_thresholds(self.data, self.thresholds, self.scene_name,
+                                                       '4-12um_BTD_Thin_Cirrus_Test')
+            thr = np.ones((5,))
+        elif test_name == 'GEMI_Test':
+            thr_xr['threshold'] = pt.GEMI_test(self.data, self.thresholds, self.scene_name)
+            thr = np.ones((5,))
+        elif (test_name == '1.38um_High_Cloud_Test' and self.scene_name in ['Ocean_Day', 'Polar_Ocean_Day']):
+            thr_xr['threshold'] = pt.test_1_38um_preproc(self.data, self.thresholds, self.scene_name)
+            thr = np.ones((5,))
+        else:
+            thr_xr['threshold'] = (('number_of_lines', 'number_of_pixels', 'z'),
+                                   np.ones((self.data[band].shape[0], self.data[band].shape[1], 5))*thr)
+
+        data = xr.Dataset(self.data, coords=thr_xr)
+
+        if test_name == 'SST_Test':
+            data['sfcdif'] = (('number_of_lines', 'number_of_pixels'),
+                              pt.preproc_sst(data, self.thresholds[self.scene_name][test_name]).values)
+            band = 'sfcdif'
+        if test_name == '11um_Variability_Test':
+            var = pt.var_11um(self.data, self.thresholds)
+            data['11um_var'] = data.M15
+            data['11um_var'].values[var != 9] = np.nan
+
+        if thr[4] == 1:
+            print('test running...')
+            confidence = conf_xr.conf_test(data, band)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin
+
+    def double_threshold_test(self, test_name, band, cmin):
+        data = self.data
+        if test_name == '11-4um_BT_Difference_Test':
+            thr = pt.bt11_4um_preproc(self.data, self.thresholds, self.scene_name)
+            print('test running...')
+            confidence = conf.conf_test_dble(data['M15-M13'].values, thr)
+            confidence = confidence.reshape(data.M01.shape)
+
+        if test_name == 'Vis/NIR_Ratio_Test':
+            print('test running...')
+            thr_no_sunglint = np.array([self.thresholds[self.scene_name][test_name][i] for i in range(8)])
+            thr_sunglint = np.array([self.thresholds['Sun_Glint']['snglnt'][i] for i in range(8)])
+            vrat = data.M07.values/data.M05.values
+            _dtr = np.pi/180.0
+            sza = data.sensor_zenith.values
+            raz = data.relative_azimuth.values
+            vza = data.sensor_zenith.values
+            cos_refang = np.sin(vza*_dtr) * np.sin(sza*_dtr) * np.cos(raz*_dtr) + \
+                np.cos(vza*_dtr) * np.cos(sza*_dtr)
+            refang = np.arccos(cos_refang) * 180./np.pi
+            idx = np.nonzero((data.solar_zenith <= 85) & (refang <= data.sunglint_angle))
+
+            confidence = conf.conf_test_dble(vrat, thr_no_sunglint)
+            confidence = confidence.reshape(data.M01.shape)
+            confidence[idx] = conf.conf_test_dble(vrat[idx], thr_sunglint)
+
+            confidence = confidence.reshape(data.M01.shape)
+
+        if (test_name == '11-4um_Oceanic_Stratus_Test' and
+            self.scene_name in ['Land_Day_Desert', 'Land_Day_Desert_Coast', 'Polar_Day_Desert',
+                                'Polar_Day_Desert_Coast']):
+            thr = np.array([self.thresholds[self.scene_name][test_name][i] for i in range(8)])
+
+            print('test running...')
+            confidence = conf.conf_test_dble(data['M15-M16'].values, thr)
+            confidence = confidence.reshape(data.M01.shape)
+
+        cmin = np.fmin(cmin, confidence)
+
+        return cmin
+
+
+class ComputeTests(CloudTests):
+
+    def __init__(self,
+                 data: xr.Dataset,
+                 scene_name: str,
+                 thresholds: Dict) -> None:
+        super().__init__(data, scene_name, thresholds)
+
+    def run_tests(self):
+        cmin_G1 = np.ones(self.data.M01.shape)
+        cmin_G2 = np.ones(self.data.M01.shape)
+        cmin_G3 = np.ones(self.data.M01.shape)
+        cmin_G4 = np.ones(self.data.M01.shape)
+        cmin_G5 = np.ones(self.data.M01.shape)
+
+        # Group 1
+        cmin_G1 = self.test_11um('M15', cmin_G1, test_name='11um_Test')
+        cmin_G1 = self.sst_test('M15', 'M16', cmin_G1, test_name='SST_Test')
+
+        # Group 2
+        cmin_G2 = self.bt_diff_86_11um('M14-M15', cmin_G2, test_name='8.6-11um_Test')
+        cmin_G2 = self.test_11_12um_diff('M15-M16', cmin_G2, test_name='11-12um_Cirrus_Test')
+        cmin_G2 = self.oceanic_stratus_11_4um_test('M15-M13', cmin_G2,
+                                                   test_name='11-4um_Oceanic_Stratus_Test')
+
+        # Group 3
+        cmin_G3 = self.nir_reflectance_test('M07', cmin_G3, test_name='NIR_Reflectance_Test')
+        cmin_G3 = self.vis_nir_ratio_test('M07-M05ratio', cmin_G3, test_name='Vis/NIR_Ratio_Test')
+        cmin_G3 = self.nir_reflectance_test('M10', cmin_G3, test_name='1.6_2.1um_NIR_Reflectance_Test')
+        cmin_G3 = self.visible_reflectance_test('M128', cmin_G3, test_name='Visible_Reflectance_Test')
+        cmin_G3 = self.gemi_test('GEMI', cmin_G3, test_name='GEMI_Test')
+
+        # Group 4
+        cmin_G4 = self.test_1_38um_high_clouds('M09', cmin_G4, test_name='1.38um_High_Cloud_Test')
+
+        # Group 5
+        cmin_G5 = self.thin_cirrus_4_12um_BTD_test('M13-M16', cmin_G5,
+                                                   test_name='4-12um_BTD_Thin_Cirrus_Test')
+
+        cmin = cmin_G1 * cmin_G2 * cmin_G3 * cmin_G4 * cmin_G5
+
+        return cmin
+
+    def clear_sky_restoral(self,
+                           cmin: np.ndarray) -> np.ndarray:
+
+        total_bit = np.full(self.data.M01.shape, 3)
+        sunglint_angle = self.thresholds['Sun_Glint']['bounds'][3]
+        scene_flags = scn.find_scene(self.data, sunglint_angle)
+
+        cmin_tmp = cmin + 0
+        idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['ice'] == 0) &
+                         (scene_flags['uniform'] == 1) & (cmin <= 0.99) & (cmin >= 0.05))
+        cmin[idx] = restoral.spatial(self.data, self.thresholds['Sun_Glint'], scene_flags, cmin_tmp)[idx]
+
+        cmin_tmp = cmin + 0
+        idx = np.nonzero((scene_flags['water'] == 1) & (scene_flags['sunglint'] == 1) &
+                         (scene_flags['uniform'] == 1) & (cmin <= 0.95))
+        cmin[idx] = restoral.sunglint(self.data, self.thresholds['Sun_Glint'], total_bit, cmin_tmp)[idx]
+
+        """
+        idx = np.nonzero((scene_flags['day'] == 1) & (scene_flags['land'] == 1) &
+                         (scene_flags['snow'] == 0) & (scene_flags['ice'] == 0) &
+                         (cmin <= 0.95))
+        cmin[idx] = restoral.land(self.data, self.thresholds, scene_flags, cmin)[idx]
+
+        idx = np.nonzero((scene_flags['day'] == 1) & (scene_flags['land'] == 1) &
+                         (scene_flags['coast'] == 1) & (scene_flags['snow'] == 0) &
+                         (scene_flags['ice'] == 0))
+        cmin[idx] = restoral.coast(self.data, self.thresholds, scene_flags, cmin)[idx]
+        """
+
+        return cmin
+
+
+def preproc_thresholds(thresholds, data):
+    thr = np.array(thresholds)
+    thr_xr = xr.Dataset()
+    thr_xr['tresholds'] = (('number_of_lines', 'number_of_pixels', 'z'),
+                           np.ones((data['M01'].shape[0], data['M01'].shape[1], 5))*thr)
+
+    nl_sfct1 = thresholds['Land_Night']['Surface_Temperature_Test'][0]
+#    nl_sfct2 = thresholds['Land_Night']['Surface_Temperature_Test'][1]
+#    nlsfct_pfm = thresholds['Land_Night']['Surface_Temperature_Test'][2]
+    nl_df1 = thresholds['Land_Night']['Surface_Temperature_Test_difference'][0:2]
+    nl_df2 = thresholds['Land_Night']['Surface_Temperature_Test_difference'][2:]
+
+#    df1 = data.M15 - data.M16
+#    df2 = data.M15 - data.M13
+    thr_xr = thr_xr.where(data.desert != 1, nl_sfct1)
+    thr_xr = thr_xr.where((data['M15-M16'] > nl_df1[0]) |
+                          ((data['M15-M16'] < nl_df1[0]) &
+                           ((data['M15-M13'] <= nl_df2[0]) | (data['M15-M13'] >= nl_df2[1]))),
+                          nl_sfct1[0])
+
+    data = xr.Dataset(data, coords=thr_xr)
+
+    return data
+
+
+def single_threshold_test(test, rad, threshold):
+
+    radshape = rad.shape
+    rad = rad.reshape(np.prod(radshape))
+
+    thr = np.array(threshold[test])
+    confidence = np.zeros(rad.shape)
+
+    if thr[4] == 1:
+        print(f"{test} test running")
+        # the C code has the line below that I don't quite understand the purpose of.
+        # It seems to be setting the bit to 0 if the BT value is greater than the midpoint
+        #
+        # if (m31 >= dobt11[1]) (void) set_bit(13, pxout.testbits);
+
+        # confidence = utils.conf_test(rad, thr)
+        confidence = conf.conf_test(rad, thr)
+
+    return confidence.reshape(radshape)
+
+
+def test():
+    rad = np.random.randint(50, size=[4, 8])
+    # coeffs = [5, 42, 20, 28, 15, 35, 1]
+    # coeffs = [20, 28, 5, 42, 15, 35, 1]
+    coeffs = [35, 15, 20, 1, 1]
+    # confidence = conf_test_dble(rad, coeffs)
+    confidence = test_11um(rad, coeffs)
+    print(rad)
+    print('\n')
+    print(confidence)
+
+
+if __name__ == "__main__":
+    test()
+
+
+
+
+
+
diff --git a/mvcm/utils.py b/mvcm/utils.py
new file mode 100644
index 0000000..6b45056
--- /dev/null
+++ b/mvcm/utils.py
@@ -0,0 +1,329 @@
+import numpy as np
+
+_test_rad = np.random.randint(25, size=[6, 8])
+# _test_thr = [15, 10, 5, 1, 1]
+_test_thr = [5, 10, 15, 1, 1]
+
+
+# this function creates a map of sunglint areas, based on the different angles set in the
+# threshold file. The goal is to create an array of indices that I can use to quickly assign
+# different coefficients depending on the angle interval. This will be mostly used in the
+# function get_sunglint_thresholds().
+# All of this is because we want to be able to process the whole array, instead of iterating
+# over all pixels one by one.
+def sunglint_scene(refang, sunglint_thr):
+    sunglint_flag = np.zeros(refang.shape)
+    sunglint_flag[(refang > sunglint_thr['bounds'][2]) & (refang <= sunglint_thr['bounds'][3])] = 1
+    sunglint_flag[(refang > sunglint_thr['bounds'][1]) & (refang <= sunglint_thr['bounds'][2])] = 2
+    sunglint_flag[refang <= sunglint_thr['bounds'][1]] = 3
+    return sunglint_flag
+
+
+def get_sunglint_thresholds(refang, thresholds, band_n, sunglint_flag, thr):
+
+    band = f'band{band_n}'
+    sunglint_thr = np.zeros((4, len(refang)))
+#    if refang > thresholds['bounds'][3]:
+#       sunglint = sunglint
+#        # dosgref[2] = hicnf
+#        # dosgref[0] = locnf
+#        # dosgref[1] = mdcnf
+#        # sunglint[3] = doref2[3]
+
+    # if refang <= thresholds['bounds'][1]:
+    if sunglint_flag == 3:
+        sunglint_thr = np.full((len(refang), 4), thresholds[f'{band}_0deg']).T
+        # sunglint_thr[:, :] = thresholds[f'{band}_0deg']
+
+    else:
+
+        # if (refang > thresholds['bounds'][1] and refang <= thresholds['bounds'][2]):
+        if sunglint_flag == 2:
+            lo_ang = thresholds['bounds'][1]
+            hi_ang = thresholds['bounds'][2]
+            lo_ang_val = thresholds[f'{band}_10deg'][0]
+            hi_ang_val = thresholds[f'{band}_10deg'][1]
+            power = thresholds[f'{band}_10deg'][3]
+            conf_range = thresholds[f'{band}_10deg'][2]
+
+        # elif (refang > thresholds['bounds'][2] and refang <= thresholds['bounds'][3]):
+        elif sunglint_flag == 1:
+            lo_ang = thresholds['bounds'][2]
+            hi_ang = thresholds['bounds'][3]
+            lo_ang_val = thresholds[f'{band}_20deg'][0]
+            hi_ang_val = thr[1]
+            power = thresholds[f'{band}_20deg'][3]
+            conf_range = thresholds[f'{band}_20deg'][2]
+        else:
+            raise ValueError("Wrong sunglint flag")
+
+        a = (refang - lo_ang) / (hi_ang - lo_ang)
+        midpt = lo_ang_val + a*(hi_ang_val - lo_ang_val)
+        sunglint_thr[1, :] = midpt
+        sunglint_thr[2, :] = midpt - conf_range
+        sunglint_thr[0, :] = midpt + conf_range
+        sunglint_thr[3, :] = power
+
+    return sunglint_thr
+
+
+def conf_test(rad=_test_rad, thr=_test_thr):
+    '''
+    Assuming a linear function between min and max confidence level, the plot below shows
+    how the confidence (y axis) is computed as function of radiance (x axis).
+    This case illustrates alpha < gamma, obviously in case alpha > gamma, the plot would be
+    flipped.
+                       gamma
+    c  1                 ________
+    o  |                /
+    n  |               /
+    f  |              /
+    i  |     beta    /
+    d 1/2    |....../
+    e  |           /
+    n  |          /
+    c  |         /
+    e  0________/
+       |      alpha
+    --------- radiance ---------->
+    '''
+
+    coeff = np.power(2, (thr[3] - 1))
+    hicut = thr[0]
+    beta = thr[1]
+    locut = thr[2]
+    power = thr[3]
+    confidence = np.zeros(rad.shape)
+
+    if hicut > locut:
+        gamma = thr[0]
+        alpha = thr[2]
+        flipped = False
+    else:
+        gamma = thr[2]
+        alpha = thr[0]
+        flipped = True
+
+    # Rad between alpha and beta
+    range_ = 2. * (beta - alpha)
+    s1 = (rad[rad <= beta] - alpha) / range_
+    if flipped is False:
+        confidence[rad <= beta] = coeff * np.power(s1, power)
+    if flipped is True:
+        confidence[rad <= beta] = 1. - (coeff * np.power(s1, power))
+
+    # Rad between beta and gamma
+    range_ = 2. * (beta - gamma)
+    s1 = (rad[rad > beta] - gamma) / range_
+    if flipped is False:
+        confidence[rad > beta] = 1. - (coeff * np.power(s1, power))
+    if flipped is True:
+        confidence[rad > beta] = coeff * np.power(s1, power)
+
+    # Rad outside alpha-gamma interval
+    if flipped is False:
+        confidence[rad > gamma] = 1
+        confidence[rad < alpha] = 0
+    if flipped is True:
+        confidence[rad > gamma] = 0
+        confidence[rad < alpha] = 1
+
+    confidence[confidence > 1] = 1
+    confidence[confidence < 0] = 0
+
+    return confidence
+
+
+def conf_test_dble(rad, coeffs):
+    # '''
+    #            gamma1                         gamma2
+    #    c  1_______                               ________
+    #    o  |       \                             /
+    #    n  |        \                           /
+    #    f  |         \                         /
+    #    i  |          \   beta1       beta2   /
+    #    d 1/2          \....|          |...../
+    #    e  |            \                   /
+    #    n  |             \                 /
+    #    c  |              \               /
+    #    e  0               \_____________/
+    #       |             alpha1       alpha2
+    #    --------------------- radiance ------------------------->
+    # '''
+
+    hicut = [coeffs[0], coeffs[1]]
+    locut = [coeffs[2], coeffs[3]]
+    midpt = [coeffs[4], coeffs[5]]
+    power = coeffs[6]
+
+    gamma1 = hicut[0]
+    gamma2 = hicut[1]
+    alpha1 = locut[0]
+    alpha2 = locut[1]
+    beta1 = midpt[0]
+    beta2 = midpt[1]
+
+    coeff = np.power(2, (power - 1))
+    radshape = rad.shape
+    rad = rad.reshape((rad.shape[0]*rad.shape[1]))
+    c = np.zeros(rad.shape)
+
+    # Find if interval between inner cutoffs passes or fails test
+    if (alpha1 - gamma1 > 0):
+
+        # Value is within range of lower set of limits
+        range_ = 2 * (beta1 - alpha1)
+        s1 = (rad[(rad <= alpha1) & (rad >= beta1)] - alpha1) / range_
+        c[(rad <= alpha1) & (rad >= beta1)] = coeff * np.power(s1, power)
+
+        range_ = 2 * (beta1 - gamma1)
+        s1 = (rad[(rad <= alpha1) & (rad < beta1)] - gamma1) / range_
+        c[(rad <= alpha1) & (rad < beta1)] = coeff * np.power(s1, power)
+
+        # Value is within range of upper set of limits
+        range_ = 2 * (beta2 - alpha2)
+        s1 = (rad[(rad > alpha1) & (rad <= beta2)] - alpha2) / range_
+        c[(rad > alpha1) & (rad <= beta2)] = coeff * np.power(s1, power)
+
+        range_ = 2 * (beta2 - gamma2)
+        s1 = (rad[(rad > alpha1) & (rad > beta2)] - gamma2) / range_
+        c[(rad > alpha1) & (rad > beta2)] = coeff * np.power(s1, power)
+
+        # Inner region fails test
+        # Check for value beyond function range
+        c[(rad > alpha1) & (rad < alpha2)] = 0
+        c[(rad < gamma1) | (rad > gamma2)] = 1
+
+    else:
+
+        # Value is withing range of lower set of limits
+        range_ = 2 * (beta1 - alpha1)
+        s1 = (rad[(rad <= gamma1) & (rad <= beta1)] - alpha1) / range_
+        c[(rad <= gamma1) & (rad <= beta1)] = coeff * np.power(s1, power)
+
+        range_ = 2 * (beta1 - gamma1)
+        s1 = (rad[(rad <= gamma1) & (rad > beta1)] - gamma1) / range_
+        c[(rad <= gamma1) & (rad > beta1)] = coeff * np.power(s1, power)
+
+        # Value is within range of upper set of limits
+        range_ = 2 * (beta2 - alpha2)
+        s1 = (rad[(rad > gamma1) & (rad >= beta2)] - alpha2) / range_
+        c[(rad > gamma1) & (rad >= beta2)] = coeff * np.power(s1, power)
+
+        range_ = 2 * (beta2 - gamma2)
+        s1 = (rad[(rad > gamma1) & (rad < beta2)] - gamma2) / range_
+        c[(rad > gamma1) & (rad < beta2)] = coeff * np.power(s1, power)
+
+        # Inner region passes test
+        # Check for value beyond function range
+        c[(rad > gamma1) & (rad < gamma2)] = 1
+        c[(rad < alpha1) | (rad > alpha2)] = 0
+
+    c[c > 1] = 1
+    c[c < 0] = 0
+
+    confidence = c.reshape(radshape)
+
+    return confidence
+
+
+def get_b1_threshold(thresholds, ndvi_thresholds):
+    # this needs to be read from the thresholds file
+    des_ndvi = 0.25
+    pxin_ndvibk = 0  # placeholder until I figure out how to pass this value
+    pxin_sctang = 0  # placeholder until I figure out how to pass this value
+    delta_ndvi_bin = 0.1  # defined as is in the original C code
+    thr = np.zeros((4, len(pxin_ndvibk)))
+    thr_adj = np.zeros((4, len(pxin_ndvibk)))
+
+    coeffs = np.full((10, 3, 4, len(pxin_ndvibk)))
+
+    indvi = np.zeros((len(pxin_ndvibk), ))
+    x = np.zeros((len(pxin_ndvibk), ))
+    y1 = np.zeros((len(pxin_ndvibk), ))
+    y2 = np.zeros((len(pxin_ndvibk), ))
+    interp = np.zeros((len(pxin_ndvibk), ))
+
+    # Check for special cases and fill values ('ndvibk'=32.767)
+    # idx = np.nonzero((pxin_ndvibk < ndvi_thresholds['fill_ndvi'][0]) &
+    #                  (pxin_ndvibk > ndvi_thresholds['fill_ndvi'][1]) &
+    #                  (pxin_ndvibk < ndvi_thresholds['ndvi_bnd1']))
+    idx = np.nonzero(pxin_ndvibk < ndvi_thresholds['ndvi_bnd1'])
+    # all gets set to zero, so no need to do anything for this idx
+
+    # idx = np.nonzero((pxin_ndvibk < ndvi_thresholds['fill_ndvi'][0]) &
+    #                  (pxin_ndvibk > ndvi_thresholds['fill_ndvi'][1]) &
+    #                  (pxin_ndvibk >= ndvi_thresholds['ndvi_bnd2']))
+    idx = np.nonzero(pxin_ndvibk >= ndvi_thresholds['ndvi_bnd2'])
+    indvi[idx] = 9
+
+    # else
+    # idx = np.nonzero((pxin_ndvibk < ndvi_thresholds['fill_ndvi'][0]) &
+    #                  (pxin_ndvibk > ndvi_thresholds['fill_ndvi'][1]) &
+    #                  (pxin_ndvibk >= ndvi_thresholds['ndvi_bnd1']) &
+    #                  (pxin_ndvibk < ndvi_thresholds['ndvi_bnd2']))
+    idx = np.nonzero((pxin_ndvibk >= ndvi_thresholds['ndvi_bnd1']) &
+                     (pxin_ndvibk < ndvi_thresholds['ndvi_bnd2']))
+    interp[idx] = 1  # this is not really needed anymore, maybe
+    indvi = (pxin_ndvibk[idx] / delta_ndvi_bin) - 0.5
+    indvi[indvi < 0] = 0
+    x = (pxin_ndvibk - delta_ndvi_bin*indvi + delta_ndvi_bin/2) / delta_ndvi_bin
+    x[x < 0] = 0
+    x[x > 1] = 1
+
+    for i in range(3):
+        # idx = np.nonzero((pxin_ndvibk < ndvi_thresholds['fill_ndvi'][0]) &
+        #                  (pxin_ndvibk > ndvi_thresholds['fill_ndvi'][1]))
+
+        y1[:, i] = (coeffs[indvi, i, 0] +
+                    coeffs[indvi, i, 1]*pxin_sctang +
+                    coeffs[indvi, i, 2]*pxin_sctang**2 +
+                    coeffs[indvi, i, 3]*pxin_sctang**3)
+
+        # idx = np.nonzero((pxin_ndvibk < ndvi_thresholds['fill_ndvi'][0]) &
+        #                  (pxin_ndvibk > ndvi_thresholds['fill_ndvi'][1]) & interp == 1)
+        y2[idx, i] = (coeffs[indvi[idx]+1, i, 0] +
+                      coeffs[indvi[idx]+1, i, 1]*pxin_sctang[idx] +
+                      coeffs[indvi[idx]+1, i, 2]*pxin_sctang[idx]**2 +
+                      coeffs[indvi[idx]+1, i, 3]*pxin_sctang[idx]**3)
+
+        thr[:, i] = ((1.0 - x) * y1) + (x * y2)
+
+        thr_adj[pxin_ndvibk < des_ndvi, i] = thr[pxin_ndvibk < des_ndvi, 0]*ndvi_thresholds['adj_fac_desert']
+        thr_adj[pxin_ndvibk >= des_ndvi, i] = thr[pxin_ndvibk >= des_ndvi, 0]*ndvi_thresholds['adj_fac_land']
+
+    thr[:, 3] = 2.0
+
+    return thr
+
+
+def find_scene(data):
+    scene = {'ocean_day': 0,
+             'ocean_night': 0,
+             'land_day': 0,
+             'land_night': 0,
+             'snow_day': 0,
+             'coast_day': 0,
+             'desert_day': 0,
+             'antarctic_day': 0,
+             'polar_day_snow': 0,
+             'polar_day_desert': 0,
+             'polar_day_desert_coast': 0,
+             'polar_day_coast': 0,
+             'polar_day_land': 0,
+             'polar_night_snow': 0,
+             'polar_night_land': 0,
+             'polar_ocean_night': 0,
+             }
+
+    return scene
+
+
+def snow_mask(viirs_data):
+    # (I1 - I3) / (I1 + I3)
+    # ndsi = (b04 - b10) / (b04 + b10)
+    # ndvi = (b07 - b05) / (b05 + b07)
+    # pred_ndvi = prd_ndvi_const[0] + 3.0*ndsi
+
+    pass
+
diff --git a/pyproject.toml b/pyproject.toml
index 1884af7..b4c84ce 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,24 +1,48 @@
-[build-system]
-requires = ['setuptools>=61.0', 'wheel', 'Cython', 'numpy', 'xarray']
-build-backend = 'setuptools.build_meta'
-
 [project]
 name = 'MVCM'
-version = '0.0.3'
-authors = [{name='Paolo Veglio', email='paolo.veglio@ssec.wisc.edu'}]
-description = 'High-Resolution MODIS-VIIRS Cloud Mask Algorithm'
+description = 'MODIS-VIIRS Cloud Mask algorithm for LEO/GEO'
 readme = 'README.md'
-requires-python = '>=3.9'
-classifiers = ['Programming Language :: Python :: 3',
-               'Programming Language :: C',
-#               'License :: OSI Approved :: MIT License',
-               'Operating System :: OS Independent',
-              ]
+requires-python = '>=3.9,<3.12'
+# license = ''
+authors = [
+    { name = 'Paolo Veglio', email = 'paolo.veglio@ssec.wisc.edu'},
+    ]
+classifiers = [
+    'Development Status :: 2 - Pre-Alpha',
+    # 'License :: OSI Approved :: Apache Software License',
+    'Programming Language :: Python :: 3.9',
+    'Programming Language :: Python :: 3.10',
+    'Programming Language :: Python :: 3.11',
+    'Programming Language :: Python :: Implementation :: CPython',
+    'Programming Language :: Cython',
+    'Programming Language :: C',
+    ]
+dependencies = [
+    'Cython',
+    'wheel',
+    'setuptools',
+    'numpy',
+    'xarray',
+    'attrs',
+    ]
+dynamic = ['version']
+
+[tool.hatch.version]
+path = 'mvcm/__init__.py'
+
 
-[project.urls]
-'Homepage' = 'https://gitlab.ssec.wisc.edu/pveglio/mvcm'
+[tool.hatch.envs.test]
+dependencies = [
+    'pytest',
+    ]
+
+[build-system]
+requires = ['hatchling']
+build-backend = 'hatchling.build'
 
 [tool.pytest.ini_options]
 addopts = [
     '--import-mode=importlib',
-]
+    ]
+
+
diff --git a/setup.py b/setup.py
index 18f61ec..137d269 100644
--- a/setup.py
+++ b/setup.py
@@ -1,31 +1,32 @@
-from distutils.core import setup
-from distutils.extension import Extension
+# from distutils.core import setup
+# from distutils.extension import Extension
+from setuptools import Extension, setup, find_packages
 
 from Cython.Build import cythonize
 
 import numpy
 
-sourcefiles = ['src/get_Reynolds_SST.c',      # SST
-               'src/bilinearInterpSST.c',     # _|
-               'src/swap_bytes.c',            # _|
-               'src/get_NDVI_background.c',   # NDVI
-               'src/get_Olson_eco.c',         # Olson eco type
-               'src/getcoord.c',              # _|
-               'src/get_GEOS.c',              # GEOS
-               'src/get_geos_times.c',        # _|
-               'src/get_granule_times.c',     # _|
-               'src/get_ti_vars.c',           # _|
-               'src/get_ti_weights.c',        # _|
-               'src/read_GEOS.c',             # _|
-               'src/read_GEOS_constants.c',   # _|
-               'src/read_GEOS_lndocn.c',      # _|
-               'src/assign_geos_vals.c',      # _|
-               'src/cithr.c',                 # Not sure
-               'src/check_reg_uniformity.c',  # reg. uniformity for restoral
-               'ancillary.pyx',
+sourcefiles = ['mvcm/ancillary.pyx',
+               'mvcm/c_tools/get_Reynolds_SST.c',      # SST
+               'mvcm/c_tools/bilinearInterpSST.c',     # _|
+               'mvcm/c_tools/swap_bytes.c',            # _|
+               'mvcm/c_tools/get_NDVI_background.c',   # NDVI
+               'mvcm/c_tools/get_Olson_eco.c',         # Olson eco type
+               'mvcm/c_tools/getcoord.c',              # _|
+               'mvcm/c_tools/get_GEOS.c',              # GEOS
+               'mvcm/c_tools/get_geos_times.c',        # _|
+               'mvcm/c_tools/get_granule_times.c',     # _|
+               'mvcm/c_tools/get_ti_vars.c',           # _|
+               'mvcm/c_tools/get_ti_weights.c',        # _|
+               'mvcm/c_tools/read_GEOS.c',             # _|
+               'mvcm/c_tools/read_GEOS_constants.c',   # _|
+               'mvcm/c_tools/read_GEOS_lndocn.c',      # _|
+               'mvcm/c_tools/assign_geos_vals.c',      # _|
+               'mvcm/c_tools/cithr.c',                 # Not sure
+               'mvcm/c_tools/check_reg_uniformity.c',  # reg. uniformity for restoral
                ]
 
-include_dirs = ['include',
+include_dirs = ['mvcm/c_tools/include',
                 '/opt/hdf4/4.2.14-gcc-8.3/include',
                 '/opt/hdfeos2/2.20-gcc-8.3/include',
                 '/opt/netcdf4/4.7.0-gcc-8.3/include',
@@ -41,5 +42,6 @@ extensions = [Extension('ancillary_data', sourcefiles,
                         library_dirs=library_dirs,
                         libraries=['netcdf']), ]
 
-setup(ext_modules=cythonize(extensions,
+setup(packages=find_packages(),
+      ext_modules=cythonize(extensions,
                             compiler_directives={'language_level': '3'}, ), )
diff --git a/tests/test_read_data.py b/tests/test_read_data.py
index 7541221..7f99a65 100644
--- a/tests/test_read_data.py
+++ b/tests/test_read_data.py
@@ -4,7 +4,7 @@ import os
 
 import pytest
 
-import read_data as rd
+import mvcm.read_data as rd
 
 
 @pytest.fixture
@@ -13,13 +13,13 @@ def fixturepath():
 
 
 @pytest.fixture
-def l1b_file():
-    return 'VNP02MOD.A2022173.1312.001.2022174011547.uwssec.nc'
+def l1b_file(fixturepath):
+    return os.path.join(fixturepath, 'VNP02MOD.A2022173.1312.001.2022174011547.uwssec.nc')
 
 
 @pytest.fixture
-def geo_file():
-    return 'VNP03MOD.A2022173.1312.001.2022174012746.uwssec.nc'
+def geo_file(fixturepath):
+    return os.path.join(fixturepath, 'VNP03MOD.A2022173.1312.001.2022174012746.uwssec.nc')
 
 
 # @pytest.fixture
@@ -74,8 +74,12 @@ def test_l1b(fixturepath, l1b_file, geo_file):
                         file_name_l1b=l1b_file,
                         file_name_geo=geo_file)
     geo = viirs.read_viirs_geo()
-    l1b = viirs.read_l1b(geo.solar_zenith.values)
+    l1b = viirs.read_viirs_l1b(geo.solar_zenith.values)
+    geo_vars = ['latitude', 'longitude', 'height', 'range', 'solar_zenith', 'solar_azimuth',
+                'sensor_zenith', 'sensor_azimuth', 'land_water_mask', 'quality_flag',
+                'relative_azimuth', 'sunglint_angle', 'scattering_angle']
 
+    assert list(geo).sort() == geo_vars.sort()
     assert 'M11' in l1b.data_vars
 
 
diff --git a/thresholds/thresholds.mvcm.snpp.v0.0.1.yaml b/thresholds/thresholds.mvcm.snpp.v0.0.1.yaml
new file mode 100644
index 0000000..801c795
--- /dev/null
+++ b/thresholds/thresholds.mvcm.snpp.v0.0.1.yaml
@@ -0,0 +1,706 @@
+# MODIS-VIIRS Continuity Cloud Mask (MVCCM) Thresholds
+
+# Version string
+thresholds_file_ver: v3.3.1_NPP
+
+Land_Day:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.3
+    adj: 1.25
+    perform: True
+  dl_ref3_tpw    : -10.00
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-16.0, -14.0, -12.0, 1.0, 1.0]
+    perform: True
+  Visible_Reflectance_Test:
+    thr: [0.207, 0.169, 0.132, 1.0, 1.0] # used to be dlref1
+    adj: 0.94 # used to be dl_b1bias_adj
+    perform: True
+  1.38um_High_Cloud_Test:
+    thr: [0.0375, 0.0250, 0.0125, 1.0, 1.0]
+    perform: True
+  dlvrat         : [1.80, 1.85, 1.90, 1.0, 1.0]
+#  dl11_12lcmult  : 0.3
+#  dl11_12hcadj   : 1.25
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+
+Land_Night:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.0
+    adj: 0
+    bt1: 270.0  # WILL FIND A BETTER NAME AT SOME POINT, MAYBE
+    lat: 30.0
+    perform: True
+  4-12um_BTD_Thin_Cirrus_Test:
+    thr: [15.0, 10.0, 5.00, 1.0, 1.0]
+    perform: True
+  7.3-11um_BTD_Mid_Level_Cloud_Test:
+    thr: [-8.0,   -10.0, -11.0, 1.0, 1.0]  # nl7_11s
+  11-4um_BT_Difference_Test:
+    thr_low: [0.0, -0.5, -1.0, 1.0]
+    thr_mid: [-0.5, 6.0, 0.5, 1.0]
+    thr_hi: [6.5, 6.0, 5.5, 1.0]
+    coeffs: [-0.3476, 0.5307, -0.1930]
+    int_adj: 0
+    ndvi: 0.25
+    ndvi_australia: 0.4
+    bt_diff_bounds : [1.0, -1.0]
+    perform: True
+  Surface_Temperature_Test:
+    desert_thr: 20.0
+    regular_thr: 12.0
+    channel_diff_11-12um_thr: [-0.2, 1.0]
+    channel_diff_11-4um_thr: [-0.5, 1.0]
+    perform: True
+      #  Surface_Temperature_Test_df1: [-0.2, 1.0]
+      #  Surface_Temperature_Test_df2: [-0.5, 1.0]
+      #  Surface_Temperature_Test_difference: [-0.2, 1.0, -0.5, 1.0]  # <- this merges the previous two arrays
+    #  Surface_Temperature_Test_1: 20.0  #   | might be worth figuring out if we can
+    #  Surface_Temperature_Test_2: 12.0  #   | merge these three coefficients
+    #  Surface_Temperature_Test_pfm: 1.0 # __|
+    #  Surface_Temperature_Test: [20.0, 12.0, 1.0]  # <- First attempt to merge the three values above
+  nlbt1          : 270.0
+  nl_lat         : 30.0
+    #  nl_ndvi        : 0.25
+    #  nl_ndvi_Aust   : 0.40
+    #  nl_intadj      : 0.0
+  nl_btd1        : -2.0
+    #  nl11_4coef     : [-0.3476, 0.5307, -0.1930]
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+
+Land_Day_Coast:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.3
+    adj: 1.25
+    perform: True
+  dl_ref3_tpw_t2 : -10.00
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-16.0, -14.0, -12.0, 1.0, 1.0]
+    perform: True
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  Visible_Reflectance_Test:
+    thr: [0.207, 0.169, 0.132, 1.0, 1.0]
+    adj: 0.94
+    perform: True
+  1.38um_High_Cloud_Test:
+    thr: [0.0375,  0.0250, 0.0125,  1.0, 1.0]
+    perform: True
+# dl11_12lcmult_t2 : 0.3
+# dl11_12hcadj_t2  : 1.25
+
+Land_Day_Desert:
+#  lds11_12hi     : [3.5, 1.0]
+  11-12um_Cirrus_Test:
+    coeffs: [3.5, 1.0]
+    cmult: 0.3
+    adj: 1.25
+    perform: True
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-28.0, -26.0, -24.0, -2.0, 0.0, 2.0, 1.0, 1.0] # this replaces lds11_4hi and lds11_4lo. The values
+    perform: True                                        # are sorted left to right so that they follow the
+                                                         # scheme: Lo-Mid-Hi--Hi-Mid-Lo This is the opposite
+                                                         # of Ocean_Day
+    #  lds11_4hi      : [2.0, 0.0, -2.0, 1.00, 1.0]
+    #  lds11_4lo      : [-28.0, -26.0, -24.0, 1.00]
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  Visible_Reflectance_Test:
+    thr: [0.326, 0.288, 0.250, 1.00, 1.0]
+    adj: 0.94
+    ndvi_thr: 0.25  # this used to be lds_ndvi
+    perform: True
+  1.38um_High_Cloud_Test:
+    thr: [0.0375, 0.0250, 0.0125, 1.0, 1.0]
+    perform: True
+  GEMI_Test:
+    gemi0: [0.085, 0.095, 0.115, 1.00, 1.0]
+    gemi1: [0.145, 0.170, 0.220, 1.00]
+    gemi2: [0.310, 0.335, 0.360, 1.00]
+    perform: True
+  lds_ref3_tpw   : -10.00
+  ldsbt1         : 320.0
+# lds11_12lcmult : 0.3
+# lds11_12hcadj  : 1.25
+
+Land_Day_Desert_Coast:
+#  lds11_12hi_c   : [3.5, 1.0]
+  11-12um_Cirrus_Test:
+    coeffs: [3.5, 1.0]
+    cmult: 0.3
+    adj: 1.25
+    perform: True
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-23.0, -21.0, -19.0, -2.0, 0.0, 2.0, 1.0, 1.0]
+    perform: True
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  Visible_Reflectance_Test:
+    thr: [0.326, 0.288, 0.250, 1.0, 1.0]
+    adj: 0.94
+    ndvi_thr: 0.25
+    perform: True
+  1.38um_High_Cloud_Test:
+    thr: [0.0375, 0.0250, 0.0125, 1.0, 1.0]
+    perform: True
+  lds_ref3_tpw_c : -10.00
+  ldsbt1_c       : 320.0
+# lds11_12lcmult_c : 0.3
+# lds11_12hcadj_c  : 1.25
+
+Ocean_Day:
+  11um_Test:
+    thr: [267.0, 270.0, 273.0, 1.0, 1.0]
+    perform: True
+  SST_Test:
+    thr: [3.000, 2.500, 2.000, 1.0, 1.0]
+    coeffs: [1.8860, 0.9380, 0.1280, 1.094]
+    perform: True
+  8.6-11um_Test:
+    thr: [-0.50,  -1.00,  -1.50,  1.0, 1.0]
+    perform: True
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.3
+    adj: 1.25
+    perform: True
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-11.0, -9.0, -7.0, 1.0, 1.0]
+    perform: True
+  NIR_Reflectance_Test:
+    thr: [0.062, 0.043, 0.029, 1.0, 1.0]
+    coeffs: [1.7291, 0.0715, -0.0026, 0.000025889]
+    adj: 0.0050
+    bias: 0.96
+    perform: True
+    midpt_coeff: 0.0200
+    locut_coeff: 0.0100
+  Vis/NIR_Ratio_Test:
+    thr: [0.837, 0.889, 0.934, 1.001, 1.154, 1.205, 1.0, 1.0] # This replace dovrathi and dovratlo
+    perform: True                                             # The values are sorted left to right
+                                                              # so that they follow the scheme:
+                                                              # Hi-Mid-Lo--Lo-Mid-Hi
+                                                              # Last two values are power and switch
+                                                              # to turn the test on/off
+  1.6_2.1um_NIR_Reflectance_Test:
+    thr: [0.0, 0.0, 0.0, 1.0, 1.0]  # This is a placeholder to force consistency with NIR_Reflectance_Test.
+                                    # In both cases the only value used is thr[3] AFAIK
+    coeffs: [1.01339303876915, -0.00277128813739, 0.00027804834484, -0.00001549681141, 0.00000016623006, 0.00000000000000]
+    bias: 0.98  # this corresponds to do_b6bias_adj
+    adj: 0.0125
+    locut_coeff: 0.0100
+    midpt_coeff: 0.0070
+    perform: True
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  #  vnir_ratio_hi  : [1.001, 1.154, 1.205, 1.0, 1.0]
+  #  vnir_ratio_lo  : [0.934, 0.889, 0.837, 1.0]
+  1.38um_High_Cloud_Test:
+    coeffs: [-0.2419, 0.0455, -0.0024, 0.000059029, -0.00000069964, 0.000000003204]
+    adj: 0.0010
+    szafac: 4.0
+    perform: True
+  do_b6bias_adj  : 0.98
+  do_b7bias_adj  : 0.97
+
+Ocean_Night:
+  11um_Test:
+    thr: [267.0, 270.0, 273.0, 2.0, 1.0]
+    perform: True
+  SST_Test:
+    thr: [3.000, 2.500, 2.000, 1.0, 1.0]
+    coeffs: [1.8860, 0.9380, 0.1280, 1.094]
+    perform: True
+  8.6-11um_Test:
+    thr: [-0.5, -1.0, -1.5, 1.0, 1.0]
+    perform: True
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.3
+    adj: 1.0
+    perform: True
+  11-4um_Oceanic_Stratus_Test:
+    thr: [1.25, 1.0, 0.25, 1.0, 1.0]
+    perform: True
+  Water_Vapor_Cloud_Test:
+    thr: [14.0, 15.0, 16.0, 1.0, 1.0]
+    perform: True
+  11um_Variability_Test:
+    thr: [3.0, 6.0, 7.0, 1.0, 1.0]
+    variability: 0.40
+    perform: True
+  11-4um_BT_Difference_Test:
+    coeffs: [-0.7093, 0.1128, -0.1567]
+    corr: 0.0  # no_intadj
+    locut_coeff: [-4.0, 4.0]  # no11_4load
+    midpt_coeff: [-2.5, 2.5]  # no11_4mdad
+    hicut_coeff: [-1.5, 1.5]  # no11_4hiad
+    perform: True             # no11_4_pfm
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+
+Polar_Day_Land:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.3
+    adj: 0.3
+    perform: True
+  pdl_ref3_tpw   : -10.00
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-16.0, -14.0, -12.0, 1.0, 1.0]
+    perform: True
+  pdlh20       : [215.0, 220.0, 225.0, 1.0, 1.0]
+  Visible_Reflectance_Test:
+    thr: [0.207, 0.169, 0.132, 1.0, 1.0]
+    adj: 0.94
+    perform: True
+  1.38um_High_Cloud_Test:
+    thr: [0.0375, 0.0250, 0.0125, 1.0, 1.0]
+    perform: True
+
+Polar_Night_Land:
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0
+    adj: 0    # I NEED TO WORK ON THIS
+    bt1: 270.0
+    perform: True
+  pnlbt2         : 270.0
+  Surface_Temperature_Test:
+    desert_thr: 20.0
+    regular_thr: 12.0
+    channel_diff_11-12um_thr: [0, 1.0]
+    channel_diff_11-4um_thr: [-0.5, 1.0]
+    perform: True
+      #  Surface_Temperature_Test_df1: [0.0, 1.0]
+      #  Surface_Temperature_Test_df2: [-0.5, 1.0]
+      #  Surface_Temperature_Test_difference: [-0.2, 1.0, -0.5, 1.0]  # <- this merges the previous two arrays
+      #  Surface_Temperature_Test_1: 20.0  #   | might be worth figuring out if we can
+      #  Surface_Temperature_Test_2: 12.0  #   | merge these three coefficients
+      #  Surface_Temperature_Test_pfm: 1.0 # __|
+      #  Surface_Temperature_Test: [20.0, 12.0, 1.0]  # <- First attempt to merge the three values above
+  11-4um_BT_Difference_Test:
+    low: [2.00, 1.70, 1.40, 1.0]
+    mid1: [1.70, 0.70, 0.3, 1.0]
+    mid2: [0.00, 0.00, 0.00, 0.0]
+    mid3: [0.00, 0.00, 0.00, 0.0]
+    high: [1.00, 0.70,  0.40, 1.0]
+    bt11_bounds: [235, 0, 0, 265]
+    perform: True
+  7.3-11um_BTD_Mid_Level_Cloud_Test:
+    low: [-1.00, 0.00, 1.00, 1.0, 1.0]  # pn_7_11l
+    mid1: [0.00, -4.50, -1.00, 1.0]  # pn_7_11m1
+    mid2: [-4.50, -10.50, -1.00, 1.0]  # pn_7_11m2
+    mid3: [-10.50, -20.0, -1.00, 1.0]  # pn_7_11m3
+    high: [-21.0, -20.0, -19.0,1.0]  # pn_7_11h
+    bt11_bounds: [220.0, 245.0, 255.0, 265.0]  # bt11_bnds2
+  4-12um_BTD_Thin_Cirrus_Test:
+    low: [2.00, 1.70, 1.40, 1.0]
+    mid1: [1.70, 0.70, 0.300, 1.0]
+    mid2: [0.00, 0.00, 0.00, 0.0]
+    mid3: [0.00, 0.00, 0.00, 0.0]
+    high: [1.00, 0.70,  0.40, 1.0]
+    bt11_bounds: [235.0, 000.0, 000.0, 265.0]
+    perform: True
+#  pnl_11_4_pfm   : 1.0
+#  pnl_7_11_pfm   : 1.0
+#  pnl_4_12_pfm   : 1.0
+
+Polar_Day_Coast:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0  # I NEED TO WORK ON THIS
+    adj: 0    # I NEED TO WORK ON THIS
+    perform: True
+  pdl_ref3_tpw_t2 : -10.00
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-16.0, -14.0, -12.0, 1.0, 1.0]
+    perform: True
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  Visible_Reflectance_Test:
+    thr: [0.207, 0.169, 0.132, 1.0, 1.0]
+    adj: 0.94
+    perform: True
+  1.38um_High_Cloud_Test:
+    thr: [0.0375, 0.0250, 0.0125, 1.0, 1.0]
+    perform: True
+
+Polar_Day_Desert:
+  11-12um_Cirrus_Test:
+    coeffs: [3.5, 1.0]
+    cmult: 0  # I NEED TO WORK ON THIS
+    adj: 0    # I NEED TO WORK ON THIS
+    perform: True
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-22.0, -20.0, -18.0, -2.0,  0.0, 2.0, 1.0, 1.0]
+    perform: True
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  Visible_Reflectance_Test:
+    thr: [0.326, 0.288, 0.250, 1.00, 1.0]
+    adj: 0.94
+    perform: True
+  1.38um_High_Cloud_Test:
+    thr: [0.0375, 0.0250, 0.0125,  1.00, 1.0]
+    perform: True
+  GEMI_Test:
+    gemi0: [0.085, 0.110, 0.135, 1.00, 1.0]
+    gemi1: [0.170, 0.220, 0.270, 1.00]
+    gemi2: [0.310, 0.335, 0.360, 1.00]
+    perform: True
+  pds_ref3_tpw   : -10.00
+  pdsbt1         : 320.0
+
+Polar_Day_Desert_Coast:
+  11-12um_Cirrus_Test:
+    coeffs: [3.5, 1.0]
+    cmult: 0  # I NEED TO WORK ON THIS
+    adj: 0    # I NEED TO WORK ON THIS
+    perform: True
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-23.0, -21.0, -19.0, -2.0, 0.0, 2.0, 1.0, 1.0]
+    perform: True
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  Visible_Reflectance_Test:
+    thr: [0.326, 0.288, 0.250, 1.00, 1.0]
+    adj: 0.94
+    perform: True
+  1.38um_High_Cloud_Test:
+    thr: [0.0375, 0.0250, 0.0125,  1.00, 1.0]
+    perform: True
+  pds_ref3_tpw_c : -10.00
+  pdsbt1_c       : 320.0
+
+Polar_Day_Snow:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0  # I NEED TO WORK ON THIS
+    adj: 0    # I NEED TO WORK ON THIS
+    perform: True
+  dps_ref3_tpw   : 0.75
+  dpsbt1         : 230.0
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  1.38um_High_Cloud_Test:
+    thr: [0.060, 0.0525, 0.045, 1.0, 1.0]
+    perform: True
+  11-4um_BT_Difference_Test:
+    low: [20.0, 18.0, 16.0, 1.0]
+    mid1: [18.0, 16.0, 2.0, 1.0]
+    mid2: [0.0, 0.0, 0.0, 0.0]
+    mid3: [0.0, 0.0, 0.0, 0.0]
+    high: [18.0, 16.0, 14.0, 1.0]
+    bt_11_bounds: [230.0, 0.0, 0.0, 245.0]
+    perform: True
+  dps11_12adj    : 0.8
+
+Polar_Night_Snow:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0
+    adj: 0    # I NEED TO WORK ON THIS
+    bt1: 270.0
+    perform: True
+  4-12um_BTD_Thin_Cirrus_Test:
+    low: [4.50, 4.00, 3.50, 1.0]  # pn_4_12l
+    mid1: [4.00,  2.00, 0.500, 1.0]  # pn_4_12m1
+    mid2: [0.00,  0.00, 0.00, 0.0]  # pn_4_12m2
+    mid3: [0.00,  0.00, 0.00, 0.0]  # pn_4_12m3
+    high: [2.50, 2.00, 1.50, 1.0]  # pn_4_12h
+    bt11_bounds: [235.0, 0.0, 0.0, 265.0]  # bt_11_bounds
+    perform: True
+  7.3-11um_BTD_Mid_Level_Cloud_Test:
+    low: [-1.00, 0.00, 1.00, 1.0, 1.0]  # pn_7_11l
+    mid1: [0.00,  -4.50, -1.00, 1.0]  # pn_7_11m1
+    mid2: [-4.50, -10.50, -1.00, 1.0]  # pn_7_11m2
+    mid3: [-10.50, -20.0, -1.00, 1.0]  # pn_7_11m3
+    high: [-21.0, -20.0, -19.0,1.0]  # pn_7_11h
+    low_ice: [0.00, 1.00, 2.00, 1.0]  # pn_7_11lw
+    mid1_ice: [1.00,  -7.00, -1.00, 1.0]  # pn_7_11m1w
+    mid2_ice: [-7.00, -16.50, -1.00, 1.0]  # pn_7_11m2w
+    mid3_ice: [-16.50, -20.0, -1.00, 1.0]  # pn_7_11m3w
+    high_ice: [-21.0 -20.0, -19.0, 1.0]  # pn_7_11hw
+    bt11_bounds: [220.0, 245.0, 255.0, 265.0]  # bt11_bnds2
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  11-4um_BT_Difference_Test:
+    top:
+      low: [2.00, 1.70, 1.40, 1.0]
+      mid1: [1.70, 0.30, 0.300, 1.0]
+      mid2: [0.00, 0.00, 0.00, 0.0]
+      mid3: [0.00, 0.00, 0.00, 0.0]
+      high: [0.60, 0.30, 0.00, 1.0]
+    bottom:
+      low: [-3.00, -2.50, -2.00, 1.0]
+      mid1: [-2.50, -2.50, -0.50, 1.0]
+      mid2: [0.00, 0.00, 0.00, 0.0]
+      mid3: [0.00, 0.00, 0.00, 0.0]
+      high: [-3.00, -2.50, -2.00, 1.0]
+    bt_thr: 230.0  # pnbt3
+    tpw_thr: 0.2   # pntpw
+    perform: True
+  pn11_12adj     : 0.8
+  pnbt2          : 270.0
+
+Polar_Day_Ocean:
+  11um_Test:
+    thr: [267.0, 270.0, 273.0, 1.0, 1.0]
+    perform: True
+  SST_Test:
+    thr: [3.000, 2.500, 2.000, 1.0, 1.0]
+    coeffs: [1.886, 0.938, 0.128, 1.094]
+    perform: True
+  8.6-11um_Test:
+    thr: [-0.50, -1.00, -1.50, 1.0, 1.0]
+    perform: True
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.3
+    adj: 1.25
+    perform: True
+  NIR_Reflectance_Test:
+    thr: [0.062, 0.043, 0.029, 1.0, 1.0]
+    coeffs: [1.7291, 0.0715, -0.0026, 0.000025889]
+    adj: 0.0050
+    bias: 0.96
+    midpt_coeff: 0.0200
+    locut_coeff: 0.0100
+    perform: True
+  Vis/NIR_Ratio_Test:
+    thr: [0.837, 0.889, 0.934, 1.001, 1.154, 1.205, 1.0, 1.0]
+    perform: True
+  1.6_2.1um_NIR_Reflectance_Test:
+    thr: [0.0, 0.0, 0.0, 1.0, 1.0]
+    coeffs: [1.01339303876915, -0.00277128813739, 0.00027804834484, -0.00001549681141, 0.00000016623006, 0.00000000000000]
+    bias: 0.98
+    adj: 0.0125
+    locut_coeff: 0.0100
+    midpt_coeff: 0.0070
+    perform: True
+  11-4um_Oceanic_Stratus_Test:
+    thr: [-11.0, -9.0,  -7.0,  1.0, 1.0]
+    perform: True
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  pdo_b26pfm     : 1.0
+  pdo_b2bias_adj : 0.96
+  pdo_b7bias_adj : 0.97
+  1.38um_High_Cloud_Test:
+    coeffs: [-0.2419, 0.0455, -0.0024, 0.000059029, -0.00000069964, 0.000000003204]
+    adj: 0.0010
+    szafac: 4.0
+    perform: True
+
+Polar_Night_Ocean:
+  11um_Test:
+    thr: [267.0, 270.0, 273.0, 1.0, 1.0]
+    perform: True
+  SST_Test:
+    thr: [3.000, 2.500, 2.000, 1.0, 1.0]
+    coeffs: [1.8860, 0.9380, 0.1280, 1.094]
+    perform: True
+  8.6-11um_Test:
+    thr: [-0.5, -1.0, -1.5, 1.0, 1.0]
+    perform: True
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.3
+    adj: 1.0
+    perform: True
+  pnobt1         : 280.0
+  11-4um_Oceanic_Stratus_Test:
+    thr: [1.25, 1.0, 0.25, 1.0, 1.0]
+    perform: True
+  Water_Vapor_Cloud_Test:
+    thr: [14.0, 15.0, 16.0, 1.0, 1.0]
+    perform: True
+  11um_Variability_Test:
+    thr: [3.0, 6.0, 7.0, 1.0, 1.0]
+    variability: 040
+    perform: True
+  11-4um_BT_Difference_Test:
+    coeffs: [-0.7093, 0.1128, -0.1567]
+    corr: 0.0                 # pno_intadj
+    locut_coeff: [-4.0, 4.0]  # pno11_4load
+    midpt_coeff: [-2.5, 2.5]  # pno11_4mdad
+    hicut_coeff: [-1.5, 1.5]  # pno11_4hiad
+    perform: True             # pno11_4_pfm
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+
+
+Day_Snow:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0.3
+    adj: 0    # I NEED TO WORK ON THIS
+    perform: True
+  ds_ref3_tpw    : 0.75
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  1.38um_High_Cloud_Test:
+    thr: [0.060, 0.0525, 0.045, 1.0, 1.0]
+    perform: True
+  11-4um_BT_Difference_Test:
+    low: [20.0, 18.0, 16.0, 1.0]
+    mid1: [18.0, 16.0, 2.0, 1.0]
+    mid2: [0.0, 0.0, 0.0, 0.0]
+    mid3: [0.0, 0.0, 0.0, 0.0]
+    high: [18.0, 16.0, 14.0, 1.0]
+    bt_11_bounds: [230.0, 0.0, 0.0, 245.0]
+    perform: True
+  ds11_12adj     : 0.8
+  ds11_12lcmult  : 0.3
+  ds11_12hcmult  : 0.3
+
+Night_Snow:
+  11-12um_Cirrus_Test:
+    coeffs: [3.0, 1.0]
+    cmult: 0
+    adj: 0    # I NEED TO WORK ON THIS
+    bt1: 270.0
+    perform: True
+  nsbt2          : 270.0
+  4-12um_BTD_Thin_Cirrus_Test:
+    thr: [4.50,  4.00,  3.50,  1.0, 1.0]
+    perform: True
+  11-4um_BT_Difference_Test:
+    thr: [0.70, 0.60, 0.50, 1.0, 1.0]
+    bottom:
+      low: [20.0, 18.0, 16.0, 1.0]
+      mid1: [18.0, 16.0, 2.0, 1.0]
+      mid2: [0.0, 0.0, 0.0, 0.0]
+      mid3: [0.0, 0.0, 0.0, 0.0]
+      high: [18.0, 16.0, 14.0, 1.0]
+    bt_11_bounds: [230.0, 0.0, 0.0, 245.0]
+    bt_thr: 230.0
+    tpw_thr: 0.2
+    perform: True
+  7.3-11um_BTD_Mid_Level_Cloud_Test:
+    low: [-1.00, 0.00, 1.00, 1.0, 1.0]  # pn_7_11l
+    mid1: [0.00,  -4.50, -1.00, 1.0]  # pn_7_11m1
+    mid2: [-4.50, -10.50, -1.00, 1.0]  # pn_7_11m2
+    mid3: [-10.50, -20.0, -1.00, 1.0]  # pn_7_11m3
+    high: [-21.0, -20.0, -19.0,1.0]  # pn_7_11h
+    low_ice: [0.00, 1.00, 2.00, 1.0]  # pn_7_11lw
+    mid1_ice: [1.00,  -7.00, -1.00, 1.0]  # pn_7_11m1w
+    mid2_ice: [-7.00, -16.50, -1.00, 1.0]  # pn_7_11m2w
+    mid3_ice: [-16.50, -20.0, -1.00, 1.0]  # pn_7_11m3w
+    high_ice: [-21.0 -20.0, -19.0, 1.0]  # pn_7_11hw
+    bt11_bounds: [220.0, 245.0, 255.0, 265.0]  # bt11_bnds2
+    perform: False
+  CO2_High_Clouds_Test: [222.0, 224.0, 226.0, 1.0, 1.0]
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  ns11_12adj     : 0.8
+
+Antarctic_Day:
+  Water_Vapor_High_Clouds_Test: [215.0, 220.0, 225.0, 1.0, 1.0]
+  ant4_11l       : [26.0   23.0,  20.0,  1.0]
+  ant4_11h       : [18.0,  15.0,  12.0,  1.0]
+  ant4_11m1      : [23.0,  15.0,   3.0   1.0]
+  ant4_11m2      : [0.0,   0.0,   0.0,  0.0]
+  ant4_11m3      : [0.0,   0.0,   0.0,  0.0]
+  bt_11_bnds4    : [240.0,  0.0,   0.0, 255.0]
+  antbt1         : 230.0
+  ant4_11_pfm    : 1.0
+
+Daytime_Ocean_Spatial_Variability:
+  dovar11        : 0.40
+  dovar86        : 0.0020
+
+Sun_Glint:
+  #  snglntv        : [1.02, 1.17]
+  #  snglntvch      : [0.97, 1.28]
+  #  snglntvcl      : [1.07, 1.12]
+  snglnt         : [0.97, 1.02, 1.07, 1.12, 1.17, 1.28, 1.0, 1.0] # This replaces snglntv, snglntvch snglntvcl
+                                                                  # The values are sorted left to right such that
+                                                                  # Hi-Mid-Lo--Lo-Mid-Hi
+                                                                  # I also added the two 1.0 at the end to
+                                                                  # maintain consistency with the other
+                                                                  # thresholds for conf_test_dble
+  sg_tbdfl       : 13.0
+  sngm09         : 0.568
+  sngm02vm       : 0.001
+  band2_0deg     : [.1296, .1152, .1008, 1.0]   # snglnt0_b2
+  band2_10deg    : [.1152, .0624, .0144, 1.0]   # snglnt10_b2
+  band2_20deg    : [.0624, .0432, .0144, 1.0]   # snglnt20_b2
+  band7_0deg     : [.1176, .1078, .0980, 1.0]   # snglnt0_b7
+  band7_10deg    : [.1078, .0686,  .0098, 1.0]  # snglnt10_b7
+  band7_20deg    : [.0686, .0196,  .0098, 1.0]  # snglnt20_b7
+  bounds         : [0.0, 10.0, 25.0, 40.0]      # snglnt_bounds
+
+Land_Restoral:
+  ldsr5_4_thr    : 1.96
+  ldr5_4_thr     : 2.94
+  ld20m22        : 11.0
+  ld22m31        : 15.0
+  ldsbt11        : [295.0, 300.0, 305.0]
+  ldsbt11bd      : [290.0, 295.0, 305.0]
+  lnbt11         : [287.5, 292.5, 297.5]
+
+Night_Snow_Inversion:
+  pn65_11        : [10.0, 1.0]
+  pn13_11        : [3.0, 1.0]
+  pn7_11         : [5.0, 1.0]
+  n65_11         : [10.0, 1.0]
+
+Snow_Mask:
+  sm_bt11        : 280.0
+  sm_ndsi        : 0.31
+  Grnlnd_ndsi    : 0.66
+  sm_ref2        : 0.1056
+  sm85_11        : 0.0
+  sm85_11hel     : 1.5
+  sm37_11        : 11.0
+  sm37_11hel     : 16.0
+  sm_mnir        : 0.196
+  sm_lsfcdif     : 20.0
+  sm_wsfcdif     : 20.0
+  sm_bt1         : 273.0
+  prd_ndvi_const : -0.2015
+
+Coastal_NDVI_Thresholds:
+  coast_ndvi     : [-0.18, 0.40]
+
+# Ocean NDVI and shallow (turbid) water test thresholds.
+Ocean_NDVI_and_Shallow_Water:
+  swc_ndvi       : [-0.35, 0.40]
+  swc_124std     : 0.0023
+
+Thin_Cirrus_Tests:
+  tci_ref3_tpw   : 0.75
+  dltci          : [0.0250, 0.0125]
+  dstci          : [0.0350, 0.0300]
+
+# Power cosine(VZA) is raised to in VZA-correction codes
+VZA_correction:
+  vzcpow         : [0.75, 0.75, 0.25]
+
+# Thresholds used in calculating 0.66 and 0.413 um cloud test thresholds
+Misc:
+  des_ndvi       : 0.25
+  fill_ndvi      : [32.000, 0.00] # replaces fill_ndvi1 and fill_ndvi2
+  ndvi_bnd1      : 0.05
+  ndvi_bnd2      : 0.95
+  adj_fac_desert : 0.10
+  adj_fac_land   : 0.00
+
+# Coefficients for band 1 land thresholds
+Coeffs_Band1_land_thresh:
+  - [32.00000000, 0.00000000, 0.00000000, 0.00000000, 42.00000000, 0.00000000, 0.00000000, 0.00000000, 52.00000000, 0.00000000, 0.00000000, 0.00000000]
+  - [24.00000000, 0.00000000, 0.00000000, 0.00000000, 28.00000000, 0.00000000, 0.00000000, 0.00000000, 32.00000000, 0.00000000, 0.00000000, 0.00000000]
+  - [99.13076923, -2.00907925, 0.01492075, -0.00003531, 122.19090909, -2.32652292, 0.01659848, -0.00003681, 142.66293706, -2.57860528, 0.01773252, -0.00003685]
+  - [85.07902098, -1.59413364, 0.01123310, -0.00002556, 144.56573427, -2.81054779, 0.01967366, -0.00004324, 204.35454545, -4.03411810, 0.02816667, -0.00006103]
+  - [85.03846154, -1.50831391, 0.01006760, -0.00002199, 165.15314685, -3.24716783, 0.02255594, -0.00004965, 242.06363636, -4.90912587, 0.03445455, -0.00007587]
+  - [81.00979021, -1.37731935, 0.00881294, -0.00001859, 220.36783217, -4.44111888, 0.03087762, -0.00006888, 359.72587413, -7.50491841, 0.05294231, -0.00011917]
+  - [76.94055944, -1.35441725, 0.00896096, -0.00001952, 172.36783217, -3.33144911, 0.02242308, -0.00004810, 267.90909091, -5.31620047, 0.03597727, -0.00007698]
+  - [85.83006993, -1.55480575, 0.01025932, -0.00002216, 160.73706294, -3.07291375, 0.02041900, -0.00004330, 237.37622378, -4.63444833, 0.03091317, -0.00006525]
+  - [105.02447552, -1.98017094, 0.01319522, -0.00002877, 135.50699301, -2.59097902, 0.01749301, -0.00003811, 165.33006993, -3.18872183, 0.02171387, -0.00004734]
+  - [105.02447552, -1.98017094, 0.01319522, -0.00002877, 135.50699301, -2.59097902, 0.01749301, -0.00003811, 165.33006993, -3.18872183, 0.02171387, -0.00004734]
+
+# Coefficients for band 8 land thresholds
+Coeffs_Band8_land_thresh:
+  - [282.74916084, -5.42869658, 0.03660781, -0.00008092, 344.87048951, -6.80660839, 0.04770163, -0.00010991, 407.83734266, -8.20392385, 0.05894114, -0.00013926]
+  - [229.50727273, -4.31606061, 0.02868182, -0.00006212, 316.38517483, -6.41910256, 0.04603089, -0.00010787, 403.78188811, -8.52743395, 0.06335781, -0.00015340]
+  - [239.32391608, -4.49928127, 0.02977214, -0.00006436, 270.31741259, -5.33625680, 0.03757168, -0.00008609, 301.76013986, -6.18557498, 0.04546562, -0.00010806]
diff --git a/unit_tests.py b/unit_tests.py
deleted file mode 100644
index 0e6b762..0000000
--- a/unit_tests.py
+++ /dev/null
@@ -1,97 +0,0 @@
-from glob import glob
-
-import ruamel_yaml as yml
-import numpy as np
-import xarray as xr
-
-from tests import CloudTests
-import read_data as rd
-import scene as scn
-
-# import pytest
-
-# #################################################################### #
-# TEST CASE
-
-# data:
-_datapath = '/ships19/hercules/pveglio/mvcm_viirs_hires'
-_fname_mod02 = glob(f'{_datapath}/VNP02MOD.A2022173.1454.001.*.uwssec_bowtie_restored.nc')[0]
-_fname_mod03 = glob(f'{_datapath}/VNP03MOD.A2022173.1454.001.*.uwssec.nc')[0]
-_fname_img02 = glob(f'{_datapath}/VNP02IMG.A2022173.1454.001.*.uwssec_bowtie_restored.nc')[0]
-_fname_img03 = glob(f'{_datapath}/VNP03IMG.A2022173.1454.001.*.uwssec.nc')[0]
-
-# thresholds:
-_threshold_file = '/home/pveglio/mvcm_leo/thresholds/new_thresholds.mvcm.snpp.v1.0.0.yaml'
-
-# ancillary files:
-_geos_atm_1 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1200.V01.nc4'
-_geos_atm_2 = 'GEOS.fpit.asm.inst3_2d_asm_Nx.GEOS5124.20220622_1500.V01.nc4'
-_geos_land = 'GEOS.fpit.asm.tavg1_2d_lnd_Nx.GEOS5124.20220622_1430.V01.nc4'
-_geos_ocean = 'GEOS.fpit.asm.tavg1_2d_ocn_Nx.GEOS5124.20220622_1430.V01.nc4'
-_geos_constants = 'GEOS.fp.asm.const_2d_asm_Nx.00000000_0000.V01.nc4'
-_ndvi_file = 'NDVI.FM.c004.v2.0.WS.00-04.177.hdf'
-_sst_file = 'oisst.20220622'
-_eco_file = 'goge1_2_img.v1'
-
-# #################################################################### #
-
-
-def load_paths():
-
-    file_names = {'MOD02': f'{_fname_mod02}',
-                  'MOD03': f'{_fname_mod03}',
-                  'IMG02': f'{_fname_img02}',
-                  'IMG03': f'{_fname_img03}',
-                  'GEOS_atm_1': f'{_geos_atm_1}',
-                  'GEOS_atm_2': f'{_geos_atm_2}',
-                  'GEOS_land': f'{_geos_land}',
-                  'GEOS_ocean': f'{_geos_ocean}',
-                  'GEOS_constants': f'{_geos_constants}',
-                  'NDVI': f'{_ndvi_file}',
-                  'SST': f'{_sst_file}',
-                  'ANC_DIR': f'{_datapath}/ancillary'
-                  }
-
-    return file_names
-
-
-def test_read_data(file_names):
-
-    with open(_threshold_file) as f:
-        text = f.read()
-    thresholds = yml.safe_load(text)
-
-    sunglint_angle = thresholds['Sun_Glint']['bounds'][3]
-
-    viirs_xr = rd.get_data(file_names, sunglint_angle)
-
-    return viirs_xr
-
-
-def test_scene(viirs_xr):
-
-    scene_xr = xr.Dataset()
-
-    for s in scn._scene_list:
-        scene_xr[s] = (('number_of_lines', 'number_of_pixels'), scn.scene_id[s])
-    scene_xr['latitude'] = viirs_xr.latitude
-    scene_xr['longitude'] = viirs_xr.longitude
-
-    viirs_data = xr.Dataset(viirs_xr, coords=scene_xr)
-    viirs_data.drop_vars(['latitude', 'longitude'])
-
-    return viirs_data
-
-
-def test_test(viirs_data):
-
-    with open(_threshold_file) as f:
-        text = f.read()
-    thresholds = yml.safe_load(text)
-
-    cmin = np.zeros(viirs_data.M01.shape)
-
-    Ocean_Day = CloudTests(viirs_data, 'Ocean_Day', thresholds)
-    cmin = Ocean_Day.single_threshold_test('11BT_Test', viirs_data.M15.values, cmin)
-
-    return cmin
-- 
GitLab