Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
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: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ repos:
(?x)^(
jwst/associations/.* |
jwst/coron/.* |
jwst/cube_build/.* |
jwst/cube_skymatch/.* |
jwst/dark_current/.* |
jwst/flatfield/.* |
Expand Down
2 changes: 0 additions & 2 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ docstring-code-format = true
exclude = [
"jwst/associations/**.py",
"jwst/coron/**.py",
"jwst/cube_build/**.py",
"jwst/cube_skymatch/**.py",
"jwst/dark_current/**.py",
"jwst/flatfield/**.py",
Expand Down Expand Up @@ -115,7 +114,6 @@ ignore-fully-untyped = true # Turn off annotation checking for fully untyped co
]
"jwst/associations/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/coron/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/cube_build/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/cube_skymatch/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/dark_current/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
"jwst/flatfield/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
Expand Down
1 change: 1 addition & 0 deletions changes/9347.cube_build.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Updated cube_build function names to adhere to Python standards
4 changes: 3 additions & 1 deletion jwst/cube_build/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Spectral Cube building program for JWST IFU data."""

from .cube_build_step import CubeBuildStep

__all__ = ['CubeBuildStep']
__all__ = ["CubeBuildStep"]
168 changes: 99 additions & 69 deletions jwst/cube_build/blot_cube_build.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
""" Main module for blotting sky cube back to detector space
"""
import numpy as np
import logging

from jwst.datamodels import ModelContainer

from ..assign_wcs import nirspec
from gwcs import wcstools
from jwst.assign_wcs.util import in_ifu_slice
from . import instrument_defaults
from .blot_median import blot_wrapper # c extension

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


class CubeBlot():
class CubeBlot:
"""Main module for blotting a sky cube back to detector space."""

def __init__(self, median_model, input_models):
"""Class Blot holds the main variables for blotting sky cube to detector
"""
Initialize main variables for blotting a sky cube to detector space.

Information is pulled out of the median sky cube created by a previous
run of cube_build in single mode and stored in the ClassBlot.These
Expand All @@ -26,19 +26,14 @@
data (instrument, channel, band, grating or filter).

Parameters
---------
median_model: ifucube model
----------
median_model : IFUCubeModel
The median input sky cube is created from a median stack of all the
individual input_models mapped to the full IFU cube imprint on the
sky.
input_models: data model
input_models : ModelContainer
The input models used to create the median sky cube.

Returns
-------
CubeBlot class initialized
"""

# Pull out the needed information from the Median IFUCube
self.median_skycube = median_model
self.instrument = median_model.meta.instrument.name
Expand All @@ -51,29 +46,30 @@
self.par_median_select1 = None
self.par_median_select2 = None

if self.instrument == 'MIRI':
if self.instrument == "MIRI":

Check warning on line 49 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L49

Added line #L49 was not covered by tests
self.channel = median_model.meta.instrument.channel
self.subchannel = median_model.meta.instrument.band.lower()
self.par_median_select1 = self.channel
self.par_median_select2 = self.subchannel

elif self.instrument == 'NIRSPEC':
elif self.instrument == "NIRSPEC":

Check warning on line 55 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L55

Added line #L55 was not covered by tests
self.grating = median_model.meta.instrument.grating
self.filter = median_model.meta.instrument.filter
self.par_median_select1 = self.grating
# ________________________________________________________________
# set up x,y,z of Median Cube
# Median cube should have linear wavelength
xcube, ycube, zcube = wcstools.grid_from_bounding_box(
self.median_skycube.meta.wcs.bounding_box,
step=(1, 1, 1))
self.median_skycube.meta.wcs.bounding_box, step=(1, 1, 1)
)

# using wcs of ifu cube determine ra,dec,lambda
self.cube_ra, self.cube_dec, self.cube_wave = \
self.median_skycube.meta.wcs(xcube + 1, ycube + 1, zcube + 1)
# using wcs of ifu cube determine ra, dec, lambda
self.cube_ra, self.cube_dec, self.cube_wave = self.median_skycube.meta.wcs(

Check warning on line 67 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L67

Added line #L67 was not covered by tests
xcube + 1, ycube + 1, zcube + 1
)

