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-3630: Add slit-specific processing metadata #9254

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/9254.barshadow.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
For NIRSpec multislit data, add a metadata keyword to each slit to record whether it has been barshadow corrected.
1 change: 1 addition & 0 deletions changes/9254.pathloss.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
For NIRSpec multislit data, add a metadata keyword to each slit to record the pathloss correction type applied (POINT or UNIFORM).
1 change: 1 addition & 0 deletions changes/9254.wavecorr.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
For NIRSpec multislit data, add a metadata keyword to each slit to record whether it has been wavelength corrected.
5 changes: 5 additions & 0 deletions docs/jwst/barshadow/description.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,8 @@ Output product
The output is a new copy of the input `~jwst.datamodels.MultiSlitModel`, with the
corrections applied to the slit data arrays. The 2-D correction array for each slit
is also added to the datamodel in the "BARSHADOW" extension.

Upon successful completion of the step, the status keyword "S_BARSHA"
in the primary header is set to "COMPLETE". For each SCI extension, the "BARSHDW"
keyword is set to True if the slit was barshadow corrected (it is an extended
source) or False if it was not corrected (it is a point source).
6 changes: 6 additions & 0 deletions docs/jwst/pathloss/description.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ Like for the point source case, the 1-d arrays of pathloss correction and wavele
are used to interpolate the correction for each pixel in the science data, using the
wavelength of each pixel to interpolate into the pathloss correction array.

Upon successful completion of the step, the status keyword "S_PTHLOS"
in the primary header is set to "COMPLETE". For each SCI extension, the "PTHLOSS"
keyword is set to "POINT" if the point source correction type was applied to the
slit data. It is set to "UNIFORM" if the uniform correction type was applied.
The keyword is not set if no correction was applied.

MIRI LRS
++++++++
The algorithm for MIRI LRS mode is largely the same as that for NIRSpec described
Expand Down
10 changes: 6 additions & 4 deletions docs/jwst/wavecorr/description.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ The wavelength correction (``wavecorr``) step in the
assignments for NIRSpec fixed-slit (FS) and MOS point sources that are
known to be off center (in the dispersion direction) in their slit.

Upon successful completion of the step, the status keyword "S_WAVCOR"
in the primary header is set to "COMPLETE". For each SCI extension, the "WAVECOR"
keyword is set to True if the slit was wavelength corrected (it is a point
source) or False if it was not corrected (it is a point source).

NIRSpec MOS
-----------
For NIRSpec MOS exposures (EXP_TYPE="NRS_MSASPEC"), wavelength
Expand Down Expand Up @@ -47,7 +52,4 @@ target coordinates and aperture reference point during the
:ref:`extract_2d <extract_2d_step>` step. This estimated position (in the
dispersion direction) is used in the same manner as described above
for MOS slitlets to update the slit WCS and compute corrected wavelengths
for the primary slit only.

