[BIFROST] Implement support for a moving detector#101
Conversation
The positions and orientation of components after the sample position are, strictly speaking, time-dependent parameters. Previously their transformation chains lacked this fidelity due to limitations in creating the NeXus Structure JSON via `moreniius`. Those limitations have been lifted and newly-generated BIFROST simulation files include NXtransformation groups with NXlog constituents. In order to calculate the correct analyzer and sample scattering angle for each detector pixel, the BIFROST workflow performs some geometry calculations in the coordinate frame which rotates with the detector tank, around the sample position. Since the analyzers and detectors are stationary in this frame, we can (and must) remove any time-dependence from their positions and orientations. This PR introduces a quick-and-dirty hack to only use the first time-point from any time-dependent transformation result for the - sample - analyzers - detectors
The NeXus format specification now allows most base objects to specify two attributes which help define the *expected* beam path through a collection of instrument components: `@inputs` and `@outputs`. Updates to `moreniius` mean that the BIFROST NeXus Structure JSON now contains these attributes, including the one-to-many and many-to-one transitions between, e.g., the sample position and the 45 analyzers. Given a detector's HDF5 Group, it is possible to follow the @inputs attributes analagously to a depends-on chain to identify which analyzer the detector should have received its neutron events from. This commit adds functionality which has only been tested outside of the workflow, by defining the `DetectorAnalyzerMap` at repl scope and the patching `ess.bifrost.io.nexus._get_analyzer_for_detector_name` to use the static relational information. I imagine it needs work to mold it into an appropriate `sciline` workflow.
For time-dependent NeXus transformations.
| # This function is separate from load_analyzer_for_detector so we get the default | ||
| # behavior for resolving NXtransformations. |
There was a problem hiding this comment.
I don't get this comment. load_analyzer_for_detector does not appear to do anything related to NXtransformations. What are the uses of the two providers, and where do they differ?
There was a problem hiding this comment.
They need to be used together. load_analyzer_for_detector provides NeXusComponent[snx.NXcrystal, RunType] which get_calibrated_analyzer consumes. And https://github.com/scipp/ess/blob/c551f7eabddda0ffa7fdc8bd7bb82648e7ef80f7/packages/essreduce/src/ess/reduce/nexus/workflow.py#L286 extracts the transformation that is used for the other two arguments of get_calibrated_analyzer. So the 'default behaviour' refers to how ESSreduce extracts transformations and computes positions for components.
Does this make sense? If so, I will update the comment.
| def stepwise_transformation_time_filter(transform: sc.DataArray) -> sc.DataArray: | ||
| """Collapse runs of equal values into a single value. | ||
|
|
||
| This can be used as a time filter for NeXus transformations when the component | ||
| mostly stays at a position and only rarely moves. | ||
| For example, a stepwise scan across detector rotations. | ||
| """ | ||
| collapsed = _collapse_runs(transform, 'time') | ||
| if collapsed.sizes['time'] == 1: | ||
| return collapsed.squeeze('time') | ||
| return collapsed |
There was a problem hiding this comment.
Not sure why this and the np.isclose above is needed. EPICS PVs stream a setpoint, so there should be no need to filter out fluctuations (one might need to filter events during a motor move, but this code does not appear to do that).
There was a problem hiding this comment.
Do they stream a setpoint? To my understanding, that is different from a PV (which is the actual momentary value). Are you saying that NXlogs will have no noise?
Not sure why this [...] is needed.
By 'this', do you mean collapsing runs of equal values? This is about producing short extra dimensions instead of thousands of identical elements.
There was a problem hiding this comment.
Yes, setpoints are streamed. NXlogs will have nnoise or not, depending on whether it was written from a setpoint. If you have a real file, check the source attribute: If it ends in VAL it is a setpoint, if ends in RBV it is the (noisy) readback. There is also DMOV (done-moving, or something like that).
By 'this', do you mean collapsing runs of equal values? This is about producing short extra dimensions instead of thousands of identical elements.
Yes, I mean the collapse. What I mean is: We we would use the setpoint then there should not be any identical elements (provided we filter out intervals during move)? If using the readback, then the issue with many points during move does not disappear - we still need to remove those or may and with large extra dimensions.
There was a problem hiding this comment.
Do the files still contain repeated values for setpoints? Or only when the setpoint changes?
I'll have to check with Greg about what they want to use on BIFROST.
There was a problem hiding this comment.
No repeated values, not according to info I got. Only on change.
This implements support for a moving BIFROST detector vessel. Uses scipp/ess#246 and a custom time filter and detector assembly provider to broadcast the raw detector into a time dimension and then turn that into the (a3, a4) dimensions.