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

Specviz2d: smoothed spectrum not showing #1956

Closed
Jdaviz-Triage-Bot opened this issue Jan 5, 2023 · 3 comments · Fixed by #2023
Closed

Specviz2d: smoothed spectrum not showing #1956

Jdaviz-Triage-Bot opened this issue Jan 5, 2023 · 3 comments · Fixed by #2023
Labels
bug Something isn't working specviz2d

Comments

@Jdaviz-Triage-Bot
Copy link

Reporter: Camilla Pacifici

Load a s2d file.
Use gaussian smooth plugin on 1d spectrum.
Smoothed spectrum is in the list of data, but does not show in the viewer.


DISCLAIMER: This issue was autocreated by the Jdaviz Issue Creation Bot on behalf of the reporter. If any information is incorrect, please contact Duy Nguyen

@kecnry
Copy link
Member

kecnry commented Jan 5, 2023

I can reproduce, it seems it is being plotted, but incorrectly at x=0. The underlying data object seems to be correct and have a matching spectral axis in pixels, so this could be a linking issue.

image

@pllim pllim added bug Something isn't working specviz2d labels Jan 17, 2023
@duytnguyendtn
Copy link
Collaborator

I've been digging into this effort and am copying over my notes to this ticket:

2023-01-27:

I am deep in a glue sink hole; this is safely no longer a 3 point ticket! I'm resurfacing and saving my progress so I don't forget, for my own future sanity.

The root of the issue is that the marks aren't generating properly; the data is being smoothed properly and produces a valid object. To confirm this, I smoothed the dataset in Specviz2D, extracted the smoothed dataset, and replotted it in Specviz; it renders properly:

image-2023-01-27-14-32-19-241

Back in Specviz2D, we can actually see that the data is intact because the "bouncing ball" actually tracks the smoothed data properly! It correctly falls off the main spectra as expected:

image-2023-01-27-14-34-02-316

We can reveal the problem by going into plot options and exaggerating the line width; we can see the smoothed spectrum IS plotting, it's just all squished at the end:

image-2023-01-27-14-39-06-017

If we inspect the spectrum-viewer's marks, we can see the root of the error:

image-2023-01-27-14-42-36-858

Specifically, these values are the DEFAULT values of the marks. This importantly suggests that the values are not being updated properly somewhere downstream, not that the values are being miscalculated somehow:

https://github.com/glue-viz/glue-jupyter/blob/d83403853887c61d9ebf94a5b4fe4048ddc9b90c/glue_jupyter/bqplot/profile/layer_artist.py#L41

self.line_mark = LinesClass(scales=self.view.scales, x=[0, 1], y=[0, 1])
The output from the smoothed spectra's marks makes sense when we see the resulting green spike. If I adjust the bounds of the viewer, it confirms what we're seeing in the marks:

image-2023-01-27-14-45-22-014

From here, we need to figure out what's happening with the marks. In following the cascade of events when the smoothed data is attempted to be added to the spectrum viewer, I found the following line, where the Y (or "profile") values are being forced to be calculated:

https://github.com/glue-viz/glue-jupyter/blob/d83403853887c61d9ebf94a5b4fe4048ddc9b90c/glue_jupyter/bqplot/profile/layer_artist.py#L157

   def _update_profile(self, force=False, **kwargs):
         ...
         if force or any(prop in changed for prop in ('layer', 'x_att', 'attribute', 'function', 'normalize', 'v_min', 'v_max', 'as_steps')):
         self._calculate_profile(reset=force)