Upon successful completion of the step, the status keyword "S_WAVCOR"
is set to "COMPLETE".
for the primary slit only.
17 changes: 16 additions & 1 deletion jwst/barshadow/bar_shadow.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,20 @@ def do_correction(
correction = _calc_correction(slitlet, barshadow_model, source_type)
corrections.slits.append(correction)

if correction is None:
# For point sources, there is no real correction applied.
# Record the correction status as False in this case.
slitlet.barshadow_corrected = False

# Store a blank barshadow image
slitlet.barshadow = np.ones_like(slitlet.data)

# No further processing needed
continue

# Otherwise, record the correction status as True.
slitlet.barshadow_corrected = True

# Apply the correction by dividing into the science and uncertainty arrays:
# var_poisson and var_rnoise are divided by correction**2,
# because they're variance, while err is standard deviation
Expand Down Expand Up @@ -118,7 +132,7 @@ def _calc_correction(slitlet, barshadow_model, source_type):
slitlet_number = slitlet.slitlet_id

# Correction only applies to extended/uniform sources
correction = datamodels.SlitModel(data=np.ones(slitlet.data.shape))
correction = None
if not has_uniform_source(slitlet, source_type):
log.info(f"Bar shadow correction skipped for slitlet {slitlet_number} (source not uniform)")
return correction
Expand Down Expand Up @@ -171,6 +185,7 @@ def _calc_correction(slitlet, barshadow_model, source_type):
wcol = (wavelength - w0) / wave_increment

# Interpolate the bar shadow correction for non-Nan pixels
correction = datamodels.SlitModel()
correction.data = ndimage.map_coordinates(shadow, [yrow, wcol], cval=np.nan, order=1)

return correction
Expand Down
26 changes: 18 additions & 8 deletions jwst/barshadow/tests/test_barshadow_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ def log_watcher(monkeypatch):
return watcher


@pytest.fixture(scope="module")
def nirspec_mos_model():
def create_nirspec_mos_model():
hdul = create_nirspec_mos_file()
msa_meta = os.path.join(
jwst.__path__[0], *["assign_wcs", "tests", "data", "msa_configuration.fits"]
)
hdul[0].header["MSAMETFL"] = msa_meta
hdul[0].header["MSAMETID"] = 12
im = datamodels.ImageModel(hdul)
hdul.close()

im.data = np.full((2048, 2048), 1.0)
im_wcs = AssignWcsStep.call(im)
im_ex2d = Extract2dStep.call(im_wcs)
Expand All @@ -43,17 +44,19 @@ def nirspec_mos_model():
im_ex2d.slits[0].var_poisson = im_ex2d.slits[0].data * 0.01
im_ex2d.slits[0].var_flat = im_ex2d.slits[0].data * 0.01

yield im_ex2d
im_ex2d.close()
im_wcs.close()
im.close()
hdul.close()
return im_ex2d


@pytest.fixture(scope="module")
def nirspec_mos_model():
return create_nirspec_mos_model()


def test_barshadow_step(nirspec_mos_model):
model = nirspec_mos_model.copy()
result = BarShadowStep.call(model)
assert result.meta.cal_step.barshadow == "COMPLETE"
assert result.slits[0].barshadow_corrected is True

# 5 shutter slitlet, correction should not be uniform
shadow = result.slits[0].barshadow
Expand Down Expand Up @@ -95,9 +98,13 @@ def test_barshadow_step_zero_length(nirspec_mos_model, log_watcher):
result = BarShadowStep.call(model)
log_watcher.assert_seen()

# correction ran, but is all 1s
# correction step ran, but is all 1s
assert result.meta.cal_step.barshadow == "COMPLETE"
assert np.all(result.slits[0].barshadow == 1)

# correction status is False
assert result.slits[0].barshadow_corrected is False

result.close()


Expand All @@ -112,6 +119,7 @@ def test_barshadow_step_not_uniform(nirspec_mos_model, log_watcher):
# correction ran, but is all 1s
assert result.meta.cal_step.barshadow == "COMPLETE"
assert np.all(result.slits[0].barshadow == 1)
assert result.slits[0].barshadow_corrected is False
result.close()


Expand All @@ -126,6 +134,7 @@ def test_barshadow_no_reffile(monkeypatch, nirspec_mos_model):
# correction did not run
assert result.meta.cal_step.barshadow == "SKIPPED"
assert result.slits[0].barshadow.size == 0
assert result.slits[0].barshadow_corrected is None
result.close()


Expand All @@ -138,6 +147,7 @@ def test_barshadow_wrong_exptype():
# correction did not run
assert result.meta.cal_step.barshadow == "SKIPPED"
assert result.slits[0].barshadow.size == 0
assert result.slits[0].barshadow_corrected is None

result.close()

Expand Down
3 changes: 3 additions & 0 deletions jwst/extract_1d/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,9 @@ def copy_keyword_info(slit, slitname, spec):
"quadrant",
"slit_xscale",
"slit_yscale",
"wavelength_corrected",
"pathloss_correction_type",
"barshadow_corrected",
]
for key in copy_populated_attributes:
if getattr(slit, key, None) is not None:
Expand Down
3 changes: 3 additions & 0 deletions jwst/extract_1d/tests/test_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@ def test_copy_keyword_info(mock_nirspec_fs_one_slit, mock_one_spec):
"source_ra": 10.0,
"source_dec": 10.0,
"shutter_state": "x",
"wavelength_corrected": True,
"pathloss_correction_type": "POINT",
"barshadow_corrected": False
}
for key, value in expected.items():
setattr(mock_nirspec_fs_one_slit, key, value)
Expand Down
68 changes: 44 additions & 24 deletions jwst/pathloss/pathloss.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,6 @@
log.warning("Forcing of source type with NIS_SOSS is not implemented. Skipping")
output_model.meta.cal_step.pathloss = "SKIPPED"
corrections = None
elif inverse:
log.warning("Use of inversion with NIS_SOSS is not implemented. Skipping")
output_model.meta.cal_step.pathloss = "SKIPPED"
corrections = None
elif source_type is not None:
log.warning("Forcing of source type with NIS_SOSS is not implemented. Skipping")
output_model.meta.cal_step.pathloss = "SKIPPED"
corrections = None
else:
corrections = do_correction_soss(output_model, pathloss_model)

