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

JP-3593: Saturation step should increase flagging for group 3 saturation #259

Closed
wants to merge 13 commits into from
5 changes: 4 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ Changes to API
Bug Fixes
---------

-
saturation
~~~~~~~~~~

- Adds a check for saturation bias in group 2 for nframes > 1. [#259]

1.7.1 (2024-05-21)
==================
Expand Down
19 changes: 16 additions & 3 deletions src/stcal/saturation/saturation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@


def flag_saturated_pixels(
data, gdq, pdq, sat_thresh, sat_dq, atod_limit, dqflags, n_pix_grow_sat=1, zframe=None, read_pattern=None
):
data, gdq, pdq, sat_thresh, sat_dq, atod_limit, dqflags,
n_pix_grow_sat=1, zframe=None, read_pattern=None):
"""
Short Summary
-------------
Expand Down Expand Up @@ -53,7 +53,6 @@ def flag_saturated_pixels(
read_pattern : List[List[float or int]] or None
The times or indices of the frames composing each group.


Returns
-------
gdq : int, 4D array
Expand Down Expand Up @@ -85,6 +84,20 @@ def flag_saturated_pixels(
plane = data[ints, group, :, :]

if read_pattern is not None:
hbushouse marked this conversation as resolved.
Show resolved Hide resolved
if group == 2:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this code will work here; it's looking for where SATURATED is set in groupdq for group==2, but the flags aren't passed from flag_temp into groupdq until after this code block executes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flag_temp? I don't see that used anywhere in here. Where is it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, copy-paste issue from my review of the jwst PR. Same general idea though- gdq for the third group isn't set until the code block after all the (if group==2) stuff, so it can never fulfill the criterion of identifying things where saturation was set in the third group.

# Identify groups which we wouldn't expect to saturate by the third group,
# on the basis of the first group
mask = data[ints, 0, ...] / np.mean(read_pattern[0]) * read_pattern[2][-1] < sat_thresh

# Identify groups with suspiciously large values in the second group
mask &= data[ints, 1, ...] > sat_thresh / len(read_pattern[1])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need something to grow this mask by n_pix_grow_sat using the adjacent_pixels routine. For pixels bordering pixels that saturate in group 3, the saturation flag is set too. Those pixels might not be intrinsically bright enough to trigger the 'suspiciously large' criterion, but will likewise be affected by the CR and restricted to only fitting a ramp to groups 1+2.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drlaw1558 thank you for the comments! I'm committing the changes soon. Can I please ask you to take look again when they are in?


# Identify groups that are saturated in the third group
mask &= np.where(gdq[ints, 2, :, :] & saturated, True, False)

# Flag the 2nd group for the pixels passing that gauntlet
gdq[ints, 1][mask] |= saturated
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the "|" (or) operator really work as desired in this case? Because we track flags in individual bits, don't we need to use "np.bitwise_or()" here?


dilution_factor = np.mean(read_pattern[group]) / read_pattern[group][-1]
dilution_factor = np.where(no_sat_check_mask, 1, dilution_factor)
else:
Expand Down
8 changes: 4 additions & 4 deletions tests/test_saturation.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_basic_saturation_flagging():
# Add ramp values up to the saturation limit
data[0, 0, 5, 5] = 0
data[0, 1, 5, 5] = 20000
data[0, 2, 5, 5] = 40000
data[0, 2, 5, 5] = 30000
data[0, 3, 5, 5] = 60000 # Signal reaches saturation limit
data[0, 4, 5, 5] = 62000

Expand Down Expand Up @@ -53,7 +53,7 @@ def test_read_pattern_saturation_flagging():
# Add ramp values up to the saturation limit
data[0, 0, 5, 5] = 0
data[0, 1, 5, 5] = 20000
data[0, 2, 5, 5] = 40000
data[0, 2, 5, 5] = 60000
data[0, 3, 5, 5] = 60000 # Signal reaches saturation limit
data[0, 4, 5, 5] = 62000

Expand Down Expand Up @@ -101,7 +101,7 @@ def test_no_sat_check_at_limit():
sat_dq[5, 5] = DQFLAGS["NO_SAT_CHECK"]

# Run the saturation flagging
gdq, pdq, _ = flag_saturated_pixels(data, gdq, pdq, sat_thresh, sat_dq, ATOD_LIMIT, DQFLAGS, 1)
gdq, pdq, _ = flag_saturated_pixels(data, gdq, pdq, sat_thresh, sat_dq, ATOD_LIMIT, DQFLAGS, n_pix_grow_sat=1)

# Make sure that no groups for the flat-lined pixel and all
# of its neighbors are flagged as saturated.
Expand Down Expand Up @@ -209,7 +209,7 @@ def test_zero_frame():

atod_limit = 65535.0 # Hard DN limit of 16-bit A-to-D converter

gdq, pdq, zframe = flag_saturated_pixels(data, gdq, pdq, ref, rdq, atod_limit, dqflags, 0, zfrm)
gdq, pdq, zframe = flag_saturated_pixels(data, gdq, pdq, ref, rdq, atod_limit, dqflags, n_pix_grow_sat=0, zframe=zfrm)

# Check DQ flags
cdq = np.array([dqflags["SATURATED"]] * ngroups)
Expand Down
Loading