Skip to content

Commit

Permalink
Merge branch 'main' into jp3858-white_light
Browse files Browse the repository at this point in the history
  • Loading branch information
penaguerrero authored Mar 7, 2025
2 parents ebbd168 + a5a4482 commit 11531aa
Show file tree
Hide file tree
Showing 24 changed files with 491 additions and 177 deletions.
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ repos:
jwst/spectral_leak/.* |
jwst/srctype/.* |
jwst/straylight/.* |
jwst/superbias/.* |
jwst/tests/.* |
jwst/tso_photometry/.* |
jwst/wfs_combine/.* |
Expand Down
2 changes: 0 additions & 2 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ exclude = [
"jwst/spectral_leak/**.py",
"jwst/srctype/**.py",
"jwst/straylight/**.py",
"jwst/superbias/**.py",
"jwst/tso_photometry/**.py",
"jwst/wfs_combine/**.py",
]
Expand Down Expand Up @@ -146,6 +145,5 @@ ignore-fully-untyped = true # Turn off annotation checking for fully untyped co
"jwst/spectral_leak/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/srctype/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/straylight/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/superbias/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/tso_photometry/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/wfs_combine/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
1 change: 1 addition & 0 deletions changes/9073.extract_1d.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
For MIRI MRS data added residual fringe corrected columns to extracted spectrum table
1 change: 1 addition & 0 deletions changes/9193.assign_wcs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix MIRI LRS s_region in assign_wcs
1 change: 1 addition & 0 deletions changes/9193.resample.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix MIRI LRS s_region and WCS in resample_spec
17 changes: 13 additions & 4 deletions docs/jwst/extract_1d/description.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ be an output table extension with the name EXTRACT1D. This extension will
have columns WAVELENGTH, FLUX, FLUX_ERROR, FLUX_VAR_POISSON, FLUX_VAR_RNOISE,
FLUX_VAR_FLAT, SURF_BRIGHT, SB_ERROR, SB_VAR_POISSON, SB_VAR_RNOISE,
SB_VAR_FLAT, DQ, BACKGROUND, BKGD_ERROR, BKGD_VAR_POISSON, BKGD_VAR_RNOISE,
BKGD_VAR_FLAT and NPIXELS. Some metadata for the slit will be written to the header for
BKGD_VAR_FLAT and NPIXELS. In the case of MIRI MRS data there are three additional
columns in the output table: RF_FLUX, RF_SURF_BRIGHT, and RF_BACKGROUND.
For more details on the MIRI MRS extracted data see :ref:`MIRI-MRS-1D-residual-fringe`.

Some metadata for the slit will be written to the header for
the table extension, mostly copied from the input SCI extension headers.

