Skip to content

Commit

Permalink
JP-3598: The ResampleSpecStep and Extract1DStep within MasterBackgrou…
Browse files Browse the repository at this point in the history
…ndMosStep are inaccessible (#8847)
  • Loading branch information
melanieclarke authored Dec 4, 2024
2 parents 56cccb9 + 099eb55 commit f295fbd
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 58 deletions.
1 change: 1 addition & 0 deletions changes/8847.master_background.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Include resample, pixel_replace and extract_1d into MOS background pipeline
1 change: 1 addition & 0 deletions docs/jwst/master_background/description.rst
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ as follows:
extended sources (appropriate for background signal), and saving the extended
source correction arrays for each slit in an internal copy of the data model
#. If a user-supplied master background spectrum is **not** given, the
:ref:`pixel_replace <pixel_replace_step>`,
:ref:`resample_spec <resample_spec_step>` and :ref:`extract_1d <extract_1d_step>`
steps are applied to the calibrated background slits, resulting
in extracted 1D background spectra
Expand Down
36 changes: 32 additions & 4 deletions jwst/master_background/master_background_mos_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
from ..flatfield import flat_field_step
from ..pathloss import pathloss_step
from ..photom import photom_step
from ..pixel_replace import pixel_replace_step
from ..resample import resample_spec_step
from ..extract_1d import extract_1d_step
from ..stpipe import Pipeline

__all__ = ['MasterBackgroundMosStep']
Expand Down Expand Up @@ -62,6 +65,9 @@ class MasterBackgroundMosStep(Pipeline):
'pathloss': pathloss_step.PathLossStep,
'barshadow': barshadow_step.BarShadowStep,
'photom': photom_step.PhotomStep,
'pixel_replace': pixel_replace_step.PixelReplaceStep,
'resample_spec': resample_spec_step.ResampleSpecStep,
'extract_1d': extract_1d_step.Extract1dStep,
}

# No need to prefetch. This will have been done by the parent step.
Expand Down Expand Up @@ -170,7 +176,7 @@ def process(self, data):
return result

def set_pars_from_parent(self):
"""Set substep parameters from the parents substeps"""
"""Set substep parameters from the parents substeps when needed"""
if not self.parent:
return

Expand All @@ -188,6 +194,21 @@ def set_pars_from_parent(self):
del pars[par]
getattr(self, step).update_pars(pars)

def _extend_bg_slits(self, pre_calibrated):
# Copy dedicated background slitlets to a temporary model
bkg_model = datamodels.MultiSlitModel()
bkg_model.update(pre_calibrated)
slits = []
for slit in pre_calibrated.slits:
if nirspec_utils.is_background_msa_slit(slit):
self.log.info(f'Using background slitlet {slit.source_name}')
slits.append(slit)
if len(slits) == 0:
self.log.warning('No background slitlets found; skipping master bkg correction')
return None
bkg_model.slits.extend(slits)
return bkg_model

