-
Couldn't load subscription status.
- Fork 2
fix: convert unlimited TOA to expected range #209
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
Changes from all commits
f98b1cf
8994b88
efdb5ef
72a5df3
fa6731b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -196,6 +196,27 @@ def _to_edges(centers: sc.Variable) -> sc.Variable: | |
| ) | ||
|
|
||
|
|
||
| def _rebin_to_tofrange(da): | ||
| '''Rebins a monitor TOA histogram to the range of values | ||
| expected from a real instrument at ESS. | ||
| That is, to the range [0, 1/14] s. | ||
|
|
||
| Strategy: | ||
| 1. Create a grid that alingns with the pulse times. | ||
| 2. Rebin TOA on that grid. | ||
| 3. Fold pulses into new dimension and sum over pulses. | ||
| ''' | ||
| dim = da.dim | ||
| unit = da.coords[dim].unit | ||
| period = (1.0 / sc.scalar(14.0, unit='Hz')).to(unit=unit) | ||
| N = sc.sum(da.coords[dim] < period).value | ||
| K = (da.coords[dim].max() // period + 1).to(dtype='int') | ||
| grid = sc.linspace(dim, sc.scalar(0.0, unit=unit), K * period, K * (N - 1) + 1) | ||
| out = da.rebin({dim: grid}).fold(dim, sizes={'_': K, dim: N - 1}).sum('_') | ||
| out.coords[dim] = sc.linspace(dim, sc.scalar(0.0, unit=unit), period, N) | ||
| return out | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems very complicated for what is effectively a modulus operation. How about something like tof, counts, err = np.loadtxt(file_path, usecols=(0, 1, 2), unpack=True)
toa = tof % sc.reciprocal(sc.scalar(14.0, unit='Hz'))
toa = _to_edges(sc.array(dims=["toa"], values=toa, unit="us"))
data = sc.DataArray(
sc.array(dims=["toa"], values=counts, variances=err**2, unit="counts"),
coords={
"toa": toa,
},
)
data = data.hist(toa=some_range_based_on(tof))There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That doesn't work. You'll get multiple values added to some bins, and no values added to others. You can try it out and look at the difference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also didn't really follow. From the Strategy outlined, it sounded like a modulo operation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're not wrong, it's similar. But if you use the suggested approach of applying mod and then re-histogramming you get aliasing effects. If your final grid is sufficiently coarse then this is not very visible, but we don't want to restrict the monitor histogram to a very coarse grid directly after loading the data. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I think you're right; the important point here is that the input data is histogrammed, right? Are we sure that it will always be histogrammed or do we need to check for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes the aliasing problem only affects histogrammed data.
In this case we're loading data from a simulation. The current version of the code assumes the monitor is a histogram, I think it's fine to keep that assumption for now. If they change the simulation in the to have an event mode monitor we can deal with that later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you just add a safety net with if da.bins is not None:
raise NotImplementedError? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there migh be a misunderstanding here. What's your concern? The monitordata is loaded from a CSV file, it can never be binned the way the program works now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm concerned about someone in the future trying to use this to load a different file where the monitor data would be events?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really don't think we need to worry about that. A non public aux function that is used in a single place should not validate it's arguments. It's only extracted from the place where it is called to improve readability. I don't want this to be a long discussion. If you think this is important, please come talk to me in person instead to avoid misunderstandings. |
||
|
|
||
|
|
||
| def load_mcstas_monitor( | ||
| file_path: MonitorFilename[RunType], | ||
| position: CaveMonitorPosition, | ||
|
|
@@ -221,14 +242,14 @@ def load_mcstas_monitor( | |
| tof, counts, err = np.loadtxt(file_path, usecols=(0, 1, 2), unpack=True) | ||
|
|
||
| tof = _to_edges(sc.array(dims=["tof"], values=tof, unit="us")) | ||
| data = sc.DataArray( | ||
| sc.array(dims=["tof"], values=counts, variances=err**2, unit="counts"), | ||
| coords={"tof": tof}, | ||
| ) | ||
| data = _rebin_to_tofrange(data) | ||
| return NeXusComponent[CaveMonitor, RunType]( | ||
| sc.DataGroup( | ||
| data=sc.DataArray( | ||
| sc.array(dims=["tof"], values=counts, variances=err**2, unit="counts"), | ||
| coords={ | ||
| "tof": tof, | ||
| }, | ||
| ), | ||
| data=data, | ||
| position=position, | ||
| ) | ||
| ) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm getting confused here between TOA=time-of-arrival and ETO=event_time_offset.
ETO is the time which will always be between 0 and 1/14s.
TOA is the time the neutron arrived at the detector, so TOF = TOA - birth time of the neutron. TOA can have any value.
Which one are we refering to here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the CSV files we get TOA. But the time-of-flight lookup table expects ETO. So to be able to compute TOF using the time-of-flight lookup table we need to convert TOA to ETO.
For the detector data this is easy, it's just a mod operator on the TOA values in the file.
For (histogramed) monitor data, it's a bit more involved, but essentially the same in principle.