For slit-like modes, the extraction region is
Expand Down Expand Up @@ -423,10 +427,15 @@ can be applied to the flux-calibrated detector data in the :ref:`residual_fringe
is part of the :ref:`calwebb_spec2 <calwebb_spec2>` pipeline, but currently it is skipped by default. For more
information see :ref:`residual_fringe <residual_fringe_step>`.

The pipeline also can apply a 1-D residual fringe correction. This correction is only relevant for MIRI MRS data and
can be turned on by setting the optional parameter `ifu_rfcorr = True` in the ``extract_1d`` step.
The pipeline also can apply a 1-D residual fringe correction. This correction is only relevant for MIRI MRS
single band data. The parameter controlling applying the residual fringe correction is by default set to true,
`ifu_rfcorr = True`, in the ``extract_1d`` step.
Empirically, the 1-D correction step has been found to work better than the 2-D correction step if it is
applied to per-band spectra.
applied to per-band spectra. If the MIRI MRS data is from multiple bands/channels the residual fringe correction
is turned off. Three additional columns are present in MIRI MRS extracted spectra: RF_FLUX, RF_SURF_BRIGHT, and
RF_BACKGROUND. These three columns are the flux, surface brightness and background arrays with the residiual
fringe correction applied. If the data is not from a single band or the residual fringe correction fails
NaN values are reported for the arrays.

When using the `ifu_rfcorr` option in the ``extract_1d`` step to apply a 1-D residual fringe
correction, it is applied during the extraction of spectra from the IFU cube. The 1D residual fringe code can also
Expand Down
12 changes: 9 additions & 3 deletions jwst/assign_wcs/assign_wcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import importlib
from gwcs.wcs import WCS
from .util import (update_s_region_spectral, update_s_region_imaging,
update_s_region_nrs_ifu, update_s_region_mrs)
update_s_region_nrs_ifu, update_s_region_mrs,
update_s_region_lrs)
from ..lib.exposure_types import IMAGING_TYPES, SPEC_TYPES, NRS_LAMP_MODE_SPEC_TYPES
from ..lib.dispaxis import get_dispersion_direction
from ..lib.wcs_utils import get_wavelengths
Expand Down Expand Up @@ -72,8 +73,13 @@ def load_wcs(input_model, reference_files={}, nrs_slit_y_range=None):

if output_model.meta.exposure.type.lower() not in exclude_types:
imaging_types = IMAGING_TYPES.copy()
imaging_types.update(['mir_lrs-fixedslit', 'mir_lrs-slitless'])
if output_model.meta.exposure.type.lower() in imaging_types:
imaging_types.update(['mir_lrs-slitless'])
imaging_lrs_types = ['mir_lrs-fixedslit']
if output_model.meta.exposure.type.lower() in imaging_lrs_types:
# uses slits corners in V2, V3 that are read in from the
# lrs specwcs reference file
update_s_region_lrs(output_model, reference_files)
elif output_model.meta.exposure.type.lower() in imaging_types:
try:
update_s_region_imaging(output_model)
except Exception as exc:
Expand Down
68 changes: 28 additions & 40 deletions jwst/assign_wcs/miri.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
from astropy.modeling import models
from astropy import coordinates as coord
from astropy import units as u
from astropy.io import fits

from scipy.interpolate import UnivariateSpline
import gwcs.coordinate_frames as cf
from gwcs import selector

from stdatamodels.jwst.datamodels import (DistortionModel, FilteroffsetModel,
DistortionMRSModel, WavelengthrangeModel,
RegionsModel, SpecwcsModel)
RegionsModel, SpecwcsModel, MiriLRSSpecwcsModel)
from stdatamodels.jwst.transforms.models import (MIRI_AB2Slice, IdealToV2V3)

from . import pointing
Expand Down Expand Up @@ -239,7 +237,6 @@ def lrs_xytoabl(input_model, reference_files):
the "specwcs" and "distortion" reference files.
"""

# subarray to full array transform
subarray2full = subarray_transform(input_model)

Expand All @@ -253,19 +250,13 @@ def lrs_xytoabl(input_model, reference_files):
else:
subarray_dist = distortion

ref = fits.open(reference_files['specwcs'])

with ref:
lrsdata = np.array([d for d in ref[1].data])
# Get the zero point from the reference data.
# The zero_point is X, Y (which should be COLUMN, ROW)
# These are 1-indexed in CDP-7 (i.e., SIAF convention) so must be converted to 0-indexed
if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit':
zero_point = ref[0].header['imx'] - 1, ref[0].header['imy'] - 1
elif input_model.meta.exposure.type.lower() == 'mir_lrs-slitless':
zero_point = ref[0].header['imxsltl'] - 1, ref[0].header['imysltl'] - 1
# Transform to slitless subarray from full array
zero_point = subarray2full.inverse(zero_point[0], zero_point[1])
refmodel = MiriLRSSpecwcsModel(reference_files['specwcs'])
if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit':
zero_point = refmodel.meta.x_ref - 1, refmodel.meta.y_ref - 1
elif input_model.meta.exposure.type.lower() == 'mir_lrs-slitless':
zero_point = refmodel.meta.x_ref_slitless - 1, refmodel.meta.y_ref_slitless - 1
# Transform to slitless subarray from full array
zero_point = subarray2full.inverse(zero_point[0], zero_point[1])

# Figure out the typical along-slice pixel scale at the center of the slit
v2_cen, v3_cen = subarray_dist(zero_point[0], zero_point[1])
Expand All @@ -276,14 +267,14 @@ def lrs_xytoabl(input_model, reference_files):
# centroid trace along the detector in pixels relative to nominal location.
# x0,y0(ul) x1,y1 (ur) x2,y2(lr) x3,y3(ll) define corners of the box within which the distortion
# and wavelength calibration was derived
xcen = lrsdata[:, 0]
ycen = lrsdata[:, 1]
wavetab = lrsdata[:, 2]
x0 = lrsdata[:, 3]
y0 = lrsdata[:, 4]
x1 = lrsdata[:, 5]
y2 = lrsdata[:, 8]

xcen = refmodel.wavetable.x_center
ycen = refmodel.wavetable.y_center
wavetab = refmodel.wavetable.wavelength
x0 = refmodel.wavetable.x0
y0 = refmodel.wavetable.y0
x1 = refmodel.wavetable.x1
y2 = refmodel.wavetable.y2
refmodel.close()
# If in fixed slit mode, define the bounding box using the corner locations provided in
# the CDP reference file.
if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit':
Expand Down Expand Up @@ -313,7 +304,6 @@ def lrs_xytoabl(input_model, reference_files):
# This function will give slit dX as a function of Y subarray pixel value
dxmodel = models.Tabular1D(lookup_table=xshiftref, points=ycen_subarray, name='xshiftref',
bounds_error=False, fill_value=np.nan)

if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit':
bb_sub = (bb_sub[0], (dxmodel.points[0].min(), dxmodel.points[0].max()))
# Fit for the wavelength as a function of Y
Expand All @@ -325,7 +315,6 @@ def lrs_xytoabl(input_model, reference_files):
# This model will now give the wavelength corresponding to a given Y subarray pixel value
wavemodel = models.Tabular1D(lookup_table=wavereference, points=ycen_subarray, name='waveref',
bounds_error=False, fill_value=np.nan)

# Wavelength barycentric correction
try:
velosys = input_model.meta.wcsinfo.velosys
Expand Down Expand Up @@ -383,6 +372,7 @@ def lrs_xytoabl(input_model, reference_files):

return dettoabl


def lrs_abltov2v3l(input_model, reference_files):
"""
The second part of LRS-FIXEDSLIT and LRS-SLITLESS WCS pipeline.
Expand All @@ -405,19 +395,16 @@ def lrs_abltov2v3l(input_model, reference_files):
else:
subarray_dist = distortion

ref = fits.open(reference_files['specwcs'])

with ref:
# Get the zero point from the reference data.
# The zero_point is X, Y (which should be COLUMN, ROW)
# These are 1-indexed in CDP-7 (i.e., SIAF convention) so must be converted to 0-indexed
if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit':
zero_point = ref[0].header['imx'] - 1, ref[0].header['imy'] - 1
elif input_model.meta.exposure.type.lower() == 'mir_lrs-slitless':
zero_point = ref[0].header['imxsltl'] - 1, ref[0].header['imysltl'] - 1
# Transform to slitless subarray from full array
zero_point = subarray2full.inverse(zero_point[0], zero_point[1])

refmodel = MiriLRSSpecwcsModel(reference_files['specwcs'])
if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit':
zero_point = refmodel.meta.x_ref - 1, refmodel.meta.y_ref - 1
elif input_model.meta.exposure.type.lower() == 'mir_lrs-slitless':
zero_point = refmodel.meta.x_ref_slitless - 1, \
refmodel.meta.y_ref_slitless - 1
# Transform to slitless subarray from full array
zero_point = subarray2full.inverse(zero_point[0], zero_point[1])

refmodel.close()
# Figure out the typical along-slice pixel scale at the center of the slit
v2_cen, v3_cen = subarray_dist(zero_point[0], zero_point[1])
v2_off, v3_off = subarray_dist(zero_point[0] + 1, zero_point[1])
Expand Down Expand Up @@ -447,6 +434,7 @@ def lrs_abltov2v3l(input_model, reference_files):

return abl_to_v2v3l


def ifu(input_model, reference_files):
"""
The MIRI MRS WCS pipeline.
Expand Down
51 changes: 49 additions & 2 deletions jwst/assign_wcs/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
from stpipe.exceptions import StpipeExitException
from stcal.alignment.util import compute_s_region_keyword, compute_s_region_imaging

from stdatamodels.jwst.datamodels import WavelengthrangeModel
from stdatamodels.jwst.datamodels import WavelengthrangeModel, MiriLRSSpecwcsModel
from stdatamodels.jwst.transforms.models import GrismObject

from ..lib.catalog_utils import SkyObject
from jwst.lib.catalog_utils import SkyObject


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -807,6 +807,51 @@ def update_s_region_imaging(model):
model.meta.wcsinfo.s_region = s_region


def update_s_region_lrs(model, reference_files):
"""
Update ``S_REGION`` using V2,V3 of the slit corners from reference file.
s_region for model is updated in place.
Parameters
----------
model : DataModel
Input model
reference_files : list
List of reference files for assign_wcs.
"""
refmodel = MiriLRSSpecwcsModel(reference_files['specwcs'])

v2vert1 = refmodel.meta.v2_vert1
v2vert2 = refmodel.meta.v2_vert2
v2vert3 = refmodel.meta.v2_vert3
v2vert4 = refmodel.meta.v2_vert4

v3vert1 = refmodel.meta.v3_vert1
v3vert2 = refmodel.meta.v3_vert2
v3vert3 = refmodel.meta.v3_vert3
v3vert4 = refmodel.meta.v3_vert4

refmodel.close()
v2 = [v2vert1, v2vert2, v2vert3, v2vert4]
v3 = [v3vert1, v3vert2, v3vert3, v3vert4]

if (any(elem is None for elem in v2) or
any(elem is None for elem in v3)):
log.info("The V2,V3 coordinates of the MIRI LRS-Fixed slit contains NaN values.")
log.info("The s_region will not be updated")

lam = 7.0 # wavelength does not matter for s region so just assign a value in range of LRS
s = model.meta.wcs.transform('v2v3', 'world', v2, v3, lam)
a = s[0]
b = s[1]
footprint = np.array([[a[0], b[0]],
[a[1], b[1]],
[a[2], b[2]],
[a[3], b[3]]])

update_s_region_keyword(model, footprint)

def compute_footprint_spectral(model):
"""
Determine spatial footprint for spectral observations using the instrument model.
Expand Down Expand Up @@ -844,9 +889,11 @@ def compute_footprint_spectral(model):
return footprint, (lam_min, lam_max)



def update_s_region_spectral(model):
""" Update the S_REGION keyword.
"""

footprint, spectral_region = compute_footprint_spectral(model)
update_s_region_keyword(model, footprint)
model.meta.wcsinfo.spectral_region = spectral_region
Expand Down
49 changes: 47 additions & 2 deletions jwst/extract_1d/extract_1d_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class Extract1dStep(Step):
and maximize the quality of the 1d spectrum. Used for IFU mode only.
ifu_rfcorr : bool
Switch to select whether or not to apply a 1d residual fringe correction
for MIRI MRS IFU spectra. Default is False.
for MIRI MRS IFU spectra. Default is True.
ifu_set_srctype : str
For MIRI MRS IFU data override srctype and set it to either POINT or EXTENDED.
ifu_rscale : float
Expand Down Expand Up @@ -170,7 +170,7 @@ class Extract1dStep(Step):
center_xy = float_list(min=2, max=2, default=None) # IFU extraction x/y center
ifu_autocen = boolean(default=False) # Auto source centering for IFU point source data.
bkg_sigma_clip = float(default=3.0) # background sigma clipping threshold for IFU
ifu_rfcorr = boolean(default=False) # Apply 1d residual fringe correction
ifu_rfcorr = boolean(default=True) # Apply 1d residual fringe correction (MIRI MRS only)
ifu_set_srctype = option("POINT", "EXTENDED", None, default=None) # user-supplied source type
ifu_rscale = float(default=None, min=0.5, max=3) # Radius in terms of PSF FWHM to scale extraction radii
ifu_covar_scale = float(default=1.0) # Scaling factor to apply to errors to account for IFU cube covariance
Expand Down Expand Up @@ -342,6 +342,36 @@ def _extract_soss(self, model):

return result

def _check_mrs_type(self, model):
"""
Check if the MIRI MRS data is for a single band.
Parameters
----------
model : DataModel
Input model.
Returns
-------
band_check : bool
Flag is data is for a single MIRI MRS band.
"""
# check channel is only 1 value and band is only 1 value
validch = ["1", "2", "3", "4"]
validband = ["short", "medium", "long"]
band_check = False
ch_check = False
b_check = False
if model.meta.instrument.channel in validch:
ch_check = True
if str(model.meta.instrument.band).lower() in validband:
b_check = True

if ch_check and b_check:
band_check = True

return band_check

def _extract_ifu(self, model, exp_type, extract_ref, apcorr_ref):
"""
Extract IFU spectra from a single datamodel.
Expand All @@ -367,6 +397,21 @@ def _extract_ifu(self, model, exp_type, extract_ref, apcorr_ref):
source_type = self.ifu_set_srctype
self.log.info(f"Overriding source type and setting it to {self.ifu_set_srctype}")

if exp_type == "MIR_MRS":
band_cube = self._check_mrs_type(model)
if self.ifu_rfcorr and not band_cube:
message = (
"Turning off residual fringe correction for MIRI MRS data "
"because the input is not a single IFU band"
)
self.log.info(message)
self.ifu_rfcorr = False
else:
self.ifu_rfcorr = False
self.log.info(
"Turning off residual fringe correction because it only works on MIRI MRS data"
)

result = ifu_extract1d(
model,
extract_ref,
Expand Down
Loading

0 comments on commit 11531aa

Please sign in to comment.