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

Dream instrument view #184

Merged
merged 36 commits into from
Nov 23, 2023
Merged

Dream instrument view #184

merged 36 commits into from
Nov 23, 2023

Conversation

nvaytet
Copy link
Member

@nvaytet nvaytet commented Jun 12, 2023

- Add some helper functions to manipulate DREAM data in logical dimensions
- Add a dream.instrument_view that can have a tof slider.
- Add notebook with dream diagnostics plots and instrument views

This adds a dream.instrument_view that can have a tof slider.
It also has some checkboxes to hide/show the separate detector modules.
Screenshot at 2023-11-17 09-08-30

Also reduce duplicate code in data registries.

da:
DataArray to reshape.
"""
return da.group('module', 'segment', 'counter', 'wire', 'strip')
Copy link
Member

Choose a reason for hiding this comment

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

Are those specific to the mantle? The endcaps and high-res detectors may be different?

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 seems all of them have the same components? (none are empty in the datagroup)
Screenshot at 2023-06-13 12-37-05

Copy link
Member

Choose a reason for hiding this comment

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

👍 (even though the NeXus file I have does not have the sumo* groups, but uses other names)

Comment on lines 23 to 28
in_dims = set(da.dims)
out_dims = set(dims)
if (not dims) or (in_dims == out_dims):
return da
grouped = da.group(*list(out_dims - in_dims))
return grouped.bins.concat(list(set(grouped.dims) - out_dims))
Copy link
Member

Choose a reason for hiding this comment

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

set is unordered, I think this may be a problem here?

out_dims = set(dims)
if (not dims) or (in_dims == out_dims):
return da
grouped = da.group(*list(out_dims - in_dims))
Copy link
Member

Choose a reason for hiding this comment

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

This won't work, I think: If you group with a dim-name then you will only get those groups that exist. So if you have, e.g., a pixel without any neutrons it will not be present in the output.

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 guess this is works for the Geant4 csv files where you are getting the events, which don't list the zeros anyway.
But as we have often said, the zeros are important.
I'll ask Celine for that Nexus file so I can see how it is stored there and get the data into a form that is equivalent to the Nexus file.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think it should work for the CSV files either: If there is no event you will not get a group. Try loading a shorter section of the file, I think it will fail here (when reshaping).

@nvaytet nvaytet changed the title Dream diagnostics Dream instrument view Sep 13, 2023
import plopp as pp
import scipp as sc

DREAM_DETECTOR_DIMENSIONS = ('module', 'segment', 'counter', 'wire', 'strip')
Copy link
Member

Choose a reason for hiding this comment

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

We should define this in a common place, also used by loaders, see my as well as @jl-wynen's PRs.

z: Optional[str] = None,
pos: Optional[str] = None,
dim: Optional[str] = None,
pixel_size: Union[sc.Variable, float] = DREAM_PIXEL_SIZE,
Copy link
Member

Choose a reason for hiding this comment

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

Note that we are violating the mutable-default-arg rule. We have the same problem in other places. Not sure if we should outright avoid it, or allow it by having the readonly flag set on a variable?

Comment on lines 90 to 97
if not isinstance(data, sc.DataArray):
data = sc.concat(
[
_preprocess_data(data=da, to_be_flattened=dims, dim=dim, to=to)
for da in data.values()
],
dim=to,
)
Copy link
Member

Choose a reason for hiding this comment

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

Didn't pythreejs support multiple point clouds? Could different detector banks be added separately, potentially with a check box to toggle them on and off?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, you're right. Having toggle boxes may be a nice addition.
To be honest, I am not so happy about how the implementation is shaping here. I think I'd rather first work on scipp/plopp#247 and then it should be easier to do this one.

f'x ({x}), y ({y}), and z ({z}) must be None.'
)
coords = {
(x := f'{pos}.x'): data.coords[pos].fields.x,
Copy link
Member

Choose a reason for hiding this comment

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

Why walrus? You are not using x?

@nvaytet nvaytet mentioned this pull request Oct 2, 2023
@nvaytet nvaytet marked this pull request as ready for review November 16, 2023 12:37
Copy link
Collaborator

@celinedurniak celinedurniak Nov 17, 2023

Choose a reason for hiding this comment

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

The instrument view works correctly. But instead of displaying the SUMOs, I think that for consistency, the 4 tick boxes for the SUMOs should be replaced by 2 tick boxes for the endcap backward and forward detectors.
Note that for the endcap detectors, SUMOs are numbered starting from 3.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right now, I am just using the keys in the data group that is supplied as input, to keep it flexible.
If you want to replace the sumos with end-caps, it's probably at the data loading that we should do that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, using dream.load_geant4_csv should make this go away.

docs/instruments/dream/dream-instrument-view.ipynb Outdated Show resolved Hide resolved
},
"outputs": [],
"source": [
"full_view = dream.instrument_view(data, dim='tof')\n",
Copy link
Member

Choose a reason for hiding this comment

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

Is the intention here that one could pass dim='wavelength' after transforming the coords? Might be worth mentioning here, since it may not be obvious to readers.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a sentence.

Comment on lines +5 to -19
from ..data import Registry

def _make_pooch():
import pooch
_registry = Registry(
instrument='amor',
files={
"reference.nxs": "md5:56d493c8051e1c5c86fb7a95f8ec643b",
"sample.nxs": "md5:4e07ccc87b5c6549e190bc372c298e83",
},
version='1',
)

return pooch.create(
path=pooch.os_cache('ess/amor'),
env='ESS_AMOR_DATA_DIR',
base_url='https://public.esss.dk/groups/scipp/ess/amor/{version}/',
version=_version,
registry={
"reference.nxs": "md5:56d493c8051e1c5c86fb7a95f8ec643b",
"sample.nxs": "md5:4e07ccc87b5c6549e190bc372c298e83",
},
Copy link
Member

Choose a reason for hiding this comment

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

This code has been moved already, the refactor will get lost (but that is ok, I guess).

Comment on lines 19 to 22
@pp.node
def slice_range(da, trunc_range):
min_tr, max_tr = trunc_range
return da['tof', min_tr:max_tr].sum('tof')
Copy link
Member

Choose a reason for hiding this comment

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

This seems to assume that data is binned (or histogrammed) with a potentially fine resolution? I believe DREAM has some high-resolution settings, where we may need thousands of TOF bins. Combined with the large number of pixels this may use too much memory. We can work around this using something event filtering.

@celinedurniak How many (how small) TOF (or wavelength) bins will you need to visualize?

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, the slice_range function is no longer used. In a previous iteration, I used to have a range slider which allowed you to change the tof range to sum over, like the Mantid instrument view.

However, I now switched to just slicing single bins, because it is much faster.

That said, I think the issue you raise is still valid?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it is still a problem.

if dim is not None:
dims.remove(dim)
out = da.flatten(dims=dims, to='pixel')
sel = sc.isfinite(out.coords['x'])
Copy link
Member

Choose a reason for hiding this comment

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

What is 'x'? The literal X-coord? Why do we need to select based on it?

Copy link
Member Author

@nvaytet nvaytet Nov 20, 2023

Choose a reason for hiding this comment

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

This is because of the way we compute the coordinates for the pixels. We do a mean on the positions inside the bins, but some of the bins are empty (no events reached that pixel).
So some positions end up being NaN, and the 3D view complains about nans (nothing is rendered).

So I need some way to filter out NaNs. I agree that maybe the instrument view shouldn't silently drop NaNs, and maybe this should be done explicitly in the notebook?

Filtering on NaNs here was easy because I was internally flattening the data. Using advanced indexing only works using a 1D mask. But I think it makes sense to keep the data with logical dimensions in the notebook.
Which is why I wanted to filter here...

I don't know what the best solution is.

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 thought about filling the empty pixels with a single event with zero weight, which would then appear as a dark pixel in the instrument view, but I then do not know what the positions of those pixels should be, because they are absent from the original table.

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense.

From NeXus files we will (according to the current plan) get a position coord instead. Will you handle this as well?

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 think that if I use the loader Jan-Lukas wrote, I get a position coord, so I will probably just filter on that.

Comment on lines 117 to 123
"""
Three-dimensional visualization of the DREAM instrument.

Parameters
----------
data:
Data to visualize.
Copy link
Member

Choose a reason for hiding this comment

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

Misses some info about the expected structure (datagroup, subgroups, dims, coords) of the input.

self.fig.artists[cut_nodes[key].id].points.visible = val


def instrument_view(data, dim=None, pixel_size=None, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

Type hints?

@nvaytet nvaytet merged commit d08f1a7 into main Nov 23, 2023
7 checks passed
@nvaytet nvaytet deleted the dream_diagnostics branch November 23, 2023 13:51
@nvaytet nvaytet mentioned this pull request Apr 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants