diff --git a/CHANGES.rst b/CHANGES.rst index a7a4862cf2..ea538e9885 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -62,10 +62,13 @@ ramp_fitting the various flavors of variance and ERR stored in the output products [#6715] +- Adding feature to turn off calculations of ramps with good 0th group, + but all other groups are saturated. [#6737] + reset ----- -- Reading nints and ngroups from model.meta for reset reference file and data instead of using the +- Read NINTS and NGROUPS from model.meta for reset reference file and data instead of using the shape of the data to define these values [#6749] set_telescope_pointing diff --git a/docs/jwst/ramp_fitting/arguments.rst b/docs/jwst/ramp_fitting/arguments.rst index 768e71e571..1c9b3a2ab1 100644 --- a/docs/jwst/ramp_fitting/arguments.rst +++ b/docs/jwst/ramp_fitting/arguments.rst @@ -11,6 +11,11 @@ The ramp fitting step has three optional arguments that can be set by the user: * ``--int_name``: A string that can be used to override the default name for the per-integration product. +* ``--suppress_one_group``: A boolean to suppress computations for saturated ramps + with good 0th group. The default is set to true to suppress these computations, + which will compute all values for the ramp the same as if the entire ramp were + saturated. + * ``--maximum_cores``: The fraction of available cores that will be used for multi-processing in this step. The default value is 'none' which does not use multi-processing. The other options are 'quarter', 'half', and 'all'. Note that these diff --git a/jwst/ramp_fitting/ramp_fit_step.py b/jwst/ramp_fitting/ramp_fit_step.py index 0263c86842..2d08440ee4 100644 --- a/jwst/ramp_fitting/ramp_fit_step.py +++ b/jwst/ramp_fitting/ramp_fit_step.py @@ -182,6 +182,7 @@ class RampFitStep(Step): int_name = string(default='') save_opt = boolean(default=False) # Save optional output opt_name = string(default='') + suppress_one_group = boolean(default=True) # Suppress saturated ramps with good 0th group maximum_cores = option('none', 'quarter', 'half', 'all', default='none') # max number of processes to create """ @@ -240,7 +241,7 @@ def process(self, input): image_info, integ_info, opt_info, gls_opt_model = ramp_fit.ramp_fit( input_model, buffsize, self.save_opt, readnoise_2d, gain_2d, self.algorithm, - self.weighting, max_cores, dqflags.pixel) + self.weighting, max_cores, dqflags.pixel, suppress_one_group=self.suppress_one_group) # Save the OLS optional fit product, if it exists if opt_info is not None: diff --git a/jwst/ramp_fitting/tests/test_ramp_fit_step.py b/jwst/ramp_fitting/tests/test_ramp_fit_step.py index e0fa3011bb..90940a3ec9 100644 --- a/jwst/ramp_fitting/tests/test_ramp_fit_step.py +++ b/jwst/ramp_fitting/tests/test_ramp_fit_step.py @@ -3,6 +3,7 @@ from jwst.ramp_fitting.ramp_fit_step import RampFitStep +from jwst.datamodels import dqflags from jwst.datamodels import RampModel from jwst.datamodels import GainModel, ReadnoiseModel @@ -246,3 +247,264 @@ def test_int_times2(generate_miri_reffiles, setup_inputs): assert cube_model is not None assert(len(cube_model.int_times) == nints) + + +def one_group_suppressed(nints, suppress, setup_inputs): + """ + Creates three pixel ramps. + The first ramp has no good groups. + The second ramp has one good group. + The third ramp has all good groups. + + Sets up the models to be used by the tests for the one + group suppression flag. + """ + # Define the data. + ngroups, nrows, ncols = 5, 1, 3 + dims = nints, ngroups, nrows, ncols + rnoise, gain = 10, 1 + group_time, frame_time = 5.0, 1 + rampmodel, gdq, rnModel, pixdq, err, gmodel = setup_inputs( + ngroups=ngroups, readnoise=rnoise, nints=nints, nrows=nrows, + ncols=ncols, gain=gain, deltatime=group_time) + + rampmodel.meta.exposure.frame_time = frame_time + + # Setup the ramp data and DQ. + arr = np.array([k + 1 for k in range(ngroups)], dtype=float) + sat = dqflags.pixel["SATURATED"] + sat_dq = np.array([sat] * ngroups, dtype=rampmodel.groupdq.dtype) + zdq = np.array([0] * ngroups, dtype=rampmodel.groupdq.dtype) + + rampmodel.data[0, :, 0, 0] = arr + rampmodel.data[0, :, 0, 1] = arr + rampmodel.data[0, :, 0, 2] = arr + + rampmodel.groupdq[0, :, 0, 0] = sat_dq # All groups sat + rampmodel.groupdq[0, :, 0, 1] = sat_dq # 0th good, all others sat + rampmodel.groupdq[0, 0, 0, 1] = 0 + rampmodel.groupdq[0, :, 0, 2] = zdq # All groups good + + if nints > 1: + rampmodel.data[1, :, 0, 0] = arr + rampmodel.data[1, :, 0, 1] = arr + rampmodel.data[1, :, 0, 2] = arr + + # All good ramps + rampmodel.groupdq[1, :, 0, 0] = zdq + rampmodel.groupdq[1, :, 0, 1] = zdq + rampmodel.groupdq[1, :, 0, 2] = zdq + + rampmodel.suppress_one_group_ramps = suppress + + # Call ramp fit through the step class + slopes, cube_model = RampFitStep.call( + rampmodel, + override_gain=gmodel, + override_readnoise=rnModel, + suppress_one_group=suppress, + maximum_cores="none") + + return slopes, cube_model, dims + + +def test_one_group_not_suppressed_one_integration(setup_inputs): + """ + This tests a one integration with three pixel ramps, with the + one group suppression switch turned off. + 1. The fully saturated ramp should have no computed data with the + DO_NOT_USE flag set in the final DQ. + 2. The one group ramp will have data computed and the SATURATED + flag set in the final DQ. + 3. The good ramp is fully computed. + """ + slopes, cube, dims = one_group_suppressed(1, False, setup_inputs) + nints, ngroups, nrows, ncols = dims + tol = 1e-5 + + # Check slopes information + check = np.array([[0., 1., 1.0000002]]) + np.testing.assert_allclose(slopes.data, check, tol) + + check = np.array([[3, 2, 0]]) + np.testing.assert_allclose(slopes.dq, check, tol) + + check = np.array([[0., 0.04, 0.01]]) + np.testing.assert_allclose(slopes.var_poisson, check, tol) + + check = np.array([[0., 3.9999995, 0.19999999]]) + np.testing.assert_allclose(slopes.var_rnoise, check, tol) + + check = np.array([[0., 2.009975, 0.45825756]]) + np.testing.assert_allclose(slopes.err, check, tol) + + # Check slopes information + check = np.array([[[0., 1., 1.0000001]]]) + np.testing.assert_allclose(cube.data, check, tol) + + check = np.array([[[3, 2, 0]]]) + np.testing.assert_allclose(cube.dq, check, tol) + + check = np.array([[[0., 0.04, 0.01]]]) + np.testing.assert_allclose(cube.var_poisson, check, tol) + + check = np.array([[[0., 3.9999995, 0.19999999]]]) + np.testing.assert_allclose(cube.var_rnoise, check, tol) + + check = np.array([[[0., 2.0099752, 0.4582576]]]) + np.testing.assert_allclose(cube.err, check, tol) + + +def test_one_group_suppressed_one_integration(setup_inputs): + """ + This tests a one integration with three pixel ramps, with the + one group suppression switch turned on. + 1. The fully saturated ramp should have no computed data with the + DO_NOT_USE flag set in the final DQ. + 2. The one group ramp will be computed as a fully saturated ramp, + but the DO_NOT_USE flag will not be set in the final DQ. + 3. The good ramp is fully computed. + """ + slopes, cube, dims = one_group_suppressed(1, True, setup_inputs) + nints, ngroups, nrows, ncols = dims + tol = 1e-5 + + # Check slopes information + check = np.array([[0., 0., 1.0000002]]) + np.testing.assert_allclose(slopes.data, check, tol) + + check = np.array([[3, 3, 0]]) + np.testing.assert_allclose(slopes.dq, check, tol) + + check = np.array([[0., 0., 0.01]]) + np.testing.assert_allclose(slopes.var_poisson, check, tol) + + check = np.array([[0., 0., 0.19999999]]) + np.testing.assert_allclose(slopes.var_rnoise, check, tol) + + check = np.array([[0., 0., 0.45825756]]) + np.testing.assert_allclose(slopes.err, check, tol) + + # Check slopes information + check = np.array([[[0., 0., 1.0000001]]]) + np.testing.assert_allclose(cube.data, check, tol) + + check = np.array([[[3, 3, 0]]]) + np.testing.assert_allclose(cube.dq, check, tol) + + check = np.array([[[0., 0., 0.01]]]) + np.testing.assert_allclose(cube.var_poisson, check, tol) + + check = np.array([[[0., 0., 0.19999999]]]) + np.testing.assert_allclose(cube.var_rnoise, check, tol) + + check = np.array([[[0., 0., 0.4582576]]]) + np.testing.assert_allclose(cube.err, check, tol) + + +def test_one_group_not_suppressed_two_integration(setup_inputs): + """ + This tests three pixel ramps with two integrations and the + one group suppression switch turned off. The second integration + for all ramps are good, so all pixels will have usable data in + the final image model. + 1. A fully saturated first integration. Usable data is obtained + from the second integration. + 2. A one group ramp in the first integration. Data from both + integrations is combeined for the final image model. + 3. Good data from both integrations. + """ + slopes, cube, dims = one_group_suppressed(2, False, setup_inputs) + nints, ngroups, nrows, ncols = dims + tol = 1e-5 + + # Check slopes information + check = np.array([[1.0000001, 1.0000002, 1.0000002]]) + np.testing.assert_allclose(slopes.data, check, tol) + + check = np.array([[2, 2, 0]]) + np.testing.assert_allclose(slopes.dq, check, tol) + + check = np.array([[0.005, 0.008, 0.005]]) + np.testing.assert_allclose(slopes.var_poisson, check, tol) + + check = np.array([[0.19999999, 0.19047618, 0.09999999]]) + np.testing.assert_allclose(slopes.var_rnoise, check, tol) + + check = np.array([[0.45276925, 0.44550666, 0.32403702]]) + np.testing.assert_allclose(slopes.err, check, tol) + + # Check slopes information + check = np.array([[[0., 1., 1.0000001]], + [[1.0000001, 1.0000001, 1.0000001]]]) + np.testing.assert_allclose(cube.data, check, tol) + + check = np.array([[[3, 2, 0]], + [[0, 0, 0]]]) + np.testing.assert_allclose(cube.dq, check, tol) + + check = np.array([[[0., 0.04, 0.01]], + [[0.005, 0.01, 0.01]]]) + np.testing.assert_allclose(cube.var_poisson, check, tol) + + check = np.array([[[0., 3.9999995, 0.19999999]], + [[0.19999999, 0.19999999, 0.19999999]]]) + np.testing.assert_allclose(cube.var_rnoise, check, tol) + + check = np.array([[[0., 2.0099752, 0.4582576]], + [[0.45276922, 0.4582576, 0.4582576]]]) + np.testing.assert_allclose(cube.err, check, tol) + + +def test_one_group_suppressed_two_integration(setup_inputs): + """ + This tests three pixel ramps with two integrations and the + one group suppression switch turned on. The key differences + for this test are in the cube data. + 1. A fully saturated first integration. Usable data is obtained + from the second integration. + 2. A one group ramp in the first integration. No data from the + first integration is used, but DO_NOT_USE flag is not set for + the first integration. + 3. Good data from both integrations. + """ + slopes, cube, dims = one_group_suppressed(2, True, setup_inputs) + nints, ngroups, nrows, ncols = dims + tol = 1e-5 + + # Check slopes information + check = np.array([[1.0000001, 1.0000001, 1.0000002]]) + np.testing.assert_allclose(slopes.data, check, tol) + + check = np.array([[2, 2, 0]]) + np.testing.assert_allclose(slopes.dq, check, tol) + + check = np.array([[0.005, 0.005, 0.005]]) + np.testing.assert_allclose(slopes.var_poisson, check, tol) + + check = np.array([[0.19999999, 0.19999999, 0.09999999]]) + np.testing.assert_allclose(slopes.var_rnoise, check, tol) + + check = np.array([[0.45276925, 0.45276925, 0.32403702]]) + np.testing.assert_allclose(slopes.err, check, tol) + + # Check slopes information + check = np.array([[[0., 0., 1.0000001]], + [[1.0000001, 1.0000001, 1.0000001]]]) + np.testing.assert_allclose(cube.data, check, tol) + + check = np.array([[[3, 3, 0]], + [[0, 0, 0]]]) + np.testing.assert_allclose(cube.dq, check, tol) + + check = np.array([[[0., 0., 0.01]], + [[0.005, 0.005, 0.01]]]) + np.testing.assert_allclose(cube.var_poisson, check, tol) + + check = np.array([[[0., 0., 0.19999999]], + [[0.19999999, 0.19999999, 0.19999999]]]) + np.testing.assert_allclose(cube.var_rnoise, check, tol) + + check = np.array([[[0., 0., 0.4582576]], + [[0.45276922, 0.45276922, 0.4582576]]]) + np.testing.assert_allclose(cube.err, check, tol)