Skip to content

Commit

Permalink
adding psf_extract.py
Browse files Browse the repository at this point in the history
  • Loading branch information
jemorrison committed Oct 24, 2024
1 parent 217d660 commit bba5efc
Show file tree
Hide file tree
Showing 3 changed files with 646 additions and 1 deletion.
21 changes: 20 additions & 1 deletion jwst/extract_1d/extract_1d_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from ..stpipe import Step
from . import extract
from . import psf_extract
from .soss_extract import soss_extract

__all__ = ["Extract1dStep"]
Expand Down Expand Up @@ -53,6 +54,9 @@ class Extract1dStep(Step):
If not None, this parameter overrides the value in the
extract_1d reference file.
psf_extraction : bool or None
If True, a optiminal psf extraction is performed.
use_source_posn : bool or None
If True, the source and background extraction positions specified in
the extract1d reference file (or the default position, if there is no
Expand Down Expand Up @@ -158,6 +162,7 @@ class Extract1dStep(Step):
bkg_sigma_clip = float(default=3.0) # background sigma clipping threshold
log_increment = integer(default=50) # increment for multi-integration log messages
subtract_background = boolean(default=None) # subtract background?
psf_extraction = boolean(default=None) # Perform psf extraction
use_source_posn = boolean(default=None) # use source coords to center extractions?
center_xy = float_list(min=2, max=2, default=None) # IFU extraction x/y center
apply_apcorr = boolean(default=True) # apply aperture corrections?
Expand All @@ -180,7 +185,7 @@ class Extract1dStep(Step):
soss_modelname = output_file(default = None) # Filename for optional model output of traces and pixel weights
"""

reference_file_types = ['extract1d', 'apcorr', 'pastasoss', 'specprofile', 'speckernel']
reference_file_types = ['extract1d', 'apcorr', 'pastasoss', 'specprofile', 'speckernel', 'specwcs']

Check warning on line 188 in jwst/extract_1d/extract_1d_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/extract_1d_step.py#L188

Added line #L188 was not covered by tests

def process(self, input):
"""Execute the step.
Expand Down Expand Up @@ -240,6 +245,8 @@ def process(self, input):
else:
exp_type = None

print('************ exp_type ************', exp_type)
print(isinstance(input_model, ModelContainer))

Check warning on line 249 in jwst/extract_1d/extract_1d_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/extract_1d_step.py#L249

Added line #L249 was not covered by tests
if self.ifu_rfcorr:
if exp_type != "MIR_MRS":
self.log.warning("The option to apply a residual refringe correction is"
Expand All @@ -255,6 +262,17 @@ def process(self, input):
self.log.warning("The option to change the source type is"
f" not supported for {input_model.meta.exposure.type} data.")

# Check if doing PSF extraction. Only works on LRS data at this time

Check warning on line 265 in jwst/extract_1d/extract_1d_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/extract_1d_step.py#L265

Added line #L265 was not covered by tests
if self.psf_extraction:
# the input_model could be a container or a single model
# for now we are assuming a single model of LRS data
print(type(input_model)) # cal file could have other nod position subtracted

Check warning on line 269 in jwst/extract_1d/extract_1d_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/extract_1d_step.py#L269

Added line #L269 was not covered by tests
print(input_model.meta.exposure.type)
#psf_ref_name = self.get_reference_file(input_model, 'psf')
psf_ref_name = '/Users/morrison/PSF_extract/MIRI_LRS_SLIT_EPSF_20240602_WAVE.fits'
specwcs_ref_name = self.get_reference_file(input_model, 'specwcs')

Check warning on line 273 in jwst/extract_1d/extract_1d_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/extract_1d_step.py#L272-L273

Added lines #L272 - L273 were not covered by tests
psf_extract.psf_extraction(input_model, psf_ref_name, specwcs_ref_name)

Check warning on line 275 in jwst/extract_1d/extract_1d_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/extract_1d_step.py#L275

Added line #L275 was not covered by tests
# ______________________________________________________________________
# Do the extraction for ModelContainer - this might only be WFSS data
if isinstance(input_model, ModelContainer):
Expand Down Expand Up @@ -490,6 +508,7 @@ def process(self, input):
extract_ref = 'N/A'
self.log.info('No EXTRACT1D reference file will be used')
else:
print(' **setting up reference files for extract1d')

Check warning on line 511 in jwst/extract_1d/extract_1d_step.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/extract_1d_step.py#L511

Added line #L511 was not covered by tests
extract_ref = self.get_reference_file(input_model, 'extract1d')
self.log.info(f'Using EXTRACT1D reference file {extract_ref}')

Expand Down
222 changes: 222 additions & 0 deletions jwst/extract_1d/psf_extract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import abc
import logging
import copy
import json
import math
import numpy as np

from typing import Union, Tuple, NamedTuple, List
from astropy.modeling import polynomial
from scipy.interpolate import CubicSpline
from scipy import interpolate
from scipy import ndimage
from astropy.io import fits
from gwcs import WCS

from stdatamodels.properties import ObjectNode
from stdatamodels.jwst import datamodels
from stdatamodels import DataModel
from stdatamodels.jwst.datamodels import dqflags, SlitModel, SpecModel,SpecwcsModel, PsfModel
from jwst.datamodels import SourceModelContainer

Check warning on line 20 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L20

Added line #L20 was not covered by tests

from ..assign_wcs.util import wcs_bbox_from_shape
from ..lib import pipe_utils
from ..lib.wcs_utils import get_wavelengths
from . import extract1d
from . import ifu
from . import spec_wcs
from . import utils

Check warning on line 28 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L22-L28

Added lines #L22 - L28 were not covered by tests

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

Check warning on line 31 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L30-L31

Added lines #L30 - L31 were not covered by tests



HORIZONTAL = 1
VERTICAL = 2
"""Dispersion direction, predominantly horizontal or vertical."""

Check warning on line 37 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L35-L37

Added lines #L35 - L37 were not covered by tests

# This is intended to be larger than any possible distance (in pixels) between the target and any point in the image;
# used by locn_from_wcs().
HUGE_DIST = 1.e20

Check warning on line 41 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L41

Added line #L41 was not covered by tests

def open_specwcs(specwcs_ref_name: str, exp_type):
print('in open specwcs', exp_type)
if exp_type == 'MIR_LRS-FIXEDSLIT':

Check warning on line 45 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L43-L45

Added lines #L43 - L45 were not covered by tests
# use fits to read file (the datamodel does not have all that is needed)
ref = fits.open(specwcs_ref_name)
print(' Number of rows in the table', ref[1].data.shape)

Check warning on line 48 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L47-L48

Added lines #L47 - L48 were not covered by tests

with ref:
lrsdata = np.array([d for d in ref[1].data])

Check warning on line 51 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L50-L51

Added lines #L50 - L51 were not covered by tests
# 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
# for lrs_fixedslit
zero_point = ref[0].header['imx'] - 1, ref[0].header['imy'] - 1
print('zero point', zero_point)

Check warning on line 57 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L56-L57

Added lines #L56 - L57 were not covered by tests

# In the lrsdata reference table, X_center,Y_center, wavelength relative to zero_point
# 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]
trace = xcen + zero_point[0]
wave_trace = ycen + zero_point[1]
return trace, wave_trace, wavetab

Check warning on line 67 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L62-L67

Added lines #L62 - L67 were not covered by tests


def open_psf(psf_refname: str, exp_type):

Check warning on line 70 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L70

Added line #L70 was not covered by tests

psf_model = None
if exp_type == 'MIR_LRS-FIXEDSLIT':
psf_model = PsfModel(psf_refname)
print(psf_model.meta.psf.center_col)
print(psf_model.meta.psf.subpix)
print(psf_model.data)
print(psf_model.wave)
return psf_model

Check warning on line 79 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L72-L79

Added lines #L72 - L79 were not covered by tests



class PSFExtractData():

Check warning on line 83 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L83

Added line #L83 was not covered by tests
""" Class for PSF extraction for each source"""

def __init__(self,

Check warning on line 86 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L86

Added line #L86 was not covered by tests
input_model: DataModel,
slit: Union[DataModel, None] = None,
dispaxis: int = HORIZONTAL,
use_source_posn: Union[bool, None] = None):


"""
Parameters
----------
input_model : data model
The input science data.
slit : an input slit, or None if not used
For MultiSlit, `slit` is one slit from
a list of slits in the input. For other types of data, `slit`
will not be used.
dispaxis : int
Dispersion direction: 1 is horizontal, 2 is vertical.
"""

self.exp_type = input_model.meta.exposure.type
self.dispaxis = dispaxis
self.wcs = None

Check warning on line 110 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L108-L110

Added lines #L108 - L110 were not covered by tests


if slit is None:
if hasattr(input_model.meta, 'wcs'):
self.wcs = input_model.meta.wcs
elif hasattr(slit, 'meta') and hasattr(slit.meta, 'wcs'):
self.wcs = slit.meta.wcs

Check warning on line 117 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L113-L117

Added lines #L113 - L117 were not covered by tests

if self.wcs is None:
log.warning("WCS function not found in input.")

Check warning on line 120 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L119-L120

Added lines #L119 - L120 were not covered by tests





def psf_extraction(input_model, psf_ref_name, specwcs_ref_name):

Check warning on line 126 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L126

Added line #L126 was not covered by tests

exp_type = input_model.meta.exposure.type
trace, wave_trace, wavetab = open_specwcs(specwcs_ref_name, exp_type)
psf_model = open_psf(psf_ref_name, exp_type)

Check warning on line 130 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L128-L130

Added lines #L128 - L130 were not covered by tests
# Input data
# 1. Must bePoint Source (for now PSF extraction is only working with Point Source data)
# 2. Can have multiple sources in one input_model
# 3. Keep track of mode to help decide how to find the sources and use WCS

# We want to organize the data by source. The input can be a source ModelContainer or a single model.
# The type of the individual models can be: ImageModel (LRS), SlitModel (NIRSpec Slit), MultiSlitModel (MOS)
# of IFUCubeModel
# For now store all the data as a list of input models. We will loop over each input mode, find the sources
# determine the spatial profile, and call extract1d.

input_models = []
if isinstance(input_model, SourceModelContainer):
input_models = input_model

Check warning on line 144 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L142-L144

Added lines #L142 - L144 were not covered by tests
else:
input_models.append(input_model)

Check warning on line 146 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L146

Added line #L146 was not covered by tests

for model in input_models:

Check warning on line 148 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L148

Added line #L148 was not covered by tests

# currently for LRS-FIXEDSLIT data it is assume there is 1 source and the source is located
# at the dither_position

if exp_type == 'MIR_LRS-FIXEDSLIT': # assume we can use the WCS to find source location
dispaxis = 2
wcs = model.meta.wcs

Check warning on line 155 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L153-L155

Added lines #L153 - L155 were not covered by tests

# assuming 1 source
# Set up the profiles that are to be determined
nsource = 1
profile_2d = []
profile_bg = [] # Currently not sure if this is needed

Check warning on line 161 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L159-L161

Added lines #L159 - L161 were not covered by tests
# Calwebb_spec2 subtracts the other nod position automatically

# Only one source
middle, locn_w, locn_x = utils.locn_from_wcs(

Check warning on line 165 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L165

Added line #L165 was not covered by tests
dispaxis,
wcs,
model,
None, None, None)

# Match the reference trace and corresponding wavelength to the data
# The wavelength for the reference trace does not exactly line up exactly with the data

cs = CubicSpline(wavetab, trace)
cen_shift = cs(locn_w)
shift = locn_x - cen_shift

Check warning on line 176 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L174-L176

Added lines #L174 - L176 were not covered by tests

print('Location of source in observed spectrum', locn_x, ' at wavelength', locn_w)
print('In the reference trace the location is ', cen_shift)
print('Shift to apply to ref trace', shift)

Check warning on line 180 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L178-L180

Added lines #L178 - L180 were not covered by tests

# For LRS-Fix data we want to cut out the slit from the full array
slit = []
bbox = wcs.bounding_box
x0, x1 = bbox[0]
y0, y1 = bbox[1]
cutout = model.data[int(np.ceil(y0)):int(np.ceil(y1)), int(np.round(x0)):int(np.round(x1))]
slit.append(cutout)

Check warning on line 188 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L183-L188

Added lines #L183 - L188 were not covered by tests

# adjust the trace to for slit region
trace_cutout = trace - bbox[0][0]
wtrace_cutout = wave_trace - bbox[1][0]
trace_shift = trace_cutout + xshift

Check warning on line 193 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L191-L193

Added lines #L191 - L193 were not covered by tests

# xtrace_shift - for each wavelength in the PSF this is the shift in x to apply to the PSF image to shift it
# to fall on the source.
# wavetab : is the wavelengh cooresponding the the trace. This wavelength may not match exactly to the the PSF.wave

# determine what the shifts per row are for the the wavelengths given by the model PSF

PSFinterp = interpolate.interp1d(wavetab, xtrace_shift,fill_value="extrapolate")
psf_shift = PSFinterp(psf_model.wave)
psf_shift = psf_model.meta.center_col - (psf_shift * psf_subpix)

Check warning on line 203 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L201-L203

Added lines #L201 - L203 were not covered by tests

# if the PSF is an ePSF
_x, _y = np.meshgrid(np.arange(cutout.shape[1]), np.arange(cutout.shape[0]))
_x = _x*psf_subpix + psf_shift[:, np.newaxis]
sprofile = ndimage.map_coordinates(psf, [_y, _x], order=1)
profile_2d.append(sprofile)

Check warning on line 209 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L206-L209

Added lines #L206 - L209 were not covered by tests

print(sprofile)

Check warning on line 211 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L211

Added line #L211 was not covered by tests

# set up the data to be passed to extract1d
result = util.setup_data(model)
data, dq, var_poisson, var_rnoise, weights = result

Check warning on line 215 in jwst/extract_1d/psf_extract.py

View check run for this annotation

Codecov / codecov/patch

jwst/extract_1d/psf_extract.py#L214-L215

Added lines #L214 - L215 were not covered by tests







Loading

0 comments on commit bba5efc

Please sign in to comment.