Skip to content

Conversation

@jokasimr
Copy link
Contributor

@jokasimr jokasimr commented Nov 27, 2025

Fixes #141

The approach requires five pieces of information:

1. The chopper position.
2. The chopper opening time.
3. The chopper frequency (only relevant for pulse skipping).
4. The average time at the source. (This might be fine to hard code).
5. The maximum time at the source. (This might be fine to hard code).

We're going to use the Tof lookup table approach from essreduce.
For that we need the chopper information required to run a simulation in tof.

@jokasimr jokasimr requested review from nvaytet and paracini November 27, 2025 12:34
Comment on lines 99 to 109
def mcstas_wavelength_coordinate_transformation_graph() -> CoordTransformationGraph:
return {
"wavelength": lambda wavelength_from_mcstas: wavelength_from_mcstas,
"theta": theta,
"divergence_angle": divergence_angle,
"Q": reflectometry_q,
"L1": lambda source_position, sample_position: sc.norm(
sample_position - source_position
), # + extra correction for guides?
"L2": lambda position, sample_position: sc.norm(position - sample_position),
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is added so that we can still use the true wavelengths from McStas in cases when we don't want the wavelength uncertainty to contribute to the resolution.


da.coords['chopper_distance'] = sc.scalar(10.895, unit='m')
velocity_max = sc.scalar(3.956034e3 / 3.75, unit='m/s')
da.coords['chopper_open_time'] = da.coords['chopper_distance'] / velocity_max
Copy link
Contributor Author

@jokasimr jokasimr Nov 27, 2025

Choose a reason for hiding this comment

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

chopper_open_time and chopper_distance should be read from the file, but in the Estia McStas files the data is not stored in the DiskChopper component.

Copy link
Member

Choose a reason for hiding this comment

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

Is this info still required here or is it now encoded in the lookup table?

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 not required and will be removed 👍

# Use frame unwrapping from scippneutron
raise NotImplementedError()
"""
Converts event_time_offset to time_of_flight.
Copy link
Member

Choose a reason for hiding this comment

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

I think we should create a lookup table and use the GenericTofWorkflow instead?

We need the chopper parameters to make a tof model for estia, or alternatively if there is a good mcstas model, then use that?

Copy link
Contributor Author

@jokasimr jokasimr Nov 27, 2025

Choose a reason for hiding this comment

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

I'm thinking that it might be overkill to use the lookup table approach here.

Estia has a single chopper, and as I understand it that chopper only eliminates frame overlap, the wavelength resolution is the natural resolution of the source.

This is approximately what it looks like:
Figure 1(58)

Copy link
Member

@nvaytet nvaytet Nov 27, 2025

Choose a reason for hiding this comment

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

You can still get a better estimate from making a lookup table. You get a much more uniform agreement to wavelengths. With a single time/distance origin, the results are good at the center of the frames, and get worse on the edges of the frames.

I don't think it's overkill and it would mean it's using a workflow which is common to other instruments, which is also good? Especially if there is only one chopper, making a table will be very easy (not so many configurations) 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay that makes sense, I'll take a stab at it tomorrow 👍

Copy link
Member

@nvaytet nvaytet Nov 28, 2025

Choose a reason for hiding this comment

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

See here for an example of how to make a table (Dream instrument):
https://scipp.github.io/essdiffraction/user-guide/dream/dream-make-tof-lookup-table.html

@jokasimr jokasimr requested a review from nvaytet December 1, 2025 10:57
Copy link
Member

@nvaytet nvaytet left a comment

Choose a reason for hiding this comment

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

Looking good 👍

Can you also update the PR description to reflect the update?

"metadata": {},
"outputs": [],
"source": [
"table.plot()"
Copy link
Member

Choose a reason for hiding this comment

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

The table has a big gap in the middle, probably because of overlap between frames. Is that expected or has something gone wrong with the chopper parameters? (in the tof time-distance diagram you posted in the PR, there didn't seem to be frame overlap?)
Screenshot_20251201_173922

Copy link
Contributor Author

@jokasimr jokasimr Dec 2, 2025

Choose a reason for hiding this comment

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

The time distance diagram I posted didn't use exactly correct chopper parameters, it was just meant illustrate the chopper system qualitatively.

Yeah, there's a big overlap in the lookup table ,and it is a bit problematic because we loose quite a lot of intensity there.
There is an overlap in the McStas data too, but is smaller than the overlap in the lookup table:

Figure 1(62)

So something seems to be wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha, found the issue. The LookupTableRelativeErrorThreshold was set too low.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I don't misremember a resonable wavelength uncertainty for Estia is ~6%, is that right @paracini?
With the relative error threshold set to 6% the lookup table looks like below:

Figure 1(66)

Copy link
Member

@nvaytet nvaytet Dec 2, 2025

Choose a reason for hiding this comment

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

The time distance diagram I posted didn't use exactly correct chopper parameters, it was just meant illustrate the chopper system qualitatively.

I guess it was an ok representation because in the table above, around 35m (where the detector was placed in the other diagram), there is almost no overlap. You can guess from the time-distance diagram that going to higher distances would lead to more overlap.

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes resolution is between 8% for short wavelengths and 3.5% for the long wavelengths

"#from ess.estia.conversions import mcstas_wavelength_coordinate_transformation_graph\n",
"#wf.insert( mcstas_wavelength_coordinate_transformation_graph )\n",
"\n",
"wf[TimeOfFlightLookupTable] = estia_tof_lookup_table()\n",
Copy link
Member

Choose a reason for hiding this comment

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

You should set the TimeOfFlightLookupTableFilename on the workflow instead of the table itself, and let the workflow do the loading (there is some handling between old and new file formats that you would be missing here).

See scipp/essspectroscopy#80

" sc.scalar(float('nan'), unit=R.unit),\n",
" R.data\n",
" R.data,\n",
" )\n",
Copy link
Member

Choose a reason for hiding this comment

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

It's kind of weird. I was looking at how the results changed. To me, the new results look more like other reflectometry curves I've seen before (the old ones look very 'spiky').

But when I then look at the plot further down with the 'ground truth' in black, it seems the old results are much better? How was the ground truth result computed?
Screenshot_20251201_175309

Copy link
Contributor Author

@jokasimr jokasimr Dec 2, 2025

Choose a reason for hiding this comment

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

This is expected, the old results used the exact wavelengths from McStas, so the resolution has no contribution from wavelength uncertainty. When we do frame unwrapping then wavelength uncertainty contributes to the resolution (or lack thereof). As I understand it wavelength uncertainty is the dominant factor of the resolution of Estia.

The black curve shows the ground truth reflectivity of the sample. When the resolution is good the computed reflectivity overlaps it. But with frame unwrapping the sharp peaks are smeared out.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That's correct, resolution on Estia is dominated by wavelength, except at very small angles (around the critical edge) where the detector resolution dominates

"#from ess.estia.conversions import mcstas_wavelength_coordinate_transformation_graph\n",
"#wf.insert( mcstas_wavelength_coordinate_transformation_graph )\n",
"\n",
"wf[TimeOfFlightLookupTable] = estia_tof_lookup_table()\n",
Copy link
Member

Choose a reason for hiding this comment

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

Also set the filename

"wf[TimeOfFlightLookupTable] = estia_tof_lookup_table()\n",
"\n",
"# There is no proton current data in the McStas files, here we just add some fake proton current\n",
"# data to make the workflow run.\n",
Copy link
Member

Choose a reason for hiding this comment

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

The new diagnostics figure looks weird, especially the top left subplot
Screenshot_20251201_180206

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed now

"\n",
"\n",
"wf.insert( mcstas_wavelength_coordinate_transformation_graph )\n",
"wf[TimeOfFlightLookupTable] = estia_tof_lookup_table()\n",
Copy link
Member

Choose a reason for hiding this comment

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

Use TimeOfFlightLookupTableFilename



def estia_tof_lookup_table(pulse_stride=1):
if pulse_stride == 1:
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 use the loader in essreduce instead.

About the pulse_stride=1 logic, this will hopefully just be handled by the future mechanism in the generic workflow that would check that the chopper params in the file that is being loaded are compatible with the lookup table being used. So it should not be needed here.



def LoadNeXusWorkflow() -> sciline.Pipeline:
def LoadNeXusWorkflow(**kwargs) -> sciline.Pipeline:
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should pick a different name for the workflow (something like EstiaWorkflow?) as it is no longer just loading from nexus?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, but we do already define EstiaWorkflow in src/ess/estia/workflow.py.

Copy link
Member

Choose a reason for hiding this comment

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

ok

wf[WavelengthBins] = sc.geomspace('wavelength', 3.5, 12, 2001, unit='angstrom')
wf[QBins] = sc.geomspace('Q', 0.005, 0.1, 200, unit='1/angstrom')

wf[TimeOfFlightLookupTable] = estia_tof_lookup_table()
Copy link
Member

Choose a reason for hiding this comment

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

Use TimeOfFlightLookupTableFilename


da.coords['chopper_distance'] = sc.scalar(10.895, unit='m')
velocity_max = sc.scalar(3.956034e3 / 3.75, unit='m/s')
da.coords['chopper_open_time'] = da.coords['chopper_distance'] / velocity_max
Copy link
Member

Choose a reason for hiding this comment

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

Is this info still required here or is it now encoded in the lookup table?

@jokasimr
Copy link
Contributor Author

jokasimr commented Dec 2, 2025

Is this info still required here or is it now encoded in the lookup table?

It is encoded in the lookup table, so that code can be removed.

@jokasimr jokasimr requested a review from nvaytet December 2, 2025 09:45
Copy link
Member

@nvaytet nvaytet left a comment

Choose a reason for hiding this comment

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

I'm happy with the changes.

We need to make sure @paracini is also happy, as the results have changed significantly.

@jokasimr
Copy link
Contributor Author

jokasimr commented Dec 2, 2025

We need to make sure @paracini is also happy, as the results have changed significantly.

Note that you can still get back the old results by changing the coordinate transformations used to compute wavelength.
Specifically, if you add this provider to the workflow:

from ess.estia.conversions import mcstas_wavelength_coordinate_transformation_graph
wf.insert( mcstas_wavelength_coordinate_transformation_graph )

then wavelength will be "computed" from the exact wavelength coordinate that is loaded from McStas, like was done before.

@jokasimr jokasimr mentioned this pull request Dec 4, 2025
@paracini paracini merged commit 7277ed8 into main Dec 4, 2025
4 checks passed
@paracini paracini deleted the estia-tof branch December 4, 2025 13:28
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.

Implement frame unwrapping for Estia

4 participants