I found the following traceback that is being swallowed up by glue, when calculating the Y ("profile") values:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue_jupyter\bqplot\profile\layer_artist.py", line 60, in _calculate_profile
    self._calculate_profile_thread(reset=reset)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue_jupyter\bqplot\profile\layer_artist.py", line 75, in _calculate_profile_thread
    self.state.update_profile(update_limits=False)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\viewers\profile\state.py", line 339, in update_profile
    axis_values = data[self.viewer_state.x_att, tuple(axis_view)]
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\data.py", line 592, in __getitem__
    return self.get_data(key, view=view)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\data.py", line 1416, in get_data
    result = comp[view]
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\component.py", line 208, in __getitem__
    return self._link.compute(self._data, key)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\component_link.py", line 166, in compute
    args = [data[join_component_view(f, view)] for f in self._from]
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\component_link.py", line 166, in <listcomp>
    args = [data[join_component_view(f, view)] for f in self._from]
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\data.py", line 592, in __getitem__
    return self.get_data(key, view=view)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\data.py", line 1416, in get_data
    result = comp[view]
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\component.py", line 208, in __getitem__
    return self._link.compute(self._data, key)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\component_link.py", line 187, in compute
    result = self._using(*args)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\component_link.py", line 395, in using
    return world2pixel_single_axis(self.coords, *args2[::-1], pixel_axis=self.ndim - 1 - self.index)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue\core\coordinate_helpers.py", line 102, in world2pixel_single_axis
    result = wcs.world_to_pixel_values(*world)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\glue_astronomy\translators\spectrum1d.py", line 80, in world_to_pixel_values
    pixel_arrays = [self.spectral_wcs.world_to_pixel_values(wx.ravel()).reshape(wx.shape),
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\gwcs\api.py", line 142, in world_to_pixel_values
    return self._remove_quantity_output(result, self.input_frame)
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\gwcs\api.py", line 81, in _remove_quantity_output
    result = tuple(r.to_value(unit) for r, unit in zip(result, frame.unit))
  File "e:\STScI\gitRepos\jdaviz\envmain\lib\site-packages\gwcs\api.py", line 81, in <genexpr>
    result = tuple(r.to_value(unit) for r, unit in zip(result, frame.unit))
AttributeError: 'numpy.ndarray' object has no attribute 'to_value' 

This is the code that swallows the exception:

https://github.com/glue-viz/glue-jupyter/blob/d83403853887c61d9ebf94a5b4fe4048ddc9b90c/glue_jupyter/bqplot/profile/layer_artist.py#L62

    def _calculate_profile(self, reset=False):
        try:
            self._calculate_profile_thread(reset=reset)
        except Exception as e:
            self._calculate_profile_error(sys.exc_info())
        else:
            self._calculate_profile_postthread() 

The reason why this is important is because it skips the self._calculate_profile_postthread() method. This postthread method is WHERE the x and y marks values are being updated:

https://github.com/glue-viz/glue-jupyter/blob/d83403853887c61d9ebf94a5b4fe4048ddc9b90c/glue_jupyter/bqplot/profile/layer_artist.py#L109-L110

        # Update the data values.
        if len(x) > 0:
            self.state.update_limits()
            # Normalize profile values to the [0:1] range based on limits
            if self._viewer_state.normalize:
                y = self.state.normalize_values(y)
            with self.line_mark.hold_sync():
                self.line_mark.x = x
                self.line_mark.y = y 

Figuring out why this traceback is firing is my biggest lead thus far to fixing the issue.

2023-02-10:

Coming back to this ticket after the crazy Jwebbinar effort sucking all my time, but the new eyes gave me a clearer view of what's going on. I'll try to describe it below:

One small piece of context I should leave here that I hadn't commented on yet is the source of the Traceback is fundamentally trying to convert the pixel axis to microns. It turns out the original data's wavelength axis is in microns. Keep this in mind for later.

In trying to remember where I left off, I decided to take a step back; rather than dive into trying to fix the traceback, I instead asked, "Why is this even happening to begin with?" Looking deeper at the callstack that leads up to the traceback, the "area" of glue where this is failing is in the component linking. By my eyes, the source of the error appears to be the resulting gaussian smooth (in pixels) trying to be linked against the original 2D spectrum, which is in microns!

So... where do I need to go from here?

  • How does the current 1D data in pixels work now? Presumably this would have happened before with the original 1D data. Is there a translation? Is linking ignored? Is this a "shadow" 2D data with a wcs NOT in microns? Is there a hack somewhere, where the linking is done against a pixel axis rather than a world axis?

  • This brings in much broader conversations about the Unit Conversion refactor that is underway. If we are providing a formal definition of unit equivalencies, presumably this wouldn't be an issue in the future. Will any solution I implement here be immediately dropped in favor of the unit handling work?

2023-02-15

I was able to get the gaussian data to plot properly by frankensteining it with the autocollapsed data. At least I now have something that will load. I'm going to look into what could possibly be the difference between the two. I've uploaded my notebook with the data: https://stsci.box.com/s/l8muozr3fqfo0vw01zlbqnmwotw83vpr

@duytnguyendtn
Copy link
Collaborator

duytnguyendtn commented Feb 21, 2023

Two other points I should mention as well:

In Specviz2D, we currently convert both the 2D and 1D spectral axes to pixels due to lack of support of uneven spectral axes (which is currently in progress). Kyle presented a theory that this may be causing the linking issue (see the source of the Traceback is fundamentally trying to convert the pixel axis to microns). Unfortunately disabling the two places where we force this did not seem to change the behavior.

Secondly, and this is where SME eyes may be useful, is that we have custom linking logic for Gaussian Smooth data products:

jdaviz/jdaviz/app.py

Lines 442 to 486 in f8b3191

try:
if linked_data.meta.get("Plugin", None) == 'GaussianSmooth':
raise AttributeError
dc.add_link(WCSLink(ref_data, linked_data))
except (AttributeError, IncompatibleWCS):
pc_ref = [str(id).split(" ")[-1][1] for id in ref_data.pixel_component_ids]
pc_linked = [str(id).split(" ")[-1][1] for id in linked_data.pixel_component_ids]
links = []
# This code loops through the returned locations of the x, y, and z
# axes in the pixel_coordinate_ids of the reference data. It matches
# the axes with the pixel_coordinate_ids of the linked data and links
# those axes. There are special rules for linking 2D and 3D data, which
# is as follows: 2D y to 3D z and 2D x to 3D y, and vice versa in the
# case of moment map and collapse data because they need to be transposed.
# See github comment:
# https://github.com/spacetelescope/jdaviz/pull/1412#discussion_r907630682
len_linked_pixel = len(linked_data.pixel_component_ids)
for ind, pixel_coord in enumerate(pc_ref):
ref_index = ind
if (len_linked_pixel == 2 and
(linked_data.meta.get("Plugin", None) in
['MomentMap', 'Collapse'])):
if pixel_coord == 'z':
linked_index = pc_linked.index('x')
elif pixel_coord == 'y':
linked_index = pc_linked.index('y')
else:
continue
elif len_linked_pixel == 2:
if pixel_coord == 'z':
linked_index = pc_linked.index('y')
elif pixel_coord == 'y':
linked_index = pc_linked.index('x')
else:
continue
elif pixel_coord in pc_linked:
linked_index = pc_linked.index(pixel_coord)
else:
continue
links.append(LinkSame(ref_data.pixel_component_ids[ref_index],
linked_data.pixel_component_ids[linked_index]))

Kyle had another theory that we're might be linking improperly here. Ignoring the special logic didn't change the behavior, and I haven't been able to find anything troubling in the linking logic yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working specviz2d
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants