diff --git a/packages/esssans/docs/user-guide/loki/loki-detector-diagnostics.ipynb b/packages/esssans/docs/user-guide/loki/loki-detector-diagnostics.ipynb index ed6010e7b..0d165be92 100644 --- a/packages/esssans/docs/user-guide/loki/loki-detector-diagnostics.ipynb +++ b/packages/esssans/docs/user-guide/loki/loki-detector-diagnostics.ipynb @@ -10,7 +10,7 @@ "In this notebook, we showcase two tools that can be used to investigate/debug the Loki detectors:\n", "\n", "- `LokiBankViewer`: a 2D view of all detector panels with sliders to select straw and layer\n", - "- `InstrumentView`: a 3D view of detector panels with toggle buttons for panel selection" + "- `scippneutron.instrument_view`: a 3D view of the detector panels" ] }, { @@ -110,10 +110,10 @@ "id": "8", "metadata": {}, "source": [ - "## The `InstrumentView`\n", + "## The `scippneutron.instrument_view`\n", "\n", - "We histogram the data into an additional `event_time_offset` dimension,\n", - "which will be controlled via a slider below the instrument view." + "We histogram the data into an `event_time_offset` dimension, which will be\n", + "controlled via a slider below the instrument view." ] }, { @@ -123,7 +123,14 @@ "metadata": {}, "outputs": [], "source": [ - "dhist = data.hist(event_time_offset=200)" + "time_bins = sc.linspace(\n", + " 'event_time_offset',\n", + " start=0.0,\n", + " stop=1.0 / 14.0,\n", + " num=201,\n", + " unit='s',\n", + ").to(unit='ns')\n", + "dhist = data.hist(event_time_offset=time_bins)" ] }, { @@ -146,9 +153,9 @@ "metadata": {}, "outputs": [], "source": [ - "from ess.loki.diagnostics import InstrumentView\n", + "import scippneutron as scn\n", "\n", - "InstrumentView(dhist, dim='event_time_offset')" + "scn.instrument_view(dhist, dim='event_time_offset')" ] } ], @@ -168,7 +175,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.7" + "version": "3.12.13" } }, "nbformat": 4, diff --git a/packages/esssans/src/ess/loki/diagnostics.py b/packages/esssans/src/ess/loki/diagnostics.py index 413f1a50b..a5f3b6f4f 100644 --- a/packages/esssans/src/ess/loki/diagnostics.py +++ b/packages/esssans/src/ess/loki/diagnostics.py @@ -235,142 +235,3 @@ def update_node_routing(self, change: dict) -> None: self.update_cmin({'new': self.cmap_vmin.value}) self._lock = False self.update_cmax({'new': self.cmap_vmax.value}) - - -def _to_data_group(data: sc.DataArray | sc.DataGroup | dict) -> sc.DataGroup: - if isinstance(data, sc.DataArray): - data = sc.DataGroup({data.name or "data": data}) - elif isinstance(data, dict): - data = sc.DataGroup(data) - return data - - -@pp.node -def _pre_process(da: sc.DataArray, dim: str) -> sc.DataArray: - dims = list(da.dims) - if dim is not None: - dims.remove(dim) - out = da.flatten(dims=dims, to="pixel") - sel = sc.isfinite(out.coords["position"]) - return out[sel] - - -class InstrumentView(ipw.VBox): - """ - Three-dimensional visualization of the Loki instrument. - The instrument view is capable of slicing the input data with a slider widget along - a dimension (e.g. ``tof``) by using the ``dim`` argument. - It will also generate toggle buttons to hide/show the different modules that make up - the Loki detectors. - - Parameters - ---------- - data: - Data to visualize. The data can be a single detector module (``DataArray``), - or a group of detector modules (``dict`` or ``DataGroup``). - The data must contain a ``position`` coordinate. - dim: - Dimension to use for the slider. No slider will be shown if this is None. - pixel_size: - Size of the pixels. - autoscale: - If ``True``, the color scale will be automatically adjusted to the data as it - gets updated. This can be somewhat expensive with many pixels, so it is set to - ``False`` by default. - **kwargs: - Additional arguments are forwarded to the scatter3d figure - (see https://scipp.github.io/plopp/generated/plopp.scatter3d.html). - """ - - def __init__( - self, - data: sc.DataArray | sc.DataGroup | dict, - dim: str | None = None, - pixel_size: float | sc.Variable | None = None, - **kwargs, - ): - from plopp.widgets import SliceWidget, slice_dims - - if dim and isinstance(data, sc.DataArray) and dim in data.dims[:-1]: - data = data.transpose([d for d in data.dims if d != dim] + [dim]) - - if dim and isinstance(data, sc.DataGroup): - data = data.copy(deep=False) - for k, v in data.items(): - if dim in v.dims[:-1]: - data[k] = v.transpose([d for d in v.dims if d != dim] + [dim]) - - self.data = _to_data_group(data) - self.pre_process_nodes = { - key: _pre_process(da, dim) for key, da in self.data.items() - } - - self._widgets = [] - - if dim is not None: - self.slider = SliceWidget(next(iter(self.data.values())), dims=[dim]) - self.slider.controls[dim].slider.layout = {"width": "600px"} - self.slider_node = pp.widget_node(self.slider) - self.slice_nodes = { - key: slice_dims(n, self.slider_node) - for key, n in self.pre_process_nodes.items() - } - to_scatter = self.slice_nodes - self._widgets.append(self.slider) - else: - self.slice_nodes = self.pre_process_nodes - to_scatter = self.pre_process_nodes - - kwargs.setdefault('cbar', True) - self.fig = pp.scatter3d( - to_scatter, - pos="position", - pixel_size=1.0 * sc.Unit("cm") if pixel_size is None else pixel_size, - **kwargs, - ) - - self._widgets.insert(0, self.fig) - - if len(self.data) > 1: - self._add_module_control() - - super().__init__(self._widgets) - - def _add_module_control(self): - import ipywidgets as ipw - - self.cutting_tool = self.fig.bottom_bar[0] - self._node_backup = list(self.cutting_tool._original_nodes) - self.artist_mapping = dict( - zip(self.data.keys(), self.fig.artists.keys(), strict=True) - ) - self.buttons = { - key: ipw.ToggleButton( - value=True, description=f"{i}", layout={"width": "initial"} - ) - for i, key in enumerate(self.data) - } - - self.modules_widget = ipw.HBox( - [ - ipw.HTML(value="Detector banks:     "), - *self.buttons.values(), - ] - ) - for key, b in self.buttons.items(): - b.key = key - b.observe(self._check_visibility, names="value") - self._widgets.insert(0, self.modules_widget) - - def _check_visibility(self, _): - active_nodes = [ - node_id - for key, node_id in self.artist_mapping.items() - if self.buttons[key].value - ] - for n in self._node_backup: - self.fig.artists[n.id].points.visible = n.id in active_nodes - self.cutting_tool._original_nodes = [ - n for n in self._node_backup if n.id in active_nodes - ] - self.cutting_tool.update_state() diff --git a/packages/esssans/tests/loki/diagnostics_test.py b/packages/esssans/tests/loki/diagnostics_test.py index f89600cb7..26cd932ba 100644 --- a/packages/esssans/tests/loki/diagnostics_test.py +++ b/packages/esssans/tests/loki/diagnostics_test.py @@ -6,7 +6,7 @@ import pytest import scipp as sc from ess import loki -from ess.loki.diagnostics import InstrumentView, LokiBankViewer +from ess.loki.diagnostics import LokiBankViewer from ess.sans.types import ( BeamCenter, Filename, @@ -88,11 +88,3 @@ def test_loki_bank_viewer_change_bank(histogrammed_loki_data): viewer.tabs.selected_index = 2 # Change back to all banks viewer.tabs.selected_index = 0 - - -def test_creat_loki_instrument_view(histogrammed_loki_data): - InstrumentView(histogrammed_loki_data) - - -def test_creat_loki_instrument_view_with_dim_slider(loki_data): - InstrumentView(loki_data.hist(event_time_offset=10), dim='event_time_offset')