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

Backport PR #3319 on branch v4.0.x (Respect loaded mask cube properly in spectral extraction.) #3348

Open
wants to merge 6 commits into
base: v4.0.x
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Cubeviz

- Fixed initializing a Gaussian1D model component when ``Cube Fit`` is toggled on. [#3295]

- Spectral extraction now correctly respects the loaded mask cube. [#3319]

Imviz
^^^^^

Expand Down
5 changes: 4 additions & 1 deletion docs/cubeviz/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ The slice plugin provides the ability to select the slice
of the cube currently visible in the image viewers, with the
corresponding wavelength highlighted in the spectrum viewer.

To choose a specific slice, enter an approximate wavelength (in which case the nearest slice will
To choose a specific slice, enter an approximate wavelength (in which case the nearest slice will
be selected and the wavelength entry will "span" to the exact value of that slice). The snapping
behavior can be disabled in the plugin settings to allow for smooth scrubbing, in which case the
closest slice will still be displayed in the cube viewer.
Expand Down Expand Up @@ -301,6 +301,9 @@ optionally choose a :guilabel:`Spatial region`, if you have one.
Click :guilabel:`EXTRACT` to produce a new 1D spectrum dataset
from the spectral cube, which has uncertainties propagated by
`astropy.nddata <https://docs.astropy.org/en/stable/nddata/nddata.html>`_.
By default, if a mask was loaded with the cube, it will be applied to the
cube when extracting in addition to any subsets chosen as an aperture. This
is not currently done for Data Quality arrays, e.g. the DQ extension in JWST files.

If using a simple subset (currently only works for a circular subset applied to data
with spatial axis units in wavelength) for the spatial aperture, an option to
Expand Down
1 change: 1 addition & 0 deletions jdaviz/configs/cubeviz/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Cubeviz(CubeConfigHelper, LineListMixin):

_loaded_flux_cube = None
_loaded_uncert_cube = None
_loaded_mask_cube = None
_cube_viewer_cls = CubevizImageView

def __init__(self, *args, **kwargs):
Expand Down
14 changes: 10 additions & 4 deletions jdaviz/configs/cubeviz/plugins/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,9 @@
app.add_data(sc, data_label)

if data_type == 'mask':
# We no longer auto-populate the mask cube into a viewer
pass
# We no longer auto-populate the mask cube into a viewer, but we still want
# to keep track of this cube for use in, e.g., spectral extraction.
app._jdaviz_helper._loaded_mask_cube = app.data_collection[data_label]

elif data_type == 'uncert':
app.add_data_to_viewer(uncert_viewer_reference_name, data_label)
Expand Down Expand Up @@ -429,8 +430,10 @@

if data_type == 'flux':
app._jdaviz_helper._loaded_flux_cube = app.data_collection[data_label]
if data_type == 'uncert':
elif data_type == 'uncert':

Check warning on line 433 in jdaviz/configs/cubeviz/plugins/parsers.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L433

Added line #L433 was not covered by tests
app._jdaviz_helper._loaded_uncert_cube = app.data_collection[data_label]
elif data_type == 'mask':
app._jdaviz_helper._loaded_mask_cube = app.data_collection[data_label]

Check warning on line 436 in jdaviz/configs/cubeviz/plugins/parsers.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L435-L436

Added lines #L435 - L436 were not covered by tests


def _parse_spectrum1d_3d(app, file_obj, data_label=None,
Expand Down Expand Up @@ -482,7 +485,8 @@
elif attr == 'uncertainty':
app.add_data_to_viewer(uncert_viewer_reference_name, cur_data_label)
app._jdaviz_helper._loaded_uncert_cube = app.data_collection[cur_data_label]
# We no longer auto-populate the mask cube into a viewer
elif attr == 'mask':
app._jdaviz_helper._loaded_mask_cube = app.data_collection[cur_data_label]


def _parse_spectrum1d(app, file_obj, data_label=None, spectrum_viewer_reference_name=None):
Expand Down Expand Up @@ -540,6 +544,8 @@
elif data_type == 'uncert':
app.add_data_to_viewer(uncert_viewer_reference_name, data_label)
app._jdaviz_helper._loaded_uncert_cube = app.data_collection[data_label]
elif data_type == 'mask':
app._jdaviz_helper._loaded_mask_cube = app.data_collection[data_label]

Check warning on line 548 in jdaviz/configs/cubeviz/plugins/parsers.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L547-L548

Added lines #L547 - L548 were not covered by tests


def _parse_gif(app, file_obj, data_label=None, flux_viewer_reference_name=None): # pragma: no cover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,16 @@
# TODO: allow selecting or associating an uncertainty cube?
return None

@property
def mask_cube(self):
if (hasattr(self._app._jdaviz_helper, '_loaded_flux_cube') and
hasattr(self.app._jdaviz_helper, '_loaded_mask_cube') and
self.dataset.selected == self._app._jdaviz_helper._loaded_flux_cube.label):
return self._app._jdaviz_helper._loaded_mask_cube
else:
# TODO: allow selecting or associating a mask/DQ cube?
return None

Check warning on line 381 in jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py#L381

Added line #L381 was not covered by tests

@property
def slice_display_unit(self):
return astropy.units.Unit(self.app._get_display_unit(self.slice_display_unit_name))
Expand Down Expand Up @@ -430,7 +440,7 @@
def bg_area_along_spectral(self):
return np.sum(self.bg_weight_mask, axis=self.spatial_axes)

def _extract_from_aperture(self, cube, uncert_cube, aperture,
def _extract_from_aperture(self, cube, uncert_cube, mask_cube, aperture,
weight_mask, wavelength_dependent,
selected_func, **kwargs):
# This plugin collapses over the *spatial axes* (optionally over a spatial subset,
Expand Down Expand Up @@ -486,6 +496,18 @@
# Filter out NaNs (False = good)
mask = np.logical_or(mask, np.isnan(flux))

# Also apply the cube's original mask array
if mask_cube:
snackbar_message = SnackbarMessage(
"Note: Applied loaded mask cube during extraction",
color="warning",
sender=self)
self.hub.broadcast(snackbar_message)
mask_from_cube = mask_cube.get_component('flux').data.copy()
# Some mask cubes have NaNs where they are not masked instead of 0
mask_from_cube[np.where(np.isnan(mask_from_cube))] = 0
mask = np.logical_or(mask, mask_from_cube.astype('bool'))

nddata_reshaped = NDDataArray(
flux, mask=mask, uncertainty=uncertainties, wcs=wcs, meta=nddata.meta
)
Expand Down Expand Up @@ -588,7 +610,7 @@
raise ValueError("aperture and background cannot be set to the same subset")

selected_func = self.function_selected.lower()
spec = self._extract_from_aperture(self.cube, self.uncert_cube,
spec = self._extract_from_aperture(self.cube, self.uncert_cube, self.mask_cube,
self.aperture, self.aperture_weight_mask,
self.wavelength_dependent,
selected_func, **kwargs)
Expand Down Expand Up @@ -642,7 +664,7 @@
# allow internal calls to override the behavior of the bg_spec_per_spaxel traitlet
bg_spec_per_spaxel = kwargs.pop('bg_spec_per_spaxel', self.bg_spec_per_spaxel)
if self.background.selected != self.background.default_text:
bg_spec = self._extract_from_aperture(self.cube, self.uncert_cube,
bg_spec = self._extract_from_aperture(self.cube, self.uncert_cube, self.mask_cube,
self.background, self.bg_weight_mask,
self.bg_wavelength_dependent,
self.function_selected.lower(), **kwargs)
Expand Down
21 changes: 21 additions & 0 deletions jdaviz/configs/cubeviz/plugins/tests/test_parsers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import warnings

import numpy as np
import pytest
from astropy import units as u
Expand Down Expand Up @@ -205,6 +207,25 @@ def test_numpy_cube(cubeviz_helper):
assert flux.units == 'ct'


@pytest.mark.remote_data
def test_manga_cube(cubeviz_helper):
# Remote data test of loading and extracting an up-to-date (as of 11/19/2024) MaNGA cube
# This also tests that spaxel is converted to pix**2
with warnings.catch_warnings():
warnings.filterwarnings("ignore")
cubeviz_helper.load_data("https://stsci.box.com/shared/static/gts87zqt5265msuwi4w5u003b6typ6h0.gz", cache=True) # noqa

uc = cubeviz_helper.plugins['Unit Conversion']
uc.spectral_y_type = "Surface Brightness"

se = cubeviz_helper.plugins['Spectral Extraction']
se.function = "Mean"
se.extract()
extracted_max = cubeviz_helper.get_data("Spectrum (mean)").max()
assert_allclose(extracted_max.value, 2.836957E-18, rtol=1E-5)
assert extracted_max.unit == u.Unit("erg / Angstrom s cm**2 pix**2")


def test_invalid_data_types(cubeviz_helper):
with pytest.raises(ValueError, match=r"The input file 'does_not_exist\.fits'"):
cubeviz_helper.load_data('does_not_exist.fits')
Expand Down
8 changes: 3 additions & 5 deletions jdaviz/core/linelists.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pkg_resources
from importlib import resources
import json

from astropy.table import QTable
Expand All @@ -8,8 +8,7 @@

def get_linelist_metadata():
"""Return metadata for line lists."""
metadata_file = pkg_resources.resource_filename("jdaviz",
"data/linelists/linelist_metadata.json")
metadata_file = resources.files("jdaviz").joinpath("data/linelists/linelist_metadata.json")
with open(metadata_file) as f:
metadata = json.load(f)
return metadata
Expand All @@ -34,8 +33,7 @@ def load_preset_linelist(name):
raise ValueError("Line name not in available set of line lists. " +
"Valid list names are: {}".format(list(metadata.keys())))
fname_base = metadata[name]["filename_base"]
fname = pkg_resources.resource_filename("jdaviz",
"data/linelists/{}.csv".format(fname_base))
fname = resources.files("jdaviz").joinpath("data/linelists/{}.csv".format(fname_base))
units = metadata[name]["units"]
linetable = QTable.read(fname)

Expand Down
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ filterwarnings = [
"ignore::DeprecationWarning:glue",
"ignore::DeprecationWarning:asteval",
"ignore:::specutils.spectra.spectrum1d",
# Remove the following line once https://github.com/astrofrog/mpl-scatter-density/issues/46 is addressed
"ignore:pkg_resources is deprecated as an API:DeprecationWarning:mpl_scatter_density",
# Ignore numpy 2.0 warning, see https://github.com/astropy/astropy/pull/15495
# and https://github.com/scipy/scipy/pull/19275
"ignore:.*numpy\\.core.*:DeprecationWarning",
Expand Down
Loading