From 2a55d5bd523f428f2f0c72fe375c3e766862f05e Mon Sep 17 00:00:00 2001 From: Tyler Pauly Date: Thu, 31 Oct 2024 13:32:57 -0400 Subject: [PATCH] JP-3426: Ensure proper order of bitwise operations (#8916) --- changes/8916.background.rst | 1 + jwst/background/background_sub.py | 22 +++++++++++++++------- jwst/background/tests/test_background.py | 24 +++++++++++++++++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 changes/8916.background.rst diff --git a/changes/8916.background.rst b/changes/8916.background.rst new file mode 100644 index 0000000000..e2e4414b14 --- /dev/null +++ b/changes/8916.background.rst @@ -0,0 +1 @@ +Apply bitwise operations in correct order when counting good pixels in the background mask during WFSS background subtraction. \ No newline at end of file diff --git a/jwst/background/background_sub.py b/jwst/background/background_sub.py index b5fa267bfb..9edd641c23 100755 --- a/jwst/background/background_sub.py +++ b/jwst/background/background_sub.py @@ -272,6 +272,20 @@ def average_background(input_model, bkg_list, sigma, maxiters): return avg_bkg +def sufficient_background_pixels(dq_array, bkg_mask, min_pixels=100): + """Count number of good pixels for background use. + + Check DQ flags of pixels selected for bkg use - XOR the DQ values with + the DO_NOT_USE flag to flip the DO_NOT_USE bit. Then count the number + of pixels that AND with the DO_NOT_USE flag, i.e. initially did not have + the DO_NOT_USE bit set. + """ + return np.count_nonzero((dq_array[bkg_mask] + ^ pixel['DO_NOT_USE']) + & pixel['DO_NOT_USE'] + ) > min_pixels + + def subtract_wfss_bkg(input_model, bkg_filename, wl_range_name, mmag_extract=None): """Scale and subtract a background reference image from WFSS/GRISM data. @@ -310,18 +324,12 @@ def subtract_wfss_bkg(input_model, bkg_filename, wl_range_name, mmag_extract=Non # i.e. in regions we can use as background. if got_catalog: bkg_mask = mask_from_source_cat(input_model, wl_range_name, mmag_extract) - # Ensure mask has 100 pixels and that those pixels correspond to valid - # pixels using model DQ array - if np.count_nonzero(input_model.dq[bkg_mask] - ^ pixel['DO_NOT_USE'] - & pixel['DO_NOT_USE'] - ) < 100: + if not sufficient_background_pixels(input_model.dq, bkg_mask): log.warning("Not enough background pixels to work with.") log.warning("Step will be SKIPPED.") return None else: bkg_mask = np.ones(input_model.data.shape, dtype=bool) - # Compute the mean values of science image and background reference # image, including only regions where there are no identified sources. # Exclude pixel values in the lower and upper 25% of the histogram. diff --git a/jwst/background/tests/test_background.py b/jwst/background/tests/test_background.py index 7e895b7ddd..00b04c93a8 100644 --- a/jwst/background/tests/test_background.py +++ b/jwst/background/tests/test_background.py @@ -9,11 +9,13 @@ from numpy.testing import assert_allclose from stdatamodels.jwst import datamodels +from stdatamodels.jwst.datamodels.dqflags import pixel from jwst.assign_wcs import AssignWcsStep from jwst.background import BackgroundStep from jwst.stpipe import Step -from jwst.background.background_sub import robust_mean, mask_from_source_cat, no_NaN +from jwst.background.background_sub import (robust_mean, mask_from_source_cat, + no_NaN, sufficient_background_pixels) @pytest.fixture(scope="module") @@ -403,3 +405,23 @@ def test_no_nan(): # Make sure arrays are equal. assert np.array_equal(model.data, result.data) + + +def test_sufficient_background_pixels(): + model = datamodels.ImageModel(data=np.zeros((2048, 2048)), + dq=np.zeros((2048, 2048))) + refpix_flags = pixel['DO_NOT_USE'] | pixel['REFERENCE_PIXEL'] + model.dq[:4, :] = refpix_flags + model.dq[-4:, :] = refpix_flags + model.dq[:, :4] = refpix_flags + model.dq[:, -4:] = refpix_flags + + bkg_mask = np.ones((2048, 2048), dtype=bool) + # With full array minux refpix available for bkg, should be sufficient + assert sufficient_background_pixels(model.dq, bkg_mask) + + bkg_mask[4: -4, :] = 0 + bkg_mask[:, 4: -4] = 0 + # Now mask out entire array, mocking full source coverage of detector - + # no pixels should be available for bkg + assert not sufficient_background_pixels(model.dq, bkg_mask)