Repository: spacetelescope/jwst

Issue:
Image resample weighting should use new time keyword
_Issue [JP-3465](https://jira.stsci.edu/browse/JP-3465) was created on JIRA by [Tyler Pauly](https://jira.stsci.edu/secure/ViewProfile.jspa?name=tpauly):_

The introduction of the measurement time in JP-3449 will prompt its usage as the correct "exptime" keyword when weighting multiple exposures during resampling. The measurement time defines the interval across which ramp fitting is done, which defines the time relevant for SNR calculations.

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/spacetelescope/jwst/pull/8212?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) Report
Attention: `37 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`c176ff3`)](https://app.codecov.io/gh/spacetelescope/jwst/commit/c176ff3e3c2d59a11dafc2a3eec9ece143cace96?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) 75.24% compared to head [(`ec0c7d8`)](https://app.codecov.io/gh/spacetelescope/jwst/pull/8212?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) 75.18%.
> Report is 1 commits behind head on master.

| [Files](https://app.codecov.io/gh/spacetelescope/jwst/pull/8212?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) | Patch % | Lines |
|---|---|---|
| [jwst/tweakreg/tweakreg\_catalog.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/8212?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC90d2Vha3JlZy90d2Vha3JlZ19jYXRhbG9nLnB5) | 22.91% | [37 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/8212?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #8212      +/-   ##
==========================================
- Coverage   75.24%   75.18%   -0.07%     
==========================================
  Files         470      470              
  Lines       38497    38548      +51     
==========================================
+ Hits        28968    28982      +14     
- Misses       9529     9566      +37     
```

