Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unit handling in ERF DNB normalization's saturation correction #2044

Merged
merged 5 commits into from Mar 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion satpy/composites/viirs.py
Expand Up @@ -268,7 +268,7 @@ def _saturation_correction(self, dnb_data, unit_factor, min_val,
max_val)) / dnb_data.size
LOG.debug("Dynamic DNB saturation percentage: %f", saturation_pct)
while saturation_pct > 0.005:
max_val *= 1.1 * unit_factor
max_val *= 1.1
saturation_pct = float(np.count_nonzero(
dnb_data > max_val)) / dnb_data.size
LOG.debug("Dynamic DNB saturation percentage: %f",
Expand Down
122 changes: 56 additions & 66 deletions satpy/tests/compositor_tests/test_viirs.py
Expand Up @@ -17,11 +17,15 @@
# satpy. If not, see <http://www.gnu.org/licenses/>.
"""Tests for VIIRS compositors."""

import unittest
import dask.array as da
import numpy as np
import pytest
import xarray as xr
from pyresample.geometry import AreaDefinition


class TestVIIRSComposites(unittest.TestCase):
"""Test VIIRS-specific composites."""
class TestVIIRSComposites:
"""Test various VIIRS-specific composites."""

def test_load_composite_yaml(self):
"""Test loading the yaml for this sensor."""
Expand All @@ -30,11 +34,6 @@ def test_load_composite_yaml(self):

def test_histogram_dnb(self):
"""Test the 'histogram_dnb' compositor."""
import dask.array as da
import numpy as np
import xarray as xr
from pyresample.geometry import AreaDefinition

from satpy.composites.viirs import HistogramDNB
rows = 5
cols = 10
Expand Down Expand Up @@ -64,22 +63,16 @@ def test_histogram_dnb(self):
dims=('y', 'x'),
attrs={'name': 'solar_zenith_angle', 'area': area})
res = comp((c01, c02))
self.assertIsInstance(res, xr.DataArray)
self.assertIsInstance(res.data, da.Array)
self.assertEqual(res.attrs['name'], 'histogram_dnb')
self.assertEqual(res.attrs['standard_name'],
'equalized_radiance')
assert isinstance(res, xr.DataArray)
assert isinstance(res.data, da.Array)
assert res.attrs['name'] == 'histogram_dnb'
assert res.attrs['standard_name'] == 'equalized_radiance'
data = res.compute()
unique_values = np.unique(data)
np.testing.assert_allclose(unique_values, [0.5994, 0.7992, 0.999], rtol=1e-3)

def test_adaptive_dnb(self):
"""Test the 'adaptive_dnb' compositor."""
import dask.array as da
import numpy as np
import xarray as xr
from pyresample.geometry import AreaDefinition

from satpy.composites.viirs import AdaptiveDNB
rows = 5
cols = 10
Expand Down Expand Up @@ -108,22 +101,16 @@ def test_adaptive_dnb(self):
dims=('y', 'x'),
attrs={'name': 'solar_zenith_angle', 'area': area})
res = comp((c01, c02))
self.assertIsInstance(res, xr.DataArray)
self.assertIsInstance(res.data, da.Array)
self.assertEqual(res.attrs['name'], 'adaptive_dnb')
self.assertEqual(res.attrs['standard_name'],
'equalized_radiance')
assert isinstance(res, xr.DataArray)
assert isinstance(res.data, da.Array)
assert res.attrs['name'] == 'adaptive_dnb'
assert res.attrs['standard_name'] == 'equalized_radiance'
data = res.compute()
np.testing.assert_allclose(data.data, 0.999, rtol=1e-4)

def test_erf_dnb(self):
"""Test the 'dynamic_dnb' or ERF DNB compositor."""
import dask.array as da
import numpy as np
import xarray as xr
from pyresample.geometry import AreaDefinition

from satpy.composites.viirs import ERFDNB
def test_hncc_dnb(self):
"""Test the 'hncc_dnb' compositor."""
from satpy.composites.viirs import NCCZinke
rows = 5
cols = 10
area = AreaDefinition(
Expand All @@ -133,11 +120,10 @@ def test_erf_dnb(self):
cols, rows,
(-20037508.34, -10018754.17, 20037508.34, 10018754.17))

