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

expanded support of SB/Flux translations #2940

Merged
merged 30 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8f99b93
UI functioning unit conversion, need to reconcile tests
gibsongreen Jul 5, 2024
a1e0b11
update syntax, remove unnecessary logic
gibsongreen Jul 5, 2024
6e32a93
tests now pass, API functionality improved
gibsongreen Jul 8, 2024
3013fb8
add additional equivalencies for coords info
gibsongreen Jul 8, 2024
69cce01
first pass at disabling specviz translations
gibsongreen Jul 10, 2024
87ca242
Remove dev flag, consolidate untranslatable units, multi-config suppo…
gibsongreen Jul 11, 2024
24ff03a
Refactor moment map unit conversion
pllim Jul 2, 2024
59eb9f2
Remove SB/flux conversion for moment map
pllim Jul 10, 2024
d7686f4
add os import
gibsongreen Jul 12, 2024
e99058a
remove duplicate import from rebase
gibsongreen Jul 12, 2024
fa7c9a5
reconcile tests, change hints/docs, handle dimensionless case
gibsongreen Jul 12, 2024
da0a967
emit GlobalDisplayUnitChanged msg when toggling SB<>flux
kecnry Jul 15, 2024
0c96e2e
resolve most tests
gibsongreen Jul 16, 2024
72295cf
temporarily remove translation message
gibsongreen Jul 17, 2024
aa718e3
add comments, reenable global display message, resolve tests
gibsongreen Jul 17, 2024
f5b1f48
force sb unit for line flux test
gibsongreen Jul 17, 2024
79b14d4
add comments, clarify naming, add case handling for args
gibsongreen Jul 18, 2024
7ce2528
remove args and loop, replaced with msg
gibsongreen Jul 18, 2024
161b3fb
consolidate 2 translator functions into 1, consolidate _translate and…
gibsongreen Jul 18, 2024
119784a
address test failures
gibsongreen Jul 18, 2024
4c8e667
update marks.py equivalencies, resolving UnitConversion errors
gibsongreen Jul 18, 2024
ac1ddc8
make sure not only translation equivalencies are in marks.py but also…
gibsongreen Jul 18, 2024
c62569b
add test coverage
gibsongreen Jul 18, 2024
c385aff
use astropy units, small logic tweaks, cache untranslatable units
gibsongreen Jul 19, 2024
7be903f
technical review changes
gibsongreen Jul 22, 2024
b46c001
add warning message in tray if PIXAR_SR not present in FITS header
gibsongreen Jul 22, 2024
6eb01c0
second pass at technical changes
gibsongreen Jul 23, 2024
dffa678
Merge remote-tracking branch 'upstream/main' into flux_sb_all
gibsongreen Jul 23, 2024
9067ef5
move flux_unit checks to beginning of _on_flux_unit_changed
gibsongreen Jul 23, 2024
069dbbc
change _unit to flux.unit
gibsongreen Jul 23, 2024
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: 1 addition & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ New Features
------------

- Added flux/surface brightness translation and surface brightness
unit conversion in Cubeviz and Specviz. [#2781, #3088]
unit conversion in Cubeviz and Specviz. [#2781, #2940, #3088]

- Plugin tray is now open by default. [#2892]

Expand Down
11 changes: 4 additions & 7 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,17 @@ def equivalent_units(self, data, cid, units):
'Jy', 'mJy', 'uJy', 'MJy',
'W / (m2 Hz)', 'W / (Hz m2)', # Order is different in astropy v5.3
'eV / (s m2 Hz)', 'eV / (Hz s m2)',
'erg / (s cm2)',
'erg / (s cm2 Angstrom)', 'erg / (s cm2 Angstrom)',
'erg / (s cm2 Hz)', 'erg / (Hz s cm2)',
'ph / (s cm2 Angstrom)', 'ph / (s cm2 Angstrom)',
'ph / (Angstrom s cm2)',
'ph / (Hz s cm2)', 'ph / (Hz s cm2)', 'bol', 'AB', 'ST'
]
+ [
'Jy / sr', 'mJy / sr', 'uJy / sr', 'MJy / sr',
'W / (Hz sr m2)',
'eV / (s m2 Hz sr)',
'erg / (s cm2 sr)',
'erg / (s cm2 Angstrom sr)', 'erg / (s cm2 Hz sr)',
'ph / (s cm2 Angstrom sr)', 'ph / (s cm2 Hz sr)',
'bol / sr', 'AB / sr', 'ST / sr'
'eV / (Hz s sr m2)',
'erg / (s sr cm2)',
'AB / sr'
])
else: # spectral axis
# prefer Hz over Bq and um over micron
Expand Down
15 changes: 1 addition & 14 deletions jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
with_spinner)
from jdaviz.core.validunits import check_if_unit_is_per_solid_angle
from jdaviz.core.user_api import PluginUserApi
from jdaviz.utils import flux_conversion


__all__ = ['MomentMap']

Expand Down Expand Up @@ -358,18 +356,7 @@ def calculate_moment(self, add_data=True):
moment_new_unit = flux_or_sb_display_unit
else:
moment_new_unit = flux_or_sb_display_unit * self.spectrum_viewer.state.x_display_unit # noqa: E501

# Create a temporary Spectrum1D object with ability to convert from surface brightness
# to flux
temp_spec = Spectrum1D(flux=self.moment)
flux_values = np.sum(np.ones_like(temp_spec.flux.value), axis=(0, 1))
pix_scale = self.dataset.selected_dc_item.meta.get('PIXAR_SR', 1.0)
pix_scale_factor = (flux_values * pix_scale)
temp_spec.meta['_pixel_scale_factor'] = pix_scale_factor
converted_spec = flux_conversion(temp_spec, self.moment.value,
self.moment.unit,
moment_new_unit) * moment_new_unit
self.moment = converted_spec
self.moment = self.moment.to(moment_new_unit)

# Reattach the WCS so we can load the result
self.moment = CCDData(self.moment, wcs=data_wcs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ def test_correct_output_flux_or_sb_units(cubeviz_helper, spectrum1d_cube_custom_

# now change surface brightness units in the unit conversion plugin

uc.flux_or_sb_unit = 'Jy / sr'
uc.sb_unit = 'Jy / sr'

# and make sure this change is propogated
output_unit_moment_0 = mm.output_unit_items[0]
Expand All @@ -344,21 +344,3 @@ def test_correct_output_flux_or_sb_units(cubeviz_helper, spectrum1d_cube_custom_
# and that calculated moment has the correct units
mm.calculate_moment()
assert mm.moment.unit == moment_unit

uc.flux_or_sb.selected = 'Flux'
mm._set_data_units()

# and make sure this change is propogated
output_unit_moment_0 = mm.output_unit_items[0]
assert output_unit_moment_0['label'] == 'Flux'
assert output_unit_moment_0['unit_str'] == 'Jy'

# TODO: Failing because of dev version of upstream dependency, figure
# out which one
# assert mm.calculate_moment()

# TODO: This test should pass once continuum subtraction works with
# flux to surface brightness conversion
# mm.continuum.selected = 'Surrounding'
#
# assert mm.calculate_moment()
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,15 @@ def extract(self, return_bg=False, add_data=True, **kwargs):
pix_scale_factor = self.aperture_area_along_spectral * self.spectral_cube.meta.get('PIXAR_SR', 1.0) # noqa
spec.meta['_pixel_scale_factor'] = pix_scale_factor

# inform the user if scale factor keyword not in metadata
if 'PIXAR_SR' not in self.spectral_cube.meta:
snackbar_message = SnackbarMessage(
("PIXAR_SR FITS header keyword not found when parsing spectral cube. "
"Flux/Surface Brightness will use default PIXAR_SR value of 1 sr/pix^2."),
color="warning",
sender=self)
self.hub.broadcast(snackbar_message)

# stuff for exporting to file
self.extracted_spec = spec
self.extracted_spec_available = True
Expand Down
6 changes: 2 additions & 4 deletions jdaviz/configs/imviz/plugins/coords_info/coords_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from jdaviz.core.marks import PluginScatter, PluginLine
from jdaviz.core.registries import tool_registry
from jdaviz.core.template_mixin import TemplateMixin, DatasetSelectMixin
from jdaviz.utils import _eqv_pixar_sr

__all__ = ['CoordsInfo']

Expand Down Expand Up @@ -561,10 +562,7 @@ def _copy_axes_to_spectral():
# temporarily here, may be removed after upstream units handling
# or will be generalized for any sb <-> flux
if '_pixel_scale_factor' in sp.meta:
eqv = [(u.MJy / u.sr,
u.MJy,
lambda x: (x * sp.meta['_pixel_scale_factor']),
lambda x: x)]
eqv = u.spectral_density(sp.spectral_axis) + _eqv_pixar_sr(sp.meta['_pixel_scale_factor']) # noqa
disp_flux = sp.flux.to_value(viewer.state.y_display_unit, eqv)
else:
disp_flux = sp.flux.to_value(viewer.state.y_display_unit,
Expand Down
15 changes: 13 additions & 2 deletions jdaviz/configs/specviz/plugins/line_analysis/line_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@
# don't need these if statements
if function == "Line Flux":
flux_unit = spec_subtracted.flux.unit
if flux_unit == u.dimensionless_unscaled:
add_flux = True
flux_unit = u.Unit(self.spectrum_viewer.state.y_display_unit)
else:
add_flux = False
# If the flux unit is equivalent to Jy, or Jy per spaxel for Cubeviz,
# enforce integration in frequency space
if (flux_unit.is_equivalent(u.Jy) or
Expand All @@ -300,7 +305,10 @@
uncertainty=spec_subtracted.uncertainty)

try:
raw_result = analysis.line_flux(freq_spec)
if add_flux:
raw_result = analysis.line_flux(freq_spec) * flux_unit

Check warning on line 309 in jdaviz/configs/specviz/plugins/line_analysis/line_analysis.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/specviz/plugins/line_analysis/line_analysis.py#L309

Added line #L309 was not covered by tests
else:
raw_result = analysis.line_flux(freq_spec)
except ValueError as e:
# can happen if interpolation out-of-bounds or any error from specutils
# let's avoid the whole app crashing and instead expose the error to the
Expand Down Expand Up @@ -332,7 +340,10 @@
flux=spec_subtracted.flux,
uncertainty=spec_subtracted.uncertainty)
try:
raw_result = analysis.line_flux(wave_spec)
if add_flux:
raw_result = analysis.line_flux(wave_spec) * flux_unit
else:
raw_result = raw_result = analysis.line_flux(wave_spec)
except ValueError as e:
# can happen if interpolation out-of-bounds or any error from specutils
# let's avoid the whole app crashing and instead expose the error to the
Expand Down
pllim marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,28 @@ def test_conv_wave_flux(specviz_helper, spectrum1d, uncert):
assert u.Unit(viewer.state.y_display_unit) == u.Unit(new_flux)


def test_conv_no_data(specviz_helper):
def test_conv_no_data(specviz_helper, spectrum1d):
"""plugin unit selections won't have valid choices yet, preventing
attempting to set display units."""
plg = specviz_helper.plugins["Unit Conversion"]
# spectrum not load is in Flux units, sb_unit and flux_unit
# should be enabled, flux_or_sb should not be
assert hasattr(plg, 'sb_unit')
assert hasattr(plg, 'flux_unit')
assert not hasattr(plg, 'flux_or_sb')
with pytest.raises(ValueError, match="no valid unit choices"):
plg.spectral_unit = "micron"
assert len(specviz_helper.app.data_collection) == 0

specviz_helper.load_data(spectrum1d, data_label="Test 1D Spectrum")
plg = specviz_helper.plugins["Unit Conversion"]

# spectrum loaded in Flux units, make sure sb_units don't
# display in the API and exposed translation isn't possible
assert hasattr(plg, 'flux_unit')
assert not hasattr(plg, 'sb_unit')
assert not hasattr(plg, 'flux_or_sb')


@pytest.mark.skipif(ASTROPY_LT_5_3, reason='this feature relies on astropy v5.3+')
def test_non_stddev_uncertainty(specviz_helper):
Expand Down Expand Up @@ -140,40 +154,21 @@ def test_unit_translation(cubeviz_helper):
cubeviz_helper.load_regions(CirclePixelRegion(center, radius=2.5))

uc_plg = cubeviz_helper.plugins['Unit Conversion']
# we can get rid of this after all spectra pass through
# spectral extraction plugin
extract_plg = cubeviz_helper.plugins['Spectral Extraction']

extract_plg.aperture = extract_plg.aperture.choices[-1]
extract_plg.aperture_method.selected = 'Exact'
extract_plg.wavelength_dependent = True
extract_plg.function = 'Sum'
# set so pixel scale factor != 1
extract_plg.reference_spectral_value = 0.000001

# all spectra will pass through spectral extraction,
# this will store a scale factor for use in translations.
collapsed_spec = extract_plg.extract()

# test that the scale factor was set
assert np.all(collapsed_spec.meta['_pixel_scale_factor'] != 1)
assert np.all(cubeviz_helper.app.data_collection['Spectrum (sum)'].meta['_pixel_scale_factor'] != 1) # noqa

# When the dropdown is displayed, this ensures the loaded
# data collection item units will be used for translations.
uc_plg._obj.show_translator = True
assert uc_plg._obj.flux_or_sb_selected == 'Flux'

# to have access to display units
viewer_1d = cubeviz_helper.app.get_viewer(
cubeviz_helper._default_spectrum_viewer_reference_name)

# for testing _set_flux_or_sb()
uc_plg._obj.show_translator = False

# change global y-units from Flux -> Surface Brightness
uc_plg._obj.flux_or_sb_selected = 'Surface Brightness'

uc_plg._obj.show_translator = True
assert uc_plg._obj.flux_or_sb_selected == 'Surface Brightness'
y_display_unit = u.Unit(viewer_1d.state.y_display_unit)

Expand All @@ -196,42 +191,35 @@ def test_sb_unit_conversion(cubeviz_helper):
uc_plg = cubeviz_helper.plugins['Unit Conversion']
uc_plg.open_in_tray()

# ensure that per solid angle cube defaults to Flux spectrum
assert uc_plg.flux_or_sb == 'Flux'
# flux choices is populated with flux units
assert uc_plg.flux_unit.choices

# to have access to display units
viewer_1d = cubeviz_helper.app.get_viewer(
cubeviz_helper._default_spectrum_viewer_reference_name)

uc_plg._obj.show_translator = True
uc_plg.flux_or_sb.selected = 'Surface Brightness'

# Surface Brightness conversion
uc_plg.flux_or_sb_unit = 'Jy / sr'
uc_plg.sb_unit = 'Jy / sr'
y_display_unit = u.Unit(viewer_1d.state.y_display_unit)
assert y_display_unit == u.Jy / u.sr

# Try a second conversion
uc_plg.flux_or_sb_unit = 'W / Hz sr m2'
uc_plg.sb_unit = 'W / Hz sr m2'
y_display_unit = u.Unit(viewer_1d.state.y_display_unit)
assert y_display_unit == u.Unit("W / (Hz sr m2)")

# really a translation test, test_unit_translation loads a Flux
# cube, this test load a Surface Brightness Cube, this ensures
# two-way translation
uc_plg.flux_or_sb_unit = 'MJy / sr'
uc_plg.sb_unit = 'MJy / sr'
y_display_unit = u.Unit(viewer_1d.state.y_display_unit)

# we can get rid of this after all spectra pass through
# spectral extraction plugin
extract_plg = cubeviz_helper.plugins['Spectral Extraction']
extract_plg.aperture = extract_plg.aperture.choices[-1]
extract_plg.aperture_method.selected = 'Exact'
extract_plg.wavelength_dependent = True
extract_plg.function = 'Sum'
extract_plg.reference_spectral_value = 0.000001
extract_plg.extract()

uc_plg._obj.show_translator = True
uc_plg._obj.flux_or_sb_selected = 'Flux'
uc_plg.flux_or_sb_unit = 'MJy'
uc_plg.flux_unit = 'MJy'
y_display_unit = u.Unit(viewer_1d.state.y_display_unit)

assert y_display_unit == u.MJy
Expand Down
Loading
Loading