| [Flag](https://app.codecov.io/gh/spacetelescope/jwst/pull/8212/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) | Coverage Δ | | *Carryforward flag |
|---|---|---|---|
| [nightly](https://app.codecov.io/gh/spacetelescope/jwst/pull/8212/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) | `77.37% <ø> (-0.01%)` | :arrow_down: | Carriedforward from [c176ff3](https://app.codecov.io/gh/spacetelescope/jwst/commit/c176ff3e3c2d59a11dafc2a3eec9ece143cace96?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |

*This pull request uses carry forward flags. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/spacetelescope/jwst/pull/8212?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope).

hbushouse: I wonder if we should make an update to the resample/resample_spec docs to just mention which exposure time related keyword is actually used by the step when doing "exptime" weighting. There may not be any mention of it at all in the docs right now, but given how many different flavors of time-related keywords we have in the headers, I think it's worth explicitly saying in the docs which one is used.
emolter: Starting [new regtest run](https://plwishmaster.stsci.edu:8081/job/RT/job/JWST-Developers-Pull-Requests/1262/) after removal of TEXPTIME keyword.
hbushouse: The latest regression test run results look good. All failures are just the absence of TEXPTIME in combined products, as expected, and a few instances of different values for the new TMEASURE keyword. So that looks good.

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 97c2c3507..5cedc0faa 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -111,6 +111,14 @@ outlier_detection
   original input files to accidentally get deleted instead of just the intermediate
   files. [#8263]
 
+resample
+--------
+- Updated exposure time weighting to use the measurement time 
+  (TMEASURE) when available. [#8212]
+
+- Removed product exposure time (``TEXPTIME``) from all computations
+  in the resample step. [#8212]
+
 photom
 ------
 
diff --git a/docs/jwst/resample/arguments.rst b/docs/jwst/resample/arguments.rst
index 748d3071e..1b44f34bd 100644
--- a/docs/jwst/resample/arguments.rst
+++ b/docs/jwst/resample/arguments.rst
@@ -96,7 +96,8 @@ image.
     (VAR_RNOISE) array stored in each input image. If the VAR_RNOISE array does
     not exist, the variance is set to 1 for all pixels (equal weighting).
     If `weight_type=exptime`, the scaling value will be set equal to the
-    exposure time found in the image header.
+    measurement time (TMEASURE) found in the image header if available; 
+    if unavailable, the scaling will be set equal to the exposure time (EFFEXPTM).
 
 ``--single`` (bool, default=False)
     If set to `True`, resample each input image into a separate output.  If
diff --git a/jwst/resample/gwcs_drizzle.py b/jwst/resample/gwcs_drizzle.py
index 8337da121..ef3d0134b 100644
--- a/jwst/resample/gwcs_drizzle.py
+++ b/jwst/resample/gwcs_drizzle.py
@@ -61,8 +61,6 @@ class GWCSDrizzle:
         self.outsci = None
         self.outwht = None
         self.outcon = None
-
-        self.outexptime = 0.0
         self.uniqid = 0
 
         if wt_scl is None:
@@ -79,7 +77,7 @@ class GWCSDrizzle:
 
         out_units = "cps"
 
-        self.outexptime = product.meta.resample.product_exposure_time or 0.0
+        self.outexptime = product.meta.exposure.measurement_time or 0.0
 
         self.outsci = product.data
         if outwcs:
diff --git a/jwst/resample/resample.py b/jwst/resample/resample.py
index fe3b55ec8..a72ebeba0 100644
--- a/jwst/resample/resample.py
+++ b/jwst/resample/resample.py
@@ -476,17 +476,25 @@ class ResampleData:
         total_exposure_time = 0.
         exposure_times = {'start': [], 'end': []}
         duration = 0.0
+        total_measurement_time = 0.0
+        measurement_time_failures = []
         for exposure in self.input_models.models_grouped:
             total_exposure_time += exposure[0].meta.exposure.exposure_time
+            if not resample_utils._check_for_tmeasure(exposure[0]):
+                measurement_time_failures.append(1)
+            else:
+                total_measurement_time += exposure[0].meta.exposure.measurement_time
+                measurement_time_failures.append(0)
             exposure_times['start'].append(exposure[0].meta.exposure.start_time)
             exposure_times['end'].append(exposure[0].meta.exposure.end_time)
             duration += exposure[0].meta.exposure.duration
 
         # Update some basic exposure time values based on output_model
         output_model.meta.exposure.exposure_time = total_exposure_time
+        if not any(measurement_time_failures):
+            output_model.meta.exposure.measurement_time = total_measurement_time
         output_model.meta.exposure.start_time = min(exposure_times['start'])
         output_model.meta.exposure.end_time = max(exposure_times['end'])
-        output_model.meta.resample.product_exposure_time = total_exposure_time
 
         # Update other exposure time keywords:
         # XPOSURE (identical to the total effective exposure time, EFFEXPTM)
diff --git a/jwst/resample/resample_step.py b/jwst/resample/resample_step.py
index a3bde2645..a59b59953 100755
--- a/jwst/resample/resample_step.py
+++ b/jwst/resample/resample_step.py
@@ -200,7 +200,6 @@ class ResampleStep(Step):
 
         return wcs
 
-
     def get_drizpars(self, ref_filename, input_models):
         """
         Extract drizzle parameters from reference file.
diff --git a/jwst/resample/resample_utils.py b/jwst/resample/resample_utils.py
index 68e328760..25c61f838 100644
--- a/jwst/resample/resample_utils.py
+++ b/jwst/resample/resample_utils.py
@@ -182,7 +182,10 @@ def build_driz_weight(model, weight_type=None, good_bits=None):
             inv_variance = 1.0
         result = inv_variance * dqmask
     elif weight_type == 'exptime':
-        exptime = model.meta.exposure.exposure_time
+        if _check_for_tmeasure(model):
+            exptime = model.meta.exposure.measurement_time
+        else:
+            exptime = model.meta.exposure.exposure_time
         result = exptime * dqmask
     else:
         result = np.ones(model.data.shape, dtype=model.data.dtype) * dqmask
@@ -306,3 +309,18 @@ def _resample_range(data_shape, bbox=None):
         ymax = min(data_shape[0] - 1, int(y2 + 0.5))
 
     return xmin, xmax, ymin, ymax
+
+
+def _check_for_tmeasure(model):
+    '''
+    Check if the measurement_time keyword is present in the datamodel
+    for use in exptime weighting. If not, revert to using exposure_time.
+    '''
+    try:
+        tmeasure = model.meta.exposure.measurement_time
+        if tmeasure is not None:
+            return 1
+        else:
+            return 0
+    except AttributeError:
+        return 0


Test patch:
diff --git a/jwst/resample/tests/test_resample_step.py b/jwst/resample/tests/test_resample_step.py
index ad018e112..64bd0e803 100644
--- a/jwst/resample/tests/test_resample_step.py
+++ b/jwst/resample/tests/test_resample_step.py
@@ -76,6 +76,7 @@ def nirspec_rate():
         'duration': 11.805952,
         'end_time': 58119.85416,
         'exposure_time': 11.776,
+        'measurement_time': 11.65824,
         'frame_time': 0.11776,
         'group_time': 0.11776,
         'groupgap': 0,
@@ -129,6 +130,7 @@ def miri_rate():
         'duration': 11.805952,
         'end_time': 58119.85416,
         'exposure_time': 11.776,
+        'measurement_time': 11.65824,
         'frame_time': 0.11776,
         'group_time': 0.11776,
         'groupgap': 0,
@@ -198,6 +200,7 @@ def nircam_rate():
         'duration': 161.05155,
         'end_time': 59512.70899968495,
         'exposure_time': 150.31478,
+        'measurement_time': 139.57801,
         'frame_time': 10.73677,
         'group_time': 21.47354,
         'groupgap': 1,
@@ -319,7 +322,18 @@ def test_weight_type(nircam_rate, _jail):
     result2 = ResampleStep.call(c, weight_type="exptime", blendheaders=False)
 
     assert_allclose(result2.data[100:105, 100:105], 6.667, rtol=1e-2)
-    assert_allclose(result2.wht[100:105, 100:105], 450.9, rtol=1e-1)
+    expectation_value = 407.
+    assert_allclose(result2.wht[100:105, 100:105], expectation_value, rtol=1e-2)
+
+    # remove measurement time to force use of exposure time
+    # this also implicitly shows that measurement time was indeed used above
+    expected_ratio = im1.meta.exposure.exposure_time / im1.meta.exposure.measurement_time
+    for im in c:
+        del im.meta.exposure.measurement_time
+
+    result3 = ResampleStep.call(c, weight_type="exptime", blendheaders=False)
+    assert_allclose(result3.data[100:105, 100:105], 6.667, rtol=1e-2)
+    assert_allclose(result3.wht[100:105, 100:105], expectation_value * expected_ratio, rtol=1e-2)
 
 
 def test_sip_coeffs_do_not_propagate(nircam_rate):
diff --git a/jwst/resample/tests/test_utils.py b/jwst/resample/tests/test_utils.py
index 3ef18cbc6..af41f9730 100644
--- a/jwst/resample/tests/test_utils.py
+++ b/jwst/resample/tests/test_utils.py
@@ -118,7 +118,7 @@ def test_build_driz_weight(weight_type):
     """Check that correct weight map is returned of different weight types"""
     model = ImageModel((10, 10))
     model.dq[0] = DO_NOT_USE
-    model.meta.exposure.exposure_time = 10.0
+    model.meta.exposure.measurement_time = 10.0
     model.var_rnoise += 0.1
 
     weight_map = build_driz_weight(model, weight_type=weight_type, good_bits="GOOD")