Skip to content

Conversation

@jokasimr
Copy link
Contributor

Fixes #205

@github-project-automation github-project-automation bot moved this to In progress in Development Board Nov 4, 2025
@jokasimr jokasimr moved this from In progress to Selected in Development Board Nov 4, 2025
@jokasimr jokasimr requested a review from jl-wynen November 4, 2025 14:41
Copy link
Member

@jl-wynen jl-wynen left a comment

Choose a reason for hiding this comment

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

Two overarching questions:

  • Is there a description of the maths somewhere? There are a lot of nontrivial equations here that don't seem to be explained.
  • How much of this is bespoke McStas code? We shouldn't mix that kind of code with code for measured data because that will make it difficult to know which functions to use once we have both.

from scippneutron.conversion.tof import dspacing_from_tof
from scipy.signal import find_peaks, medfilt

from .conversions import _t0_estimate, _time_of_arrival
Copy link
Member

Choose a reason for hiding this comment

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

Please don't import protected attributes. If you need them across modules, make them public. Possibly in a protected module if you don't want package users to use them.

if 'lambda' in da.coords:
da.coords['wavelength_estimate'] = da.coords.pop('lambda')

if da.coords['wavelength_estimate'].value == '0':
Copy link
Member

Choose a reason for hiding this comment

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

This is bad because it scatters parameters and associations of modes with parameters over the code. Can you combine these parameters in one place? E.g., a mapping from mode to dict / dataclass of parameters? Then these long if-else chains can be collapsed into dict-lookups.

@jokasimr
Copy link
Contributor Author

jokasimr commented Nov 5, 2025

Is there a description of the maths somewhere? There are a lot of nontrivial equations here that don't seem to be explained.

You're right that it could use some more documentation.
But I think it's easier if you point to specific places where you find there is insufficient explanations.

The tof math for these pulse shaping chopper modes is:

  1. Use a wavelength estimate to compute the time at the chopper.
  2. Use the time at the chopper in combination with the time at the detector to compute the time of flight.

How much of this is bespoke McStas code? We shouldn't mix that kind of code with code for measured data because that will make it difficult to know which functions to use once we have both.

The McStas specific stuff should be contained in the io module. I don't think there is any McStas specific stuff outside of there, if there is it should be changed.

@jokasimr jokasimr requested a review from jl-wynen November 5, 2025 08:37
@jl-wynen
Copy link
Member

jl-wynen commented Nov 5, 2025

The McStas specific stuff should be contained in the io module. I don't think there is any McStas specific stuff outside of there, if there is it should be changed.

True, but where will code for nexus files go? Also the io module?

_eto = event_time_offset
T = sc.scalar(1 / 14, unit='s').to(unit=_eto.unit)
tc = tc.to(unit=_eto.unit)
return sc.where(_eto >= tc % T, _eto, _eto + T)
Copy link
Member

Choose a reason for hiding this comment

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

Why is there a modulo here? 'time cutoff' sounds like the condition should be _eto >= tc.

Copy link
Contributor 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. tc should be in the interval [0, 1/14s], so the modulo does nothing.

Copy link
Member

Choose a reason for hiding this comment

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

Are there tests for this code? If not, can you add some? (I mean the additions in the PR overall.)

):
'''Does frame unwrapping based on the cutoff time `tc` and `event_time_ffset`.
Events before `tc` are assumed to come from the previous pulse.'''
_eto = event_time_offset
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
_eto = event_time_offset
eto = event_time_offset

It's unusual to use a protected local name like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, but spellcheck complained about eto for some reason, so I renamed it.

tc: sc.Variable,
):
'''Does frame unwrapping based on the cutoff time `tc` and `event_time_ffset`.
Events before `tc` are assumed to come from the previous pulse.'''
Copy link
Member

Choose a reason for hiding this comment

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

Some general comments on docstring style:

  • According to https://peps.python.org/pep-0257/ they should always use double quotes.
  • Sphinx will use the entire first paragraph as the summary which is displayed in the overview tables:
summary These get unwieldy when the summary is long. So as a general rule, it is best to limit the summary to a single line and then use a separate paragraph for the detailed description. See also the [coding convention](https://scipp.github.io/development/coding-conventions.html#docstrings)

else:
raise ValueError('Sample position entry not found in file.')
data, events, params, sample_pos, chopper_pos = _load_h5(
raise ValueError(f'Unkonwn chopper mode {mode}.')
Copy link
Member

Choose a reason for hiding this comment

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

Which chopper's position is this? And why do we need to compute it? Isn't it written in the file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's the "effective" chopper position. It's either the position of one of the choppers, or the midpoint between two choppers.

Copy link
Member

Choose a reason for hiding this comment

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

But of which chopper? It looks like there is only a single position in the code even though there are many choppers. Is it the t0 chopper?

Copy link
Contributor Author

@jokasimr jokasimr Nov 5, 2025

Choose a reason for hiding this comment

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

Good question.. After thinking a bit more about this, here is how I think it works:

The chopper system is designed so that, in pulse shaping mode,

  1. there is one point along the beamline where every neutron has to pass within a short interval of time. That can be seen as a "virtual source" position or a "virtual chopper" position. This is "the chopper position" in the code.
  2. a neutron emitted at the start of the pulse, with a particular wavelength, called lambda in the McStas files, reaches this virtual chopper exactly in the center of the time window when the chopper is open. That's why we can use the lambda parameter from the files to determine the opening time of the chopper.

I don't know if we are going to have access to this lambda parameter in practice, or if we are going to have to find the opening time of the virtual chopper by some other means.

Copy link
Member

Choose a reason for hiding this comment

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

I'm pretty sure we won't have lambda in the file. But isn't it a constant for a given chopper setting?

What about other modes? The code doesn't seem to only run for pulse shaping.

there is one point along the beamline where every neutron has to pass within a short interval of time. That can be seen as a "virtual source" position or a "virtual chopper" position. This is "the chopper position" in the code.

Can you change the name to reflect that and add a docstring?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What about other modes? The code doesn't seem to only run for pulse shaping.

For the multiplexing modes the neutron has to pass through one of a number of openings. But the concept of a virtual source with a position is still there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm pretty sure we won't have lambda in the file. But isn't it a constant for a given chopper setting?

I'm not sure, I'll ask Celine about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you change the name to reflect that and add a docstring?

👍

Co-authored-by: Jan-Lukas Wynen <jan-lukas.wynen@ess.eu>
@jokasimr
Copy link
Contributor Author

jokasimr commented Nov 5, 2025

True, but where will code for nexus files go? Also the io module?

Yes that's one option.

@jokasimr jokasimr requested a review from jl-wynen November 5, 2025 12:32
@jokasimr jokasimr moved this from Selected to In progress in Development Board Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

[BEER] Time-of-flight for pulse shaping mode

3 participants