Expand Down Expand Up @@ -584,15 +576,21 @@

if not inverse:
slit.data /= correction.data
slit.err /= correction.data
slit.var_poisson /= correction.data**2
slit.var_rnoise /= correction.data**2
if slit.var_flat is not None and np.size(slit.var_flat) > 0:
slit.var_flat /= correction.data**2
else:
slit.data *= correction.data
slit.err /= correction.data
slit.var_poisson /= correction.data**2
slit.var_rnoise /= correction.data**2
if slit.var_flat is not None and np.size(slit.var_flat) > 0:
slit.var_flat /= correction.data**2
slit.err *= correction.data
slit.var_poisson *= correction.data**2
slit.var_rnoise *= correction.data**2
if slit.var_flat is not None and np.size(slit.var_flat) > 0:
slit.var_flat *= correction.data**2
slit.pathloss_point = correction.pathloss_point
slit.pathloss_uniform = correction.pathloss_uniform
slit.pathloss_correction_type = correction.pathloss_correction_type

# Make sure all NaNs and flags match up in the output slit model
match_nans_and_flags(slit)
Expand Down Expand Up @@ -651,15 +649,22 @@

if not inverse:
slit.data /= correction.data
slit.err /= correction.data
slit.var_poisson /= correction.data**2
slit.var_rnoise /= correction.data**2
if slit.var_flat is not None and np.size(slit.var_flat) > 0:
slit.var_flat /= correction.data**2
else:
slit.data *= correction.data
slit.err /= correction.data
slit.var_poisson /= correction.data**2
slit.var_rnoise /= correction.data**2
if slit.var_flat is not None and np.size(slit.var_flat) > 0:
slit.var_flat /= correction.data**2
slit.err *= correction.data
slit.var_poisson *= correction.data**2
slit.var_rnoise *= correction.data**2
if slit.var_flat is not None and np.size(slit.var_flat) > 0:
slit.var_flat *= correction.data**2

slit.pathloss_point = correction.pathloss_point
slit.pathloss_uniform = correction.pathloss_uniform
slit.pathloss_correction_type = correction.pathloss_correction_type

# Make sure all NaNs and flags match up in the output slit model
match_nans_and_flags(slit)
Expand Down Expand Up @@ -705,15 +710,21 @@

if not inverse:
data.data /= correction.data
data.err /= correction.data
data.var_poisson /= correction.data**2
data.var_rnoise /= correction.data**2
if data.var_flat is not None and np.size(data.var_flat) > 0:
data.var_flat /= correction.data**2

Check warning on line 717 in jwst/pathloss/pathloss.py

View check run for this annotation

Codecov / codecov/patch

jwst/pathloss/pathloss.py#L713-L717

Added lines #L713 - L717 were not covered by tests
else:
data.data *= correction.data
data.err /= correction.data
data.var_poisson /= correction.data**2
data.var_rnoise /= correction.data**2
if data.var_flat is not None and np.size(data.var_flat) > 0:
data.var_flat /= correction.data**2
data.err *= correction.data
data.var_poisson *= correction.data**2
data.var_rnoise *= correction.data**2
if data.var_flat is not None and np.size(data.var_flat) > 0:
data.var_flat *= correction.data**2

Check warning on line 724 in jwst/pathloss/pathloss.py

View check run for this annotation

Codecov / codecov/patch

jwst/pathloss/pathloss.py#L720-L724

Added lines #L720 - L724 were not covered by tests
data.pathloss_point = correction.pathloss_point
data.pathloss_uniform = correction.pathloss_uniform
data.pathloss_correction_type = correction.pathloss_correction_type

Check warning on line 727 in jwst/pathloss/pathloss.py

View check run for this annotation

Codecov / codecov/patch

jwst/pathloss/pathloss.py#L727

Added line #L727 was not covered by tests

# This might be useful to other steps
data.wavelength = correction.wavelength
Expand Down Expand Up @@ -962,14 +973,18 @@
# Use the appropriate correction for this slit
if is_pointsource(source_type or slit.source_type):
pathloss_2d = pathloss_2d_ps
correction_type = "POINT"
else:
pathloss_2d = pathloss_2d_un
correction_type = "UNIFORM"

# Save the corrections. The `data` portion is the correction used.
# The individual ones will be saved in the respective attributes.
correction = datamodels.SlitModel(data=pathloss_2d)
correction.pathloss_point = pathloss_2d_ps
correction.pathloss_uniform = pathloss_2d_un
correction.pathloss_correction_type = correction_type

else:
log.warning("Source is outside slit.")
else:
Expand Down Expand Up @@ -1046,7 +1061,7 @@
)

pathloss_2d = pathloss_2d_ps

correction_type = "POINT"
else:
wavelength_array = slit.wavelength

Expand All @@ -1061,12 +1076,14 @@
)

pathloss_2d = pathloss_2d_un
correction_type = "UNIFORM"

# Save the corrections. The `data` portion is the correction used.
# The individual ones will be saved in the respective attributes.
correction = datamodels.SlitModel(data=pathloss_2d)
correction.pathloss_point = pathloss_2d_ps
correction.pathloss_uniform = pathloss_2d_un
correction.pathloss_correction_type = correction_type

else:
log.warning(
Expand Down Expand Up @@ -1150,15 +1167,18 @@
# Use the appropriate correction for the source type
if is_pointsource(source_type or data.meta.target.source_type):
pathloss_2d = pathloss_2d_ps
correction_type = "POINT"

Check warning on line 1170 in jwst/pathloss/pathloss.py

View check run for this annotation

Codecov / codecov/patch

jwst/pathloss/pathloss.py#L1170

Added line #L1170 was not covered by tests
else:
pathloss_2d = pathloss_2d_un
correction_type = "UNIFORM"

Check warning on line 1173 in jwst/pathloss/pathloss.py

View check run for this annotation

Codecov / codecov/patch

jwst/pathloss/pathloss.py#L1173

Added line #L1173 was not covered by tests

# Save the corrections. The `data` portion is the correction used.
# The individual ones will be saved in the respective attributes.
correction = type(data)(data=pathloss_2d)
correction.pathloss_point = pathloss_2d_ps
correction.pathloss_uniform = pathloss_2d_un
correction.wavelength = wavelength_array
correction.pathloss_correction_type = correction_type

Check warning on line 1181 in jwst/pathloss/pathloss.py

View check run for this annotation

Codecov / codecov/patch

jwst/pathloss/pathloss.py#L1181

Added line #L1181 was not covered by tests

return correction

Expand Down
Loading
Loading