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

Add FlatVoxelViewer for DREAM #90

Merged
merged 9 commits into from
Oct 25, 2024
Merged

Add FlatVoxelViewer for DREAM #90

merged 9 commits into from
Oct 25, 2024

Conversation

jl-wynen
Copy link
Member

Simple voxel diagnostics tool that can show all voxels and does not require summing or slicing.

@jl-wynen jl-wynen requested a review from nvaytet September 16, 2024 14:50
observer(change)


def _flat_voxel_figure(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the flattening scheme only apply to dream, or could we make this a generic tool for other instruments like the MaskingTool in scippneutron?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be generalised easily. Which other instruments have multi-logical-dimensional detectors?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of them? I remember when Morten was talking about the EFU, all instruments seems to have some logical ordering. Loki for sure has banks, tubes, straws, pixels.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest, we merge this here and ask for feedback. If that is positive, we can generalise the viewer and move it into ESSreduce. What do you think?

docs/user-guide/dream/dream-detector-diagnostics.ipynb Outdated Show resolved Hide resolved
docs/user-guide/dream/index.md Outdated Show resolved Hide resolved
src/ess/dream/diagnostics.py Outdated Show resolved Hide resolved


class FlatVoxelViewer(ipw.VBox):
def __init__(self, data: sc.DataGroup) -> None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add docstring?

src/ess/dream/diagnostics.py Outdated Show resolved Hide resolved
if names != 'value':
super().observe(handler, names, *args, **kwargs)
else:
self._value_observers.append(handler)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the mechanism with the value_observers. Maybe you can explain to me?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is about updating only once when dimensions are swapped between x and y. I tried to find a way to stop widgets from emitting messages. But I couldn't get this to work. So instead, I put a custom layer in between so that I can control what messages make it to the user.

All observers with names='value are registered in self._value_observers. And the actual handler for the toggle buttons is self._observe_values. This function calls the user handlers when appropriate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about having a lock?
Add a self._lock = False.

Then in the function that gets called on update:

def update(change):
    if self._lock:
        return
    if other.value == clicked.value:
        self._lock = True
        other.value = change["old"]
        self._lock = False
    ...

Would that work? It would simplify a lot of the logic I think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this would work. This would prevent updating within _DimensionSelector while the lock is held. But the observers of the button that control the actual figure would still get called on other.value = change["old"].

Copy link
Member

@nvaytet nvaytet Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the implementation I'm suggesting:

class _DimensionSelector(ipw.VBox):
    def __init__(self, dims: tuple[str, ...], callback: Callable) -> None:
        self._lock = False
        self._callback = callback

        style = {'button_width': '10em'}
        options = {dim.capitalize(): dim for dim in dims}
        default_h, default_v = self._default_dims(dims)
        self._horizontal_buttons = ipw.ToggleButtons(
            options=options, value=default_h, style=style
        )
        self._vertical_buttons = ipw.ToggleButtons(
            options=options, value=default_v, style=style
        )
        self._horizontal_buttons.observe(self.update, names='value')
        self._vertical_buttons.observe(self.update, names='value')

        super().__init__(
            [
                ipw.HBox([ipw.Label('X'), self._horizontal_buttons]),
                ipw.HBox([ipw.Label('Y'), self._vertical_buttons]),
            ]
        )

    def set_dims(self, new_dims: tuple[str, ...]) -> None:
        default_h, default_v = self._default_dims(new_dims)
        options = {dim.capitalize(): dim for dim in new_dims}
        self._lock = True
        self._horizontal_buttons.options = options
        self._vertical_buttons.options = options
        self._horizontal_buttons.value = default_h
        self._vertical_buttons.value = default_v
        self._lock = False

    @staticmethod
    def _default_dims(dims: tuple[str, ...]) -> tuple[str, str]:
        return dims[math.ceil(len(dims) / 2)], dims[0]

    @property
    def value(self):
        return {
            'horizontal': self._horizontal_buttons.value,
            'vertical': self._vertical_buttons.value,
        }

    def update(self, change: dict) -> None:
        if self._lock:
            return
        clicked = change['owner']
        other = (
            self._vertical_buttons
            if clicked is self._horizontal_buttons
            else self._horizontal_buttons
        )
        if other.value == clicked.value:
            self._lock = True
            other.value = change["old"]
            self._lock = False
        self._callback(change)

and then in the FlatVoxelViewer you would simply have

        self._dim_selector = _DimensionSelector(
            self._bank.dims, callback=self._update_view
        )

(note that in the _DimensionSelector you don't need to put the togglebuttons in boxes to change their options, you can just set togglebuttons.options = new_dims)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work. Thanks!

I don't think this solution is valid for multi-threaded applications. But I am not 100% confident about my old implementation either. And it should be fine because ipywidgets does not spawn threads, right?

src/ess/dream/diagnostics.py Outdated Show resolved Hide resolved
h_labels = [str(value) for value in h_coord.values]
v_labels = [str(value) for value in v_coord.values]

fig = flat.plot(rasterized=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably make rasterized a kwarg for creating the viewer? (with True as default).

src/ess/dream/diagnostics.py Outdated Show resolved Hide resolved
@jl-wynen
Copy link
Member Author

The code may need to be updated when #92 is merged. So I propose to wait for that PR.

@jl-wynen jl-wynen force-pushed the dream-diagnostics-plot branch from 92f8f1f to 4c370b3 Compare September 19, 2024 09:38
@jl-wynen jl-wynen force-pushed the dream-diagnostics-plot branch from fd2db06 to 980a14a Compare October 21, 2024 12:12
Simplifies the logic of suppressing duplicate and invalid updates. Courtesy of Neil Vaytet.
@jl-wynen jl-wynen force-pushed the dream-diagnostics-plot branch from 980a14a to de94bc6 Compare October 21, 2024 12:18
@jl-wynen jl-wynen merged commit 9e4572e into main Oct 25, 2024
4 checks passed
@jl-wynen jl-wynen deleted the dream-diagnostics-plot branch October 25, 2024 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants