Skip to content

Commit

Permalink
Add option of using self derived interpolated profile for horne extra…
Browse files Browse the repository at this point in the history
…ct in specviz2d/spectral extraction plugin (#2845)

* add option for self profile for horne extract in spectral extraction

* bump up specreduce version

* review comments

* move change log entry to 3.11

* typo
  • Loading branch information
cshanahan1 authored Jun 21, 2024
1 parent 55e6f6a commit 69fabb7
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Specviz
Specviz2d
^^^^^^^^^


API Changes
-----------

Expand Down Expand Up @@ -132,6 +133,7 @@ Specviz

Specviz2d
^^^^^^^^^
- Add option to use self-derived spatial profile for Horne extract in spectral extraction plugin. [#2845]

API Changes
-----------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ class SpectralExtraction(PluginTemplateMixin):
* ``ext_type`` (:class:`~jdaviz.core.template_mixin.SelectPluginComponent`)
* :attr:`ext_width` :
full width of the extraction window.
* ``horne_ext_profile`` (:class:`~jdaviz.core.template_mixin.SelectPluginComponent`):
For Horne extract, choice of 'Gaussian' or 'Self (interpolated)' to use
empirical profile from data.
* :attr:`self_prof_n_bins` :
Number of bins to use when computing the self-derived profile for Horne Extract.
* :attr:`self_prof_interp_degree_x` :
Interpolation degree (in X) to use when computing the self-derived profile
for Horne Extract.
* :attr:`self_prof_interp_degree_y` :
Interpolation degree (in Y) to use when computing the self-derived profile
for Horne Extract.
* ``ext_add_results`` (:class:`~jdaviz.core.template_mixin.AddResults`)
* :meth:`import_extract`
* :meth:`export_extract`
Expand Down Expand Up @@ -190,6 +201,13 @@ class SpectralExtraction(PluginTemplateMixin):
ext_type_items = List().tag(sync=True)
ext_type_selected = Unicode().tag(sync=True)

horne_ext_profile_items = List().tag(sync=True)
horne_ext_profile_selected = Unicode().tag(sync=True)

self_prof_n_bins = IntHandleEmpty(10).tag(sync=True)
self_prof_interp_degree_x = IntHandleEmpty(1).tag(sync=True)
self_prof_interp_degree_y = IntHandleEmpty(1).tag(sync=True)

ext_width = FloatHandleEmpty(0).tag(sync=True)

ext_uncert_warn = Bool(False).tag(sync=True)
Expand Down Expand Up @@ -313,6 +331,11 @@ def __init__(self, *args, **kwargs):
selected='ext_type_selected',
manual_options=['Boxcar', 'Horne'])

self.horne_ext_profile = SelectPluginComponent(self,
items='horne_ext_profile_items',
selected='horne_ext_profile_selected',
manual_options=['Gaussian', 'Self (interpolated)']) # noqa

self.ext_add_results = AddResults(self, 'ext_results_label',
'ext_results_label_default',
'ext_results_label_auto',
Expand Down Expand Up @@ -350,6 +373,10 @@ def user_api(self):
'export_bg', 'export_bg_img', 'export_bg_sub',
'ext_dataset', 'ext_trace', 'ext_type',
'ext_width', 'ext_add_results',
'horne_ext_profile',
'self_prof_n_bins',
'self_prof_interp_degree_x',
'self_prof_interp_degree_y',
'import_extract',
'export_extract', 'export_extract_spectrum'))

Expand Down Expand Up @@ -600,7 +627,9 @@ def _interaction_in_bg_step(self, event={}):
self.active_step = 'bg'

@observe('is_active', 'ext_dataset_selected', 'ext_trace_selected',
'ext_type_selected', 'ext_width', 'active_step')
'ext_type_selected', 'ext_width', 'active_step',
'horne_ext_profile_selected', 'self_prof_n_bins',
'self_prof_interp_degree_x', 'self_prof_interp_degree_y')
@skip_if_not_tray_instance()
@skip_if_no_updates_since_last_active()
def _interaction_in_ext_step(self, event={}):
Expand Down Expand Up @@ -925,11 +954,37 @@ def export_extract(self, **kwargs):
if self.ext_type_selected == 'Boxcar':
ext = extract.BoxcarExtract(inp_sp2d, trace, width=self.ext_width)
elif self.ext_type_selected == 'Horne':
spatial_profile = None
if inp_sp2d.uncertainty is None:
inp_sp2d.uncertainty = VarianceUncertainty(np.ones_like(inp_sp2d.data))

if not hasattr(inp_sp2d.uncertainty, 'uncertainty_type'):
inp_sp2d.uncertainty = StdDevUncertainty(inp_sp2d.uncert)
ext = extract.HorneExtract(inp_sp2d, trace)

if self.horne_ext_profile_selected == 'Self (interpolated)':

# check inputs
if self.self_prof_n_bins <= 0:
raise ValueError('`self_prof_n_bins` must be greater than 0.')
if self.self_prof_interp_degree_x <= 0:
raise ValueError('`self_prof_interp_degree_x` must be greater than 0.')
if self.self_prof_interp_degree_y <= 0:
raise ValueError('`self_prof_interp_degree_y` must be greater than 0.')

# setup dict of interpolation options
n_bins_interpolated_profile = self.self_prof_n_bins
interp_degree = (self.self_prof_interp_degree_x, self.self_prof_interp_degree_y)
spatial_profile = {'name': 'interpolated_profile',
'n_bins_interpolated_profile': n_bins_interpolated_profile,
'interp_degree': interp_degree}

elif self.horne_ext_profile_selected == 'Gaussian':
spatial_profile = 'gaussian'

else:
raise ValueError("Horne extraction profile must either be 'Gaussian' or 'Self (interpolated)'") # noqa

ext = extract.HorneExtract(inp_sp2d, trace, spatial_profile=spatial_profile)
else:
raise NotImplementedError(f"extraction type '{self.ext_type_selected}' not supported") # noqa

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,59 @@
</v-text-field>
</v-row>

<v-row v-if="ext_type_selected === 'Horne'">
<v-select
attach
:menu-props="{ left: true }"
:items=" horne_ext_profile_items.map(i => i.label)"
v-model="horne_ext_profile_selected"
label="Profile Type"
hint="Profile to use for Horne extractoin."
persistent-hint
></v-select>
</v-row>

<v-row v-if="horne_ext_profile_selected === 'Self (interpolated)'">
<v-text-field
label="N Bins"
type="number"
v-model.number=self_prof_n_bins
:rules="[() => self_prof_n_bins !== '' || 'This field is required',
() => self_prof_n_bins >0 || 'Number of bins must be greater than zero']"
hint="Number of bins used to measure self profile."
persistent-hint
>
</v-text-field>
</v-row>

<div v-if="ext_type_selected === 'Horne'">
<v-row v-if="horne_ext_profile_selected === 'Self (interpolated)'">
<v-text-field
label="Interpolation Degree (X)"
type="number"
v-model.number=self_prof_interp_degree_x
:rules="[() => self_prof_interp_degree_x !== '' || 'This field is required',
() => self_prof_interp_degree_x >0 || 'X interpolation degree must be be greater than zero']"
hint="Interpolation degree (X) to measure self profile."
persistent-hint
>
</v-text-field>
</v-row>

<v-row v-if="horne_ext_profile_selected === 'Self (interpolated)'">
<v-text-field
label="Interpolation Degree (Y)"
type="number"
v-model.number=self_prof_interp_degree_y
:rules="[() => self_prof_interp_degree_y !== '' || 'This field is required',
() => self_prof_interp_degree_y >0 || 'Y interpolation degree must be be greater than zero']"
hint="Interpolation degree (Y) to measure self profile."
persistent-hint
>
</v-text-field>
</v-row>
</div>

<plugin-add-results
:label.sync="ext_results_label"
:label_default="ext_results_label_default"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import gwcs
import pytest
from astropy.modeling import models
from astropy.nddata import VarianceUncertainty
from astropy.tests.helper import assert_quantity_allclose
import astropy.units as u
from astropy.utils.data import download_file
import numpy as np
from packaging.version import Version
from specreduce import tracing, background, extract
from specutils import Spectrum1D
Expand Down Expand Up @@ -185,3 +190,78 @@ def test_spectrum_on_top(specviz2d_helper):
pext = specviz2d_helper.app.get_tray_item_from_name('spectral-extraction')
assert pext.bg_type_selected == 'OneSided'
assert pext.bg_separation < 0


@pytest.mark.filterwarnings('ignore')
def test_horne_extract_self_profile(specviz2d_helper):

spec2d = np.zeros((40, 100))
spec2dvar = np.ones((40, 100))

for ii in range(spec2d.shape[1]):
mgaus = models.Gaussian1D(amplitude=10,
mean=(9.+(20/spec2d.shape[1])*ii),
stddev=2)
rg = np.arange(0, spec2d.shape[0], 1)
gaus = mgaus(rg)
spec2d[:, ii] = gaus

wave = np.arange(0, spec2d.shape[1], 1)
objectspec = Spectrum1D(spectral_axis=wave*u.m,
flux=spec2d*u.Jy,
uncertainty=VarianceUncertainty(spec2dvar*u.Jy*u.Jy))

specviz2d_helper.load_data(objectspec)
pext = specviz2d_helper.app.get_tray_item_from_name('spectral-extraction')

trace_fit = tracing.FitTrace(objectspec,
trace_model=models.Polynomial1D(degree=1),
window=13, peak_method='gaussian', guess=20)
pext.import_trace(trace_fit)

pext.ext_type.selected = "Horne"
pext.horne_ext_profile.selected = "Self (interpolated)"

# check that correct defaults are set
assert pext.self_prof_n_bins == 10
assert pext.self_prof_interp_degree_x == 1
assert pext.self_prof_interp_degree_y == 1

sp_ext = pext.export_extract_spectrum()

bg_sub = pext.export_bg_sub()

extract_horne_interp = extract.HorneExtract(bg_sub, trace_fit,
spatial_profile='interpolated_profile')

assert_quantity_allclose(extract_horne_interp.spectrum.flux, sp_ext.flux)

# now try changing from defaults
pext.self_prof_n_bins = 5
pext.self_prof_interp_degree_x = 2
pext.self_prof_interp_degree_y = 2

sp_ext = pext.export_extract_spectrum()
bg_sub = pext.export_bg_sub()

extract_horne_interp = extract.HorneExtract(bg_sub, trace_fit,
spatial_profile={'name': 'interpolated_profile',
'n_bins_interpolated_profile': 5,
'interp_degree': (2, 2)})

assert_quantity_allclose(extract_horne_interp.spectrum.flux, sp_ext.flux)

# test that correct errors are raised
pext.self_prof_n_bins = 0
with pytest.raises(ValueError, match='must be greater than 0'):
sp_ext = pext.export_extract_spectrum()

pext.self_prof_n_bins = 1
pext.self_prof_interp_degree_x = 0
with pytest.raises(ValueError, match='must be greater than 0'):
sp_ext = pext.export_extract_spectrum()

pext.self_prof_interp_degree_x = 1
pext.self_prof_interp_degree_y = 0
with pytest.raises(ValueError, match='`self_prof_interp_degree_y` must be greater than 0.'):
sp_ext = pext.export_extract_spectrum()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ dependencies = [
"voila>=0.4,<0.5",
"pyyaml>=5.4.1",
"specutils>=1.15",
"specreduce>=1.3.0,<1.4.0",
"specreduce>=1.4.1",
"photutils>=1.4",
"glue-astronomy>=0.10",
"asteval>=0.9.23",
Expand Down

0 comments on commit 69fabb7

Please sign in to comment.