# pull out flux from the median sky cube that matches with
# cube_ra,dec,wave
# cube_ra, dec, wave
self.cube_flux = self.median_skycube.data

# remove all the nan values - just in case
Expand All @@ -93,11 +89,11 @@
self.input_list_number = []

for icount, model in enumerate(input_models):
if self.instrument == 'MIRI':
if self.instrument == "MIRI":

Check warning on line 92 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L92

Added line #L92 was not covered by tests
par1 = model.meta.instrument.channel
par2 = model.meta.instrument.band.lower()
found2 = par2.find(self.par_median_select2)
if self.instrument == 'NIRSPEC':
if self.instrument == "NIRSPEC":

Check warning on line 96 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L96

Added line #L96 was not covered by tests
par1 = model.meta.instrument.grating
par2 = model.meta.instrument.grating
found2 = 1
Expand All @@ -106,50 +102,69 @@
if found1 > -1 and found2 > -1:
self.input_models.append(model)
self.input_list_number.append(icount)

# **********************************************************************

def blot_info(self):
""" Prints the basic parameters of the blot image and median sky cube
"""
log.info('Information on Blotting')
log.info('Working with instrument %s', self.instrument)
log.info('Shape of sky cube %f %f %f',
self.median_skycube.data.shape[2],
self.median_skycube.data.shape[1],
self.median_skycube.data.shape[0])

if self.instrument == 'MIRI':
log.info('Channel %s', self.channel)
log.info('Sub-channel %s', self.subchannel)

elif self.instrument == 'NIRSPEC':
log.info('Grating %s', self.grating)
log.info('Filter %s', self.filter)
"""Print the basic parameters of the blot image and median sky cube."""
log.info("Information on Blotting")
log.info("Working with instrument %s", self.instrument)
log.info(

Check warning on line 112 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L110-L112

Added lines #L110 - L112 were not covered by tests
"Shape of sky cube %f %f %f",
self.median_skycube.data.shape[2],
self.median_skycube.data.shape[1],
self.median_skycube.data.shape[0],
)

if self.instrument == "MIRI":
log.info("Channel %s", self.channel)
log.info("Sub-channel %s", self.subchannel)

Check warning on line 121 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L119-L121

Added lines #L119 - L121 were not covered by tests

elif self.instrument == "NIRSPEC":
log.info("Grating %s", self.grating)
log.info("Filter %s", self.filter)

Check warning on line 125 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L123-L125

Added lines #L123 - L125 were not covered by tests

# ***********************************************************************

def blot_images(self):
if self.instrument == 'MIRI':
"""
Call the instrument specific blotting code.

Returns
-------
blotmodels : ModelContainer
Blotted IFU image models
input_list_number : list of int
List containing index of blot model in input_models
"""
if self.instrument == "MIRI":

Check warning on line 140 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L140

Added line #L140 was not covered by tests
blotmodels = self.blot_images_miri()
elif self.instrument == 'NIRSPEC':
elif self.instrument == "NIRSPEC":

Check warning on line 142 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L142

Added line #L142 was not covered by tests
blotmodels = self.blot_images_nirspec()
return blotmodels, self.input_list_number

# ************************************************************************

def blot_images_miri(self):
""" Core blotting routine for MIRI
"""
Core blotting routine for MIRI.

This is the main routine for blotting the MIRI median sky cube back to
the detector space and creating a blotting image for each input model
1. Loop over every data model to be blotted and find ra,dec,wavelength
for every pixel in a valid slice on the detector.
1. Loop over every data model to be blotted and find ra, dec and
wavelength for every pixel in a valid slice on the detector.
2. Loop over every input model and using the inverse (backwards) transform
convert the median sky cube values ra, dec, lambda to the blotted
x, y detector value (x_cube, y_cube).
3. For each input model loop over the blotted x,y values and find
the x, y detector values that fall within the roi region.
The blotted flux = the weighted flux, where the weight is based on
distance between the center of the blotted pixel and the detector pixel.