def _classify_slits(self, data):
"""Determine how many Slits are background and source types
Expand Down Expand Up @@ -269,9 +290,16 @@ def _calc_master_background(
master_background = user_background
bkg_x1d_spectra = None
else:
self.log.debug('Calculating 1D master background')
master_background, bkg_x1d_spectra = nirspec_utils.create_background_from_multislit(
pre_calibrated, sigma_clip=sigma_clip, median_kernel=median_kernel)
self.log.info('Creating MOS master background from background slitlets')
bkg_model = self._extend_bg_slits(pre_calibrated)
if bkg_model is not None:
bkg_model = self.pixel_replace.run(bkg_model)
bkg_model = self.resample_spec.run(bkg_model)
bkg_x1d_spectra = self.extract_1d.run(bkg_model)
master_background = nirspec_utils.create_background_from_multispec(
bkg_x1d_spectra, sigma_clip=sigma_clip, median_kernel=median_kernel)
else:
master_background = None
if master_background is None:
self.log.debug('No master background could be calculated. Returning None')
return None, None, None
Expand Down
39 changes: 5 additions & 34 deletions jwst/master_background/nirspec_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from scipy.signal import medfilt

from stdatamodels.jwst import datamodels

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
Expand Down Expand Up @@ -78,14 +77,14 @@ def map_to_science_slits(input_model, master_bkg):
return output_model


def create_background_from_multislit(input_model, sigma_clip=3, median_kernel=1):
def create_background_from_multispec(bkg_model, sigma_clip=3, median_kernel=1):
"""Create a 1D master background spectrum from a set of
calibrated background MOS slitlets in the input
MultiSlitModel.
MultiSpecModel.
Parameters
----------
input_model : `~jwst.datamodels.MultiSlitModel`
bkg_model : `~jwst.datamodels.MultiSpecModel`
The input data model containing all slit instances.
sigma_clip : None or float, optional
Optional factor for sigma clipping outliers when combining background spectra.
Expand All @@ -101,36 +100,11 @@ def create_background_from_multislit(input_model, sigma_clip=3, median_kernel=1)
x1d: `jwst.datamodels.MultiSpecModel`
The 1D extracted background spectra of the inputs.
"""
from ..resample import resample_spec_step
from ..extract_1d import extract_1d_step
from ..combine_1d.combine1d import combine_1d_spectra

log.info('Creating MOS master background from background slitlets')

# Copy dedicated background slitlets to a temporary model
bkg_model = datamodels.MultiSlitModel()
bkg_model.update(input_model)
slits = []
for slit in input_model.slits:
if is_background_msa_slit(slit):
log.info(f'Using background slitlet {slit.source_name}')
slits.append(slit)

if len(slits) == 0:
log.warning('No background slitlets found; skipping master bkg correction')
return None

bkg_model.slits.extend(slits)

# Apply resample_spec and extract_1d to all background slitlets
log.info('Applying resampling and 1D extraction to background slits')
resamp = resample_spec_step.ResampleSpecStep.call(bkg_model)
x1d = extract_1d_step.Extract1dStep.call(resamp)

# Call combine_1d to combine the 1D background spectra
log.info('Combining 1D background spectra into master background')
master_bkg = combine_1d_spectra(
x1d, exptime_key='exposure_time', sigma_clip=sigma_clip)
master_bkg = combine_1d_spectra(bkg_model, exptime_key='exposure_time', sigma_clip=sigma_clip)

# If requested, apply a moving-median boxcar filter to the master background spectrum
# Round down even kernel sizes because only odd kernel sizes are supported.
Expand All @@ -150,10 +124,7 @@ def create_background_from_multislit(input_model, sigma_clip=3, median_kernel=1)
kernel_size=[median_kernel]
)

del bkg_model
del resamp

return master_bkg, x1d
return master_bkg


def correct_nrs_ifu_bkg(input_model):
Expand Down
65 changes: 45 additions & 20 deletions jwst/master_background/tests/test_master_background_mos.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
import pytest
from astropy.io import fits
from astropy.table import Table
from stdatamodels.jwst.datamodels import ImageModel
from stdatamodels.jwst.datamodels import ImageModel, MultiSlitModel

from jwst.stpipe import query_step_status
from jwst.assign_wcs import AssignWcsStep
from jwst.extract_2d.extract_2d_step import Extract2dStep
from jwst.extract_2d.tests.test_nirspec import create_nirspec_hdul
from jwst.master_background import MasterBackgroundMosStep
from jwst.master_background import nirspec_utils
from jwst.pixel_replace import PixelReplaceStep
from jwst.resample import ResampleSpecStep
from jwst.extract_1d import Extract1dStep


def create_msa_hdul():
Expand Down Expand Up @@ -74,6 +77,7 @@ def nirspec_msa_metfl(tmp_path):
hdul.close()
return filename


@pytest.fixture
def nirspec_msa_extracted2d(nirspec_msa_rate, nirspec_msa_metfl):
model = ImageModel(nirspec_msa_rate)
Expand All @@ -82,6 +86,20 @@ def nirspec_msa_extracted2d(nirspec_msa_rate, nirspec_msa_metfl):
return model


def mk_multispec(model):
specs_model = MultiSlitModel()
specs_model.update(model)
slits = []
for slit in model.slits:
if nirspec_utils.is_background_msa_slit(slit):
slits.append(slit)
specs_model.slits.extend(slits)
specs_model = PixelReplaceStep.call(specs_model)
specs_model = ResampleSpecStep.call(specs_model)
specs_model = Extract1dStep.call(specs_model)
return specs_model


def test_master_background_mos(nirspec_msa_extracted2d):
model = nirspec_msa_extracted2d

Expand All @@ -97,43 +115,47 @@ def test_master_background_mos(nirspec_msa_extracted2d):
# Check that a background was subtracted from the science data
assert not np.allclose(sci_orig, sci_bkgsub)

model.close()
result.close()
del model
del result


def test_create_background_from_multislit(nirspec_msa_extracted2d):
def test_create_background_from_multispec(nirspec_msa_extracted2d):
model = nirspec_msa_extracted2d

# Insert a outliers into one of the background spectra
# Insert outliers into one of the background spectra
nypix = len(model.slits[0].data)
nxpix = len(model.slits[0].data)
model.slits[0].data[nypix//2,nxpix//2-1:nxpix//2+1] = 10
model.slits[0].data[nypix//2, nxpix//2-1:nxpix//2+1] = 10

specs_model = mk_multispec(model)

# First check that we can make a master background from the inputs

# Check that with sigma_clip=None, the outlier is retained
master_background, _ = nirspec_utils.create_background_from_multislit(
model, sigma_clip=None)
master_background = nirspec_utils.create_background_from_multispec(
specs_model, sigma_clip=None)
assert np.any(master_background.spec[0].spec_table['surf_bright'] > 1)

# Confirm that using a median_filter will filter out the outlier
master_background, _ = nirspec_utils.create_background_from_multislit(
model, median_kernel=4)
master_background = nirspec_utils.create_background_from_multispec(
specs_model, median_kernel=4)
assert np.allclose(master_background.spec[0].spec_table['surf_bright'], 1)

# Confirm that using a sigma clipping when combining background spectra
# removes the outlier
master_background, _ = nirspec_utils.create_background_from_multislit(
model, sigma_clip=3)
master_background = nirspec_utils.create_background_from_multispec(
specs_model, sigma_clip=3)
assert np.allclose(master_background.spec[0].spec_table['surf_bright'], 1)

model.close()
del model
del specs_model


def test_map_to_science_slits(nirspec_msa_extracted2d):
model = nirspec_msa_extracted2d
specs_model = mk_multispec(model)

master_background, _ = nirspec_utils.create_background_from_multislit(
model)
master_background = nirspec_utils.create_background_from_multispec(specs_model)

# Check that the master background is expanded to the shape of the input slits
mb_multislit = nirspec_utils.map_to_science_slits(model, master_background)
Expand All @@ -145,13 +167,15 @@ def test_map_to_science_slits(nirspec_msa_extracted2d):
nonzero = slit_data != 0
assert np.allclose(slit_data[nonzero], 1)

model.close()
del model
del specs_model


def test_apply_master_background(nirspec_msa_extracted2d):
model = nirspec_msa_extracted2d
specs_model = mk_multispec(model)

master_background, _ = nirspec_utils.create_background_from_multislit(
model)
master_background = nirspec_utils.create_background_from_multispec(specs_model)
mb_multislit = nirspec_utils.map_to_science_slits(model, master_background)

result = nirspec_utils.apply_master_background(model, mb_multislit, inverse=False)
Expand All @@ -175,7 +199,8 @@ def test_apply_master_background(nirspec_msa_extracted2d):
assert np.any(diff != 0)
assert np.allclose(diff[diff != 0], -1)

model.close()
result.close()
del model
del result
del specs_model


0 comments on commit f295fbd

Please sign in to comment.