Skip to content

Commit 1272f00

Browse files
committed
JP-3967: SATURATION flag for partially saturated pixels should be propagated to the _rate DQ
C: Remove outdated comment Possible partially saturated ramp flagging. JP-3967 update for pllim (#1) * Undoing test changes in prep to change the C-extension, as well as analyze the test failures in the LIKELY CI tests. * Commenting out a part of a test that fails. The OLS SAT flagging needs to be updated before this test works. * Updated C-extension and tests. * Updating tests for ramp fitting cases. * Removing preprocessing switches and adding a comment, cleaning up the code. * Removing unnecessary comment. * Comments on how to update slope fitter for partial saturation flagging. * Updating the parital flagging for the rate product. Updating ramp fit tests for updating flagging. * Updating partial saturation flagging and tests. DOC: Reword second paragraph. Co-authored-by: Ken MacDonald <[email protected] m> TST: Extend test_ramp_fitting.py to LIKELY algo
1 parent b3416f5 commit 1272f00

File tree

7 files changed

+364
-156
lines changed

7 files changed

+364
-156
lines changed

changes/421.apichange.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Set saturation flag for any saturation. This reverts back to saturation flagging behavior prior to #125

docs/stcal/ramp_fitting/description.rst

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,11 @@ Segment-specific Computations
195195
+++++++++++++++++++++++++++++
196196
The variance of the slope of a segment due to read noise is:
197197

198-
.. math::
198+
.. math::
199199
var^R_{s} = \frac{12 \ R^2 }{ (ngroups_{s}^3 - ngroups_{s})(tgroup^2)(gain^2) } \,,
200200
201-
where :math:`R` is the noise in the difference between 2 frames,
202-
:math:`ngroups_{s}` is the number of groups in the segment, and :math:`tgroup` is the group
201+
where :math:`R` is the noise in the difference between 2 frames,
202+
:math:`ngroups_{s}` is the number of groups in the segment, and :math:`tgroup` is the group
203203
time in seconds (from the keyword TGROUP). The divide by gain converts to
204204
:math:`DN`. For the special case where as segment has length 1, the
205205
:math:`ngroups_{s}` is set to :math:`2`.
@@ -272,7 +272,7 @@ The square-root of the combined variance is stored in the ERR array of the outpu
272272
The overall slope depends on the slope and the combined variance of the slope of each integration's
273273
segments, so is a sum over integration values computed from the segements:
274274

275-
.. math::
275+
.. math::
276276
slope_{o} = \frac{ \sum_{i}{ \frac{slope_{i}} {var^C_{i}}}} { \sum_{i}{ \frac{1} {var^C_{i}}}}
277277
278278
@@ -300,21 +300,20 @@ Data Quality Propagation
300300
For a given pixel, if all groups in an integration are flagged as DO_NOT_USE or
301301
SATURATED, then that pixel will be flagged as DO_NOT_USE in the corresponding
302302
integration in the "rateints" product. Note this does NOT mean that all groups
303-
are flagged as SATURATED, nor that all groups are flagged as DO_NOT_USE. For
303+
are flagged as DO_NOT_USE. For
304304
example, slope calculations that are suppressed due to a ramp containing only
305305
one good group will be flagged as DO_NOT_USE in the
306306
first group, but not necessarily any other group, while only groups two and
307307
beyond are flagged as SATURATED. Further, only if all integrations in the "rateints"
308308
product are flagged as DO_NOT_USE, then the pixel will be flagged as DO_NOT_USE
309309
in the "rate" product.
310310

311-
For a given pixel, if all groups in an integration are flagged as SATURATED,
312-
then that pixel will be flagged as SATURATED and DO_NOT_USE in the corresponding
313-
integration in the "rateints" product. This is different from the above case in
314-
that this is only for all groups flagged as SATURATED, not for some combination
315-
of DO_NOT_USE and SATURATED. Further, only if all integrations in the "rateints"
316-
product are flagged as SATURATED, then the pixel will be flagged as SATURATED
317-
and DO_NOT_USE in the "rate" product.
311+
For a given pixel, if any groups in an integration are flagged as SATURATED,
312+
then that pixel will be flagged as SATURATED in the corresponding
313+
integration in the "rateints" product.
314+
Furthermore, if any integration in the "rateints"
315+
product is flagged as SATURATED, then the pixel will be flagged as SATURATED
316+
in the "rate" product.
318317

319318
For a given pixel, if any group in an integration is flagged as JUMP_DET, then
320319
that pixel will be flagged as JUMP_DET in the corresponding integration in the
@@ -338,12 +337,12 @@ group/resultant directly, the likelihood algorithm is based on differences of
338337
the groups/resultants :math:`d_i = r_i - r_{i-1}`. The model used to determine
339338
the slope/countrate, :math:`a`, is:
340339

341-
.. math::
340+
.. math::
342341
\chi^2 = ({\bf d} - a \cdot {\bf 1})^T C ({\bf d} - a \cdot {\bf 1}) \,,
343342
344-
Differentiating, setting to zero, then solving for :math:`a` results in
343+
Differentiating, setting to zero, then solving for :math:`a` results in
345344

346-
.. math::
345+
.. math::
347346
a = ({\bf 1}^T C {\bf d})({\bf 1}^T C {\bf 1})^T \,,
348347
349348
The covariance matrix :math:`C` is a tridiagonal matrix, due to the nature of the

src/stcal/ramp_fitting/src/slope_fitter.c

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,6 @@
1414
#include <numpy/arrayobject.h>
1515
#include <numpy/npy_math.h>
1616

17-
/*
18-
To build C code, make sure the setup.py file is correct and
19-
lists all extensions, then run:
20-
21-
python setup.py build_ext --inplace
22-
23-
or
24-
25-
pip install -e .
26-
*/
27-
2817
/* ========================================================================= */
2918
/* TYPEDEFs */
3019
/* ------------------------------------------------------------------------- */
@@ -2746,6 +2735,7 @@ ramp_fit_pixel(
27462735
int ret = 0;
27472736
npy_intp integ;
27482737
int sat_cnt = 0, dnu_cnt = 0;
2738+
int set_rate_sat_flag = 0;
27492739

27502740
/* Ramp fitting depends on the averaged median rate for each integration */
27512741
if (compute_median_rate(rd, pr)) {
@@ -2777,20 +2767,28 @@ ramp_fit_pixel(
27772767
dnu_cnt++;
27782768
pr->rateints[integ].slope = NAN;
27792769
}
2770+
2771+
if (rd->save_opt) {
2772+
get_pixel_ramp_integration_segments_and_pedestal(integ, pr, rd);
2773+
}
2774+
27802775
if (pr->rateints[integ].dq & rd->sat) {
27812776
sat_cnt++;
27822777
pr->rateints[integ].slope = NAN;
27832778
}
27842779

2785-
if (rd->save_opt) {
2786-
get_pixel_ramp_integration_segments_and_pedestal(integ, pr, rd);
2780+
// The partial saturation must go here to not mess up pedestal computations
2781+
if (pr->stats[integ].cnt_sat > 0) {
2782+
pr->rateints[integ].dq |= rd->sat;
2783+
set_rate_sat_flag = 1;
27872784
}
27882785
}
27892786

27902787
if (rd->nints == dnu_cnt) {
27912788
pr->rate.dq |= rd->dnu;
27922789
}
2793-
if (rd->nints == sat_cnt) {
2790+
2791+
if (sat_cnt == rd->nints) {
27942792
pr->rate.dq |= rd->sat;
27952793
}
27962794

@@ -2815,6 +2813,11 @@ ramp_fit_pixel(
28152813
pr->rate.var_err = 0.;
28162814
}
28172815

2816+
// Partial saturation flagging must be done here
2817+
if (set_rate_sat_flag) {
2818+
pr->rate.dq |= rd->sat;
2819+
}
2820+
28182821
if (!isnan(pr->rate.slope)) {
28192822
pr->rate.slope = pr->rate.slope / pr->invvar_e_sum;
28202823
}
@@ -2968,6 +2971,7 @@ ramp_fit_pixel_integration(
29682971
goto END;
29692972
}
29702973

2974+
// Whole ramp not usable
29712975
if (rd->ngroups == pr->stats[integ].cnt_dnu_sat) {
29722976
pr->rateints[integ].dq |= rd->dnu;
29732977
if (rd->ngroups == pr->stats[integ].cnt_sat) {

src/stcal/ramp_fitting/utils.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ def dq_compress_sect(ramp_data, num_int, gdq_sect, pixeldq_sect):
111111
gdq0_sat = np.bitwise_and(gdq_sect[0], sat)
112112
pixeldq_sect[gdq0_sat != 0] = np.bitwise_or(pixeldq_sect[gdq0_sat != 0], sat | dnu)
113113

114+
# If saturation occurs mark the appropriate flag.
115+
sat_loc = np.bitwise_and(gdq_sect, sat)
116+
sat_check = np.where(sat_loc.sum(axis=0) > 0)
117+
pixeldq_sect[sat_check] = np.bitwise_or(pixeldq_sect[sat_check], sat)
118+
114119
# If jump occurs mark the appropriate flag.
115120
jump_loc = np.bitwise_and(gdq_sect, jump)
116121
jump_check = np.where(jump_loc.sum(axis=0) > 0)
@@ -144,11 +149,11 @@ def dq_compress_final(dq_int, ramp_data):
144149
dnu = np.uint32(ramp_data.flags_do_not_use)
145150
sat = np.uint32(ramp_data.flags_saturated)
146151

147-
# Remove DO_NOT_USE and SATURATED because they need special handling.
148-
# These flags are not set in the final pixel DQ array by simply being set
152+
# Remove DO_NOT_USE because it needs special handling.
153+
# This flag is not set in the final pixel DQ array by simply being set
149154
# in one of the integrations.
150-
not_sat_or_dnu = np.uint32(~(dnu | sat))
151-
final_dq = np.bitwise_and(final_dq, not_sat_or_dnu)
155+
not_dnu = np.uint32(~dnu)
156+
final_dq = np.bitwise_and(final_dq, not_dnu)
152157

153158
# If all integrations are DO_NOT_USE or SATURATED, then set DO_NOT_USE.
154159
set_if_total_integ(final_dq, dq_int, dnu | sat, dnu)

0 commit comments

Comments
 (0)