Returns
-------
blot_models : ModelContainer
Container of blotted IFUImage models
"""
blot_models = ModelContainer()
instrument_info = instrument_defaults.InstrumentInfo()
Expand All @@ -169,8 +184,7 @@
ydet, xdet = np.mgrid[:ysize, :xsize]
ydet = ydet.flatten()
xdet = xdet.flatten()
self.ycenter_grid, self.xcenter_grid = np.mgrid[0:ysize,
0:xsize]
self.ycenter_grid, self.xcenter_grid = np.mgrid[0:ysize, 0:xsize]

Check warning on line 187 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L187

Added line #L187 was not covered by tests

xsize2 = xend - xstart + 1
xcenter = np.arange(xsize2) + xstart
Expand All @@ -180,23 +194,23 @@
xdet = xdet[valid_channel]
ydet = ydet[valid_channel]
# cube spaxel ra,dec values --> x, y on detector
x_cube, y_cube = model.meta.wcs.backward_transform(self.cube_ra,
self.cube_dec,
self.cube_wave)
x_cube, y_cube = model.meta.wcs.backward_transform(

Check warning on line 197 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L197

Added line #L197 was not covered by tests
self.cube_ra, self.cube_dec, self.cube_wave
)
x_cube = np.ndarray.flatten(x_cube)
y_cube = np.ndarray.flatten(y_cube)
flux_cube = np.ndarray.flatten(self.cube_flux)

valid = ~np.isnan(y_cube)
valid_channel = np.logical_and(x_cube >= xstart, x_cube <= xend)
valid_flux = (flux_cube != 0)
valid_flux = flux_cube != 0

Check warning on line 206 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L206

Added line #L206 was not covered by tests

fuse = np.where(valid & valid_channel & valid_flux)
x_cube = x_cube[fuse]
y_cube = y_cube[fuse]
flux_cube = flux_cube[fuse]

log.info('Blotting back to %s', model.meta.filename)
log.info("Blotting back to %s", model.meta.filename)

Check warning on line 213 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L213

Added line #L213 was not covered by tests
# ______________________________________________________________________________
# blot_wrapper is a c extension that finds:
# the overlapping median cube spaxels with the detector pixels
Expand All @@ -207,9 +221,9 @@
roi_det = 1.0 # Just large enough that we don't get holes

# set up c wrapper for blotting
result = blot_wrapper(roi_det, xsize, ysize, xstart, xsize2,
xcenter, ycenter,
x_cube, y_cube, flux_cube)
result = blot_wrapper(

Check warning on line 224 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L224

Added line #L224 was not covered by tests
roi_det, xsize, ysize, xstart, xsize2, xcenter, ycenter, x_cube, y_cube, flux_cube
)
blot_flux, blot_weight = result
igood = np.where(blot_weight > 0)
blot_flux[igood] = blot_flux[igood] / blot_weight[igood]
Expand All @@ -222,7 +236,8 @@
# ************************************************************************

def blot_images_nirspec(self):
""" Core blotting routine for NIRSPEC
"""
Core blotting routine for NIRSPEC.

This is the main routine for blotting the NIRSPEC median sky cube back to
the detector space and creating a blotting image for each input model.
Expand All @@ -243,6 +258,10 @@
determines the overlap of the blotted x,y values with a regular grid setup
in the detector plane which is the blotted image.

Returns
-------
blot_models : ModelContainer
Container of blotted IFUImage models
"""
blot_models = ModelContainer()

Expand All @@ -261,18 +280,19 @@

# for NIRSPEC wcs information accessed separately for each slice
nslices = 30
log.info('Blotting 30 slices on NIRSPEC detector')
log.info("Blotting 30 slices on NIRSPEC detector")

Check warning on line 283 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L283

Added line #L283 was not covered by tests
roi_det = 1.0 # Just large enough that we don't get holes

wcsobj, tr1, tr2, tr3 = nirspec._get_transforms(model, np.arange(nslices))
wcsobj, tr1, tr2, tr3 = nirspec._get_transforms(model, np.arange(nslices)) # noqa: SLF001

Check warning on line 286 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L286

Added line #L286 was not covered by tests

for ii in range(nslices):
# for each slice pull out the blotted values that actually fall on the slice region
# use the bounding box of each slice to determine the slice limits
slice_wcs = nirspec._nrs_wcs_set_input_lite(model, wcsobj, ii,
[tr1, tr2[ii], tr3[ii]])
slicer2world = slice_wcs.get_transform('slicer','world')
detector2slicer = slice_wcs.get_transform('detector','slicer')
slice_wcs = nirspec._nrs_wcs_set_input_lite( # noqa: SLF001

Check warning on line 291 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L291

Added line #L291 was not covered by tests
model, wcsobj, ii, [tr1, tr2[ii], tr3[ii]]
)
slicer2world = slice_wcs.get_transform("slicer", "world")
detector2slicer = slice_wcs.get_transform("detector", "slicer")

Check warning on line 295 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L294-L295

Added lines #L294 - L295 were not covered by tests

# find some rough limits on ra,dec, lambda using the x,y -> ra,dec,lambda
x, y = wcstools.grid_from_bounding_box(slice_wcs.bounding_box)
Expand All @@ -297,18 +317,19 @@
use1 = np.logical_and(self.cube_ra >= ramin, self.cube_ra <= ramax)
use2 = np.logical_and(self.cube_dec >= decmin, self.cube_dec <= decmax)
use3 = np.logical_and(self.cube_wave >= lam_min, self.cube_wave <= lam_max)
use = np.logical_and(np.logical_and(use1, use2),use3)
use = np.logical_and(np.logical_and(use1, use2), use3)

Check warning on line 320 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L320

Added line #L320 was not covered by tests

ra_use = self.cube_ra[use]
dec_use = self.cube_dec[use]
wave_use = self.cube_wave[use]
flux_use = self.cube_flux[use]

# get the indices of elements on the slice
onslice_ind = in_ifu_slice(slice_wcs,ra_use,dec_use,wave_use)
onslice_ind = in_ifu_slice(slice_wcs, ra_use, dec_use, wave_use)

Check warning on line 328 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L328

Added line #L328 was not covered by tests
slx, sly, sllam = slicer2world.inverse(ra_use, dec_use, wave_use)
xslice,yslice = detector2slicer.inverse(slx[onslice_ind], sly[onslice_ind],
sllam[onslice_ind])
xslice, yslice = detector2slicer.inverse(

Check warning on line 330 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L330

Added line #L330 was not covered by tests
slx[onslice_ind], sly[onslice_ind], sllam[onslice_ind]
)
# pull out region for slice
fluxslice = flux_use[onslice_ind]

Expand All @@ -317,7 +338,7 @@
xlimit, ylimit = slice_wcs.bounding_box
xuse = np.logical_and(xslice >= xlimit[0], xslice <= xlimit[1])
yuse = np.logical_and(yslice >= ylimit[0], yslice <= ylimit[1])
use = np.logical_and(xuse,yuse)
use = np.logical_and(xuse, yuse)

Check warning on line 341 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L341

Added line #L341 was not covered by tests
xuse = xslice[use]
yuse = yslice[use]
flux_use = fluxslice[use]
Expand All @@ -336,9 +357,18 @@
xstart = 0
xsize2 = blot_xsize

result = blot_wrapper(roi_det, blot_xsize, blot_ysize, xstart, xsize2,
xcenter, ycenter,
x_total, y_total, flux_total)
result = blot_wrapper(

Check warning on line 360 in jwst/cube_build/blot_cube_build.py

View check run for this annotation

Codecov / codecov/patch

jwst/cube_build/blot_cube_build.py#L360

Added line #L360 was not covered by tests
roi_det,
blot_xsize,
blot_ysize,
xstart,
xsize2,
xcenter,
ycenter,
x_total,
y_total,
flux_total,
)
blot_flux_slice, blot_weight_slice = result
blot_flux = blot_flux + blot_flux_slice
blot_weight = blot_weight + blot_weight_slice
Expand Down
Loading