comp = ERFDNB('dynamic_dnb', prerequisites=('dnb',),
standard_name='toa_outgoing_radiance_per_'
'unit_wavelength')
comp = NCCZinke('hncc_dnb', prerequisites=('dnb',),
standard_name='toa_outgoing_radiance_per_'
'unit_wavelength')
dnb = np.zeros((rows, cols)) + 0.25
dnb[2, :cols // 2] = np.nan
dnb[3, :] += 0.25
dnb[4:, :] += 0.5
dnb = da.from_array(dnb, chunks=25)
Expand All @@ -162,28 +148,22 @@ def test_erf_dnb(self):
dims=('y',),
attrs={'name': 'moon_illumination_fraction', 'area': area})
res = comp((c01, c02, c03, mif))
self.assertIsInstance(res, xr.DataArray)
self.assertIsInstance(res.data, da.Array)
self.assertEqual(res.attrs['name'], 'dynamic_dnb')
self.assertEqual(res.attrs['standard_name'],
'equalized_radiance')
assert isinstance(res, xr.DataArray)
assert isinstance(res.data, da.Array)
assert res.attrs['name'] == 'hncc_dnb'
assert res.attrs['standard_name'] == 'ncc_radiance'
data = res.compute()
unique = np.unique(data)
assert np.isnan(unique).any()
nonnan_unique = unique[~np.isnan(unique)]
np.testing.assert_allclose(
nonnan_unique,
[0.00000000e+00, 1.00446703e-01, 1.64116082e-01, 2.09233451e-01,
1.43916324e+02, 2.03528498e+02, 2.49270516e+02])

def test_hncc_dnb(self):
"""Test the 'hncc_dnb' compositor."""
import dask.array as da
import numpy as np
import xarray as xr
from pyresample.geometry import AreaDefinition
unique, [3.48479712e-04, 6.96955799e-04, 1.04543189e-03, 4.75394738e-03,
9.50784532e-03, 1.42617433e-02, 1.50001560e+03, 3.00001560e+03,
4.50001560e+03])

from satpy.composites.viirs import NCCZinke
@pytest.mark.parametrize("dnb_units", ["W m-2 sr-1", "W cm-2 sr-1"])
@pytest.mark.parametrize("saturation_correction", [False, True])
def test_erf_dnb(self, dnb_units, saturation_correction):
"""Test the 'dynamic_dnb' or ERF DNB compositor."""
from satpy.composites.viirs import ERFDNB
rows = 5
cols = 10
area = AreaDefinition(
Expand All @@ -193,16 +173,20 @@ def test_hncc_dnb(self):
cols, rows,
(-20037508.34, -10018754.17, 20037508.34, 10018754.17))

comp = NCCZinke('hncc_dnb', prerequisites=('dnb',),
standard_name='toa_outgoing_radiance_per_'
'unit_wavelength')
comp = ERFDNB('dynamic_dnb', prerequisites=('dnb',),
saturation_correction=saturation_correction,
standard_name='toa_outgoing_radiance_per_'
'unit_wavelength')
dnb = np.zeros((rows, cols)) + 0.25
dnb[2, :cols // 2] = np.nan
dnb[3, :] += 0.25
dnb[4:, :] += 0.5
if dnb_units == "W cm-2 sr-1":
dnb /= 10000.0
dnb = da.from_array(dnb, chunks=25)
c01 = xr.DataArray(dnb,
dims=('y', 'x'),
attrs={'name': 'DNB', 'area': area})
attrs={'name': 'DNB', 'area': area, 'units': dnb_units})
sza = np.zeros((rows, cols)) + 70.0
sza[:, 3] += 20.0
sza[:, 4:] += 45.0
Expand All @@ -221,14 +205,20 @@ def test_hncc_dnb(self):
dims=('y',),
attrs={'name': 'moon_illumination_fraction', 'area': area})
res = comp((c01, c02, c03, mif))
self.assertIsInstance(res, xr.DataArray)
self.assertIsInstance(res.data, da.Array)
self.assertEqual(res.attrs['name'], 'hncc_dnb')
self.assertEqual(res.attrs['standard_name'],
'ncc_radiance')
assert isinstance(res, xr.DataArray)
assert isinstance(res.data, da.Array)
assert res.attrs['name'] == 'dynamic_dnb'
assert res.attrs['standard_name'] == 'equalized_radiance'
data = res.compute()
unique = np.unique(data)
np.testing.assert_allclose(
unique, [3.48479712e-04, 6.96955799e-04, 1.04543189e-03, 4.75394738e-03,
9.50784532e-03, 1.42617433e-02, 1.50001560e+03, 3.00001560e+03,
4.50001560e+03])
assert np.isnan(unique).any()
nonnan_unique = unique[~np.isnan(unique)]
if saturation_correction:
exp_unique = [0.000000e+00, 3.978305e-04, 6.500003e-04,
8.286927e-04, 5.628335e-01, 7.959671e-01,
9.748567e-01]
else:
exp_unique = [0.00000000e+00, 1.00446703e-01, 1.64116082e-01,
2.09233451e-01, 1.43916324e+02, 2.03528498e+02,
2.49270516e+02]
np.testing.assert_allclose(nonnan_unique, exp_unique)