spacetelescope / jdaviz

JWST astronomical data analysis tools in the Jupyter platform
https://jdaviz.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
139 stars 74 forks source link

Specviz2d: smoothed spectrum not showing #1956

Closed Jdaviz-Triage-Bot closed 1 year ago

Jdaviz-Triage-Bot commented 1 year ago

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 commented 1 year ago

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
duytnguyendtn commented 1 year ago

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?

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 commented 1 year ago

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: https://github.com/spacetelescope/jdaviz/blob/f8b31919df8ff91d3db90c9a644b5a276a53295f/jdaviz/app.py#L442-L486 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.