Bragg-edge imaging with ODIN#

This notebook illustrates how to convert recorded events on the ODIN detector to a single wavelength spectrum, revealing a Bragg edge in the data. WFM mode was used in the chopper cascade.

[1]:
import scipp as sc
from ess.reduce import time_of_flight
from ess import odin
import ess.odin.data  # noqa: F401
from ess.imaging.types import *

Create and configure the workflow#

[2]:
wf = odin.OdinBraggEdgeWorkflow()

wf[Filename[SampleRun]] = odin.data.iron_simulation_sample_small()
wf[NeXusDetectorName] = "event_mode_detectors/timepix3"
wf[time_of_flight.TimeOfFlightLookupTableFilename] = odin.data.odin_tof_lookup_table()
Downloading file 'iron_simulation_sample_small.nxs' from 'https://public.esss.dk/groups/scipp/ess/odin/1/iron_simulation_sample_small.nxs' to '/home/runner/.cache/ess/odin'.
Downloading file 'ODIN-tof-lookup-table.h5' from 'https://public.esss.dk/groups/scipp/ess/odin/1/ODIN-tof-lookup-table.h5' to '/home/runner/.cache/ess/odin'.

First look at the data#

We load the raw detector data and perform a quick visualization of the event_time_offset spectrum.

[3]:
tmpx3 = wf.compute(RawDetector[SampleRun])
tmpx3
[3]:
Show/Hide data repr Show/Hide attributes
scipp.DataArray (74.89 MB out of 74.91 MB)
    • dim_0: 1024
    • dim_1: 1024
    • detector_number
      (dim_0, dim_1)
      int32
      1, 2, ..., 1048575, 1048576
      Values:
      array([[ 1, 2, 3, ..., 1022, 1023, 1024], [ 1025, 1026, 1027, ..., 2046, 2047, 2048], [ 2049, 2050, 2051, ..., 3070, 3071, 3072], ..., [1045505, 1045506, 1045507, ..., 1046526, 1046527, 1046528], [1046529, 1046530, 1046531, ..., 1047550, 1047551, 1047552], [1047553, 1047554, 1047555, ..., 1048574, 1048575, 1048576]], shape=(1024, 1024), dtype=int32)
    • position
      (dim_0, dim_1)
      vector3
      m
      [-0.007033 0.007033 0. ], [-0.007019 0.007033 0. ], ..., [ 0.007019 -0.007033 0. ], [ 0.007033 -0.007033 0. ]
      Values:
      array([[[-0.007033, 0.007033, 0. ], [-0.007019, 0.007033, 0. ], [-0.007006, 0.007033, 0. ], ..., [ 0.007006, 0.007033, 0. ], [ 0.007019, 0.007033, 0. ], [ 0.007033, 0.007033, 0. ]], [[-0.007033, 0.007019, 0. ], [-0.007019, 0.007019, 0. ], [-0.007006, 0.007019, 0. ], ..., [ 0.007006, 0.007019, 0. ], [ 0.007019, 0.007019, 0. ], [ 0.007033, 0.007019, 0. ]], [[-0.007033, 0.007006, 0. ], [-0.007019, 0.007006, 0. ], [-0.007006, 0.007006, 0. ], ..., [ 0.007006, 0.007006, 0. ], [ 0.007019, 0.007006, 0. ], [ 0.007033, 0.007006, 0. ]], ..., [[-0.007033, -0.007006, 0. ], [-0.007019, -0.007006, 0. ], [-0.007006, -0.007006, 0. ], ..., [ 0.007006, -0.007006, 0. ], [ 0.007019, -0.007006, 0. ], [ 0.007033, -0.007006, 0. ]], [[-0.007033, -0.007019, 0. ], [-0.007019, -0.007019, 0. ], [-0.007006, -0.007019, 0. ], ..., [ 0.007006, -0.007019, 0. ], [ 0.007019, -0.007019, 0. ], [ 0.007033, -0.007019, 0. ]], [[-0.007033, -0.007033, 0. ], [-0.007019, -0.007033, 0. ], [-0.007006, -0.007033, 0. ], ..., [ 0.007006, -0.007033, 0. ], [ 0.007019, -0.007033, 0. ], [ 0.007033, -0.007033, 0. ]]], shape=(1024, 1024, 3))
    • x_pixel_offset
      (dim_0, dim_1)
      float32
      m
      -0.007033, -0.007019, ..., 0.007019, 0.007033
      Values:
      array([[-0.007033, -0.007019, -0.007006, ..., 0.007006, 0.007019, 0.007033], [-0.007033, -0.007019, -0.007006, ..., 0.007006, 0.007019, 0.007033], [-0.007033, -0.007019, -0.007006, ..., 0.007006, 0.007019, 0.007033], ..., [-0.007033, -0.007019, -0.007006, ..., 0.007006, 0.007019, 0.007033], [-0.007033, -0.007019, -0.007006, ..., 0.007006, 0.007019, 0.007033], [-0.007033, -0.007019, -0.007006, ..., 0.007006, 0.007019, 0.007033]], shape=(1024, 1024), dtype=float32)
    • y_pixel_offset
      (dim_0, dim_1)
      float32
      m
      0.007033, 0.007033, ..., -0.007033, -0.007033
      Values:
      array([[ 0.007033, 0.007033, 0.007033, ..., 0.007033, 0.007033, 0.007033], [ 0.007019, 0.007019, 0.007019, ..., 0.007019, 0.007019, 0.007019], [ 0.007006, 0.007006, 0.007006, ..., 0.007006, 0.007006, 0.007006], ..., [-0.007006, -0.007006, -0.007006, ..., -0.007006, -0.007006, -0.007006], [-0.007019, -0.007019, -0.007019, ..., -0.007019, -0.007019, -0.007019], [-0.007033, -0.007033, -0.007033, ..., -0.007033, -0.007033, -0.007033]], shape=(1024, 1024), dtype=float32)
    • (dim_0, dim_1)
      float32
      counts
      binned data [len=2, len=0, ..., len=1, len=3]
      dim='event',
      content=DataArray(
                dims=(event: 1000000),
                data=float32[counts],
                coords={'event_time_offset':float64[ns], 'event_time_zero':datetime64[ns]})
[4]:
tmpx3.bins.concat().hist(event_time_offset=300).plot()
[4]:
../_images/odin_odin-data-reduction_6_0.svg

Compute neutron time-of-flight/wavelength#

We will now use the workflow to compute the neutron time-of-flight (equivalent to wavelength) using a lookup table built from the beamline chopper information.

[5]:
wf.visualize(TofDetector[SampleRun], graph_attr={"rankdir": "LR"})
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/backend/execute.py:76, in run_check(cmd, input_lines, encoding, quiet, **kwargs)
     75         kwargs['stdout'] = kwargs['stderr'] = subprocess.PIPE
---> 76     proc = _run_input_lines(cmd, input_lines, kwargs=kwargs)
     77 else:

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/backend/execute.py:96, in _run_input_lines(cmd, input_lines, kwargs)
     95 def _run_input_lines(cmd, input_lines, *, kwargs):
---> 96     popen = subprocess.Popen(cmd, stdin=subprocess.PIPE, **kwargs)
     98     stdin_write = popen.stdin.write

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/subprocess.py:1026, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask, pipesize, process_group)
   1023             self.stderr = io.TextIOWrapper(self.stderr,
   1024                     encoding=encoding, errors=errors)
-> 1026     self._execute_child(args, executable, preexec_fn, close_fds,
   1027                         pass_fds, cwd, env,
   1028                         startupinfo, creationflags, shell,
   1029                         p2cread, p2cwrite,
   1030                         c2pread, c2pwrite,
   1031                         errread, errwrite,
   1032                         restore_signals,
   1033                         gid, gids, uid, umask,
   1034                         start_new_session, process_group)
   1035 except:
   1036     # Cleanup if the child failed starting.

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/subprocess.py:1955, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session, process_group)
   1954 if err_filename is not None:
-> 1955     raise child_exception_type(errno_num, err_msg, err_filename)
   1956 else:

FileNotFoundError: [Errno 2] No such file or directory: PosixPath('dot')

The above exception was the direct cause of the following exception:

ExecutableNotFound                        Traceback (most recent call last)
File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/IPython/core/formatters.py:1036, in MimeBundleFormatter.__call__(self, obj, include, exclude)
   1033     method = get_real_method(obj, self.print_method)
   1035     if method is not None:
-> 1036         return method(include=include, exclude=exclude)
   1037     return None
   1038 else:

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/jupyter_integration.py:98, in JupyterIntegration._repr_mimebundle_(self, include, exclude, **_)
     96 include = set(include) if include is not None else {self._jupyter_mimetype}
     97 include -= set(exclude or [])
---> 98 return {mimetype: getattr(self, method_name)()
     99         for mimetype, method_name in MIME_TYPES.items()
    100         if mimetype in include}

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/jupyter_integration.py:98, in <dictcomp>(.0)
     96 include = set(include) if include is not None else {self._jupyter_mimetype}
     97 include -= set(exclude or [])
---> 98 return {mimetype: getattr(self, method_name)()
     99         for mimetype, method_name in MIME_TYPES.items()
    100         if mimetype in include}

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/jupyter_integration.py:112, in JupyterIntegration._repr_image_svg_xml(self)
    110 def _repr_image_svg_xml(self) -> str:
    111     """Return the rendered graph as SVG string."""
--> 112     return self.pipe(format='svg', encoding=SVG_ENCODING)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/piping.py:104, in Pipe.pipe(self, format, renderer, formatter, neato_no_op, quiet, engine, encoding)
     55 def pipe(self,
     56          format: typing.Optional[str] = None,
     57          renderer: typing.Optional[str] = None,
   (...)     61          engine: typing.Optional[str] = None,
     62          encoding: typing.Optional[str] = None) -> typing.Union[bytes, str]:
     63     """Return the source piped through the Graphviz layout command.
     64
     65     Args:
   (...)    102         '<?xml version='
    103     """
--> 104     return self._pipe_legacy(format,
    105                              renderer=renderer,
    106                              formatter=formatter,
    107                              neato_no_op=neato_no_op,
    108                              quiet=quiet,
    109                              engine=engine,
    110                              encoding=encoding)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/_tools.py:185, in deprecate_positional_args.<locals>.decorator.<locals>.wrapper(*args, **kwargs)
    177     wanted = ', '.join(f'{name}={value!r}'
    178                        for name, value in deprecated.items())
    179     warnings.warn(f'The signature of {func_name} will be reduced'
    180                   f' to {supported_number} positional arg{s_}{qualification}'
    181                   f' {list(supported)}: pass {wanted} as keyword arg{s_}',
    182                   stacklevel=stacklevel,
    183                   category=category)
--> 185 return func(*args, **kwargs)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/piping.py:121, in Pipe._pipe_legacy(self, format, renderer, formatter, neato_no_op, quiet, engine, encoding)
    112 @_tools.deprecate_positional_args(supported_number=1, ignore_arg='self')
    113 def _pipe_legacy(self,
    114                  format: typing.Optional[str] = None,
   (...)    119                  engine: typing.Optional[str] = None,
    120                  encoding: typing.Optional[str] = None) -> typing.Union[bytes, str]:
--> 121     return self._pipe_future(format,
    122                              renderer=renderer,
    123                              formatter=formatter,
    124                              neato_no_op=neato_no_op,
    125                              quiet=quiet,
    126                              engine=engine,
    127                              encoding=encoding)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/piping.py:149, in Pipe._pipe_future(self, format, renderer, formatter, neato_no_op, quiet, engine, encoding)
    146 if encoding is not None:
    147     if codecs.lookup(encoding) is codecs.lookup(self.encoding):
    148         # common case: both stdin and stdout need the same encoding
--> 149         return self._pipe_lines_string(*args, encoding=encoding, **kwargs)
    150     try:
    151         raw = self._pipe_lines(*args, input_encoding=self.encoding, **kwargs)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/backend/piping.py:212, in pipe_lines_string(engine, format, input_lines, encoding, renderer, formatter, neato_no_op, quiet)
    206 cmd = dot_command.command(engine, format,
    207                           renderer=renderer,
    208                           formatter=formatter,
    209                           neato_no_op=neato_no_op)
    210 kwargs = {'input_lines': input_lines, 'encoding': encoding}
--> 212 proc = execute.run_check(cmd, capture_output=True, quiet=quiet, **kwargs)
    213 return proc.stdout

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/graphviz/backend/execute.py:81, in run_check(cmd, input_lines, encoding, quiet, **kwargs)
     79 except OSError as e:
     80     if e.errno == errno.ENOENT:
---> 81         raise ExecutableNotFound(cmd) from e
     82     raise
     84 if not quiet and proc.stderr:

ExecutableNotFound: failed to execute PosixPath('dot'), make sure the Graphviz executables are on your systems' PATH
[5]:
<graphviz.graphs.Digraph at 0x7f59d04082d0>

Inspect the lookup table#

It is always a good idea to quickly plot the TOF lookup table, as a sanity check.

[6]:
table = wf.compute(time_of_flight.TimeOfFlightLookupTable)
table.plot(figsize=(9, 4))
[6]:
../_images/odin_odin-data-reduction_10_0.svg

Compute neutron wavelengths#

[7]:
sample_wavs = wf.compute(WavelengthDetector[SampleRun])

sample_wavs.bins.concat().hist(wavelength=300).plot()
---------------------------------------------------------------------------
UnsatisfiedRequirement                    Traceback (most recent call last)
Cell In[7], line 1
----> 1 sample_wavs = wf.compute(WavelengthDetector[SampleRun])
      3 sample_wavs.bins.concat().hist(wavelength=300).plot()

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/sciline/pipeline.py:191, in Pipeline.compute(self, tp, reporter, **kwargs)
    170 def compute(
    171     self,
    172     tp: type | Iterable[type] | "UnionType",  # noqa: UP037 (needed by Sphinx)
    173     reporter: Reporter | None = None,
    174     **kwargs: Any,
    175 ) -> Any:
    176     """
    177     Compute result for the given keys.
    178
   (...)    189         Keyword arguments passed to the ``.get()`` method.
    190     """
--> 191     return self.get(tp, **kwargs).compute(reporter=reporter)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/sciline/pipeline.py:281, in Pipeline.get(self, keys, scheduler, handler, max_depth)
    278         info = f'{e} Requested node not in graph. Did you mean one of: {nodes}?'
    279     # Not raising `from e` because that includes noisy traceback of internals,
    280     # which are not relevant to the user.
--> 281     raise type(e)(f'{info}\n\n') from None
    282 return TaskGraph(
    283     graph=graph,
    284     targets=targets if multi else keys,  # type: ignore[arg-type]
    285     scheduler=scheduler,
    286 )

UnsatisfiedRequirement: Missing input node 'LookupTableRelativeErrorThreshold'. Affects requested targets (via providers given in parentheses):
1. LookupTableRelativeErrorThreshold → (ess.reduce.time_of_flight.eto_to_tof.mask_large_uncertainty_in_lut_detector) → ErrorLimitedTofLookupTable[NXdetector] → (ess.reduce.time_of_flight.eto_to_tof.detector_time_of_flight_data) → TofDetector[SampleRun] → (ess.imaging.conversion.compute_detector_wavelength) → WavelengthDetector[SampleRun]


Process the open-beam run#

We now reuse the same workflow to process the open-beam run that will be used later for normalization.

[8]:
wf[Filename[OpenBeamRun]] = odin.data.iron_simulation_ob_small()
openbeam_wavs = wf.compute(WavelengthDetector[OpenBeamRun])

openbeam_wavs.bins.concat().hist(wavelength=300).plot()
Downloading file 'iron_simulation_ob_small.nxs' from 'https://public.esss.dk/groups/scipp/ess/odin/1/iron_simulation_ob_small.nxs' to '/home/runner/.cache/ess/odin'.
---------------------------------------------------------------------------
UnsatisfiedRequirement                    Traceback (most recent call last)
Cell In[8], line 2
      1 wf[Filename[OpenBeamRun]] = odin.data.iron_simulation_ob_small()
----> 2 openbeam_wavs = wf.compute(WavelengthDetector[OpenBeamRun])
      4 openbeam_wavs.bins.concat().hist(wavelength=300).plot()

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/sciline/pipeline.py:191, in Pipeline.compute(self, tp, reporter, **kwargs)
    170 def compute(
    171     self,
    172     tp: type | Iterable[type] | "UnionType",  # noqa: UP037 (needed by Sphinx)
    173     reporter: Reporter | None = None,
    174     **kwargs: Any,
    175 ) -> Any:
    176     """
    177     Compute result for the given keys.
    178
   (...)    189         Keyword arguments passed to the ``.get()`` method.
    190     """
--> 191     return self.get(tp, **kwargs).compute(reporter=reporter)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/sciline/pipeline.py:281, in Pipeline.get(self, keys, scheduler, handler, max_depth)
    278         info = f'{e} Requested node not in graph. Did you mean one of: {nodes}?'
    279     # Not raising `from e` because that includes noisy traceback of internals,
    280     # which are not relevant to the user.
--> 281     raise type(e)(f'{info}\n\n') from None
    282 return TaskGraph(
    283     graph=graph,
    284     targets=targets if multi else keys,  # type: ignore[arg-type]
    285     scheduler=scheduler,
    286 )

UnsatisfiedRequirement: Missing input node 'LookupTableRelativeErrorThreshold'. Affects requested targets (via providers given in parentheses):
1. LookupTableRelativeErrorThreshold → (ess.reduce.time_of_flight.eto_to_tof.mask_large_uncertainty_in_lut_detector) → ErrorLimitedTofLookupTable[NXdetector] → (ess.reduce.time_of_flight.eto_to_tof.detector_time_of_flight_data) → TofDetector[OpenBeamRun] → (ess.imaging.conversion.compute_detector_wavelength) → WavelengthDetector[OpenBeamRun]


Select region of interest by masking outer regions#

Making a 2D histogram of the data shows a dark square region in the centre of the detector panel; this is the region of interest, where the square sample has absorbed neutrons.

[9]:
sample_wavs.hist().plot(aspect='equal')
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 sample_wavs.hist().plot(aspect='equal')

NameError: name 'sample_wavs' is not defined

The brighter areas around the edges are regions where neutrons did not travel through the sample. We thus want to mask those out using masking rules based on the spatial coordinates of the data:

[10]:
wf[MaskingRules] = {
    'x_pixel_offset': lambda x: (x < sc.scalar(-5.8e-3, unit='m').to(unit=x.unit)) | (x > sc.scalar(5.8e-3, unit='m').to(unit=x.unit)),
    'y_pixel_offset': lambda y: (y < sc.scalar(-5.8e-3, unit='m').to(unit=y.unit)) | (y > sc.scalar(5.8e-3, unit='m').to(unit=y.unit))
}
[11]:
masked = wf.compute(CorrectedDetector[SampleRun])

masked.hist().plot(aspect='equal')
---------------------------------------------------------------------------
UnsatisfiedRequirement                    Traceback (most recent call last)
Cell In[11], line 1
----> 1 masked = wf.compute(CorrectedDetector[SampleRun])
      3 masked.hist().plot(aspect='equal')

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/sciline/pipeline.py:191, in Pipeline.compute(self, tp, reporter, **kwargs)
    170 def compute(
    171     self,
    172     tp: type | Iterable[type] | "UnionType",  # noqa: UP037 (needed by Sphinx)
    173     reporter: Reporter | None = None,
    174     **kwargs: Any,
    175 ) -> Any:
    176     """
    177     Compute result for the given keys.
    178
   (...)    189         Keyword arguments passed to the ``.get()`` method.
    190     """
--> 191     return self.get(tp, **kwargs).compute(reporter=reporter)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/sciline/pipeline.py:281, in Pipeline.get(self, keys, scheduler, handler, max_depth)
    278         info = f'{e} Requested node not in graph. Did you mean one of: {nodes}?'
    279     # Not raising `from e` because that includes noisy traceback of internals,
    280     # which are not relevant to the user.
--> 281     raise type(e)(f'{info}\n\n') from None
    282 return TaskGraph(
    283     graph=graph,
    284     targets=targets if multi else keys,  # type: ignore[arg-type]
    285     scheduler=scheduler,
    286 )

UnsatisfiedRequirement: Missing input node 'LookupTableRelativeErrorThreshold'. Affects requested targets (via providers given in parentheses):
1. LookupTableRelativeErrorThreshold → (ess.reduce.time_of_flight.eto_to_tof.mask_large_uncertainty_in_lut_detector) → ErrorLimitedTofLookupTable[NXdetector] → (ess.reduce.time_of_flight.eto_to_tof.detector_time_of_flight_data) → TofDetector[SampleRun] → (ess.imaging.conversion.compute_detector_wavelength) → WavelengthDetector[SampleRun] → (ess.odin.masking.apply_masks) → CorrectedDetector[SampleRun]


Normalize to open beam#

Finally, we use the masked sample and open-beam data to obtain a normalized signal, which reveals the Fe Bragg edges:

[12]:
wbins = sc.linspace('wavelength', 1.1, 9.4, 301, unit='angstrom')

normalized = (
        wf.compute(CorrectedDetector[SampleRun]).bins.concat().hist(wavelength=wbins) /
        wf.compute(CorrectedDetector[OpenBeamRun]).bins.concat().hist(wavelength=wbins)
)

normalized.plot()
---------------------------------------------------------------------------
UnsatisfiedRequirement                    Traceback (most recent call last)
Cell In[12], line 4
      1 wbins = sc.linspace('wavelength', 1.1, 9.4, 301, unit='angstrom')
      3 normalized = (
----> 4         wf.compute(CorrectedDetector[SampleRun]).bins.concat().hist(wavelength=wbins) /
      5         wf.compute(CorrectedDetector[OpenBeamRun]).bins.concat().hist(wavelength=wbins)
      6 )
      8 normalized.plot()

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/sciline/pipeline.py:191, in Pipeline.compute(self, tp, reporter, **kwargs)
    170 def compute(
    171     self,
    172     tp: type | Iterable[type] | "UnionType",  # noqa: UP037 (needed by Sphinx)
    173     reporter: Reporter | None = None,
    174     **kwargs: Any,
    175 ) -> Any:
    176     """
    177     Compute result for the given keys.
    178
   (...)    189         Keyword arguments passed to the ``.get()`` method.
    190     """
--> 191     return self.get(tp, **kwargs).compute(reporter=reporter)

File ~/work/ess/ess/.pixi/envs/docs-essimaging/lib/python3.11/site-packages/sciline/pipeline.py:281, in Pipeline.get(self, keys, scheduler, handler, max_depth)
    278         info = f'{e} Requested node not in graph. Did you mean one of: {nodes}?'
    279     # Not raising `from e` because that includes noisy traceback of internals,
    280     # which are not relevant to the user.
--> 281     raise type(e)(f'{info}\n\n') from None
    282 return TaskGraph(
    283     graph=graph,
    284     targets=targets if multi else keys,  # type: ignore[arg-type]
    285     scheduler=scheduler,
    286 )

UnsatisfiedRequirement: Missing input node 'LookupTableRelativeErrorThreshold'. Affects requested targets (via providers given in parentheses):
1. LookupTableRelativeErrorThreshold → (ess.reduce.time_of_flight.eto_to_tof.mask_large_uncertainty_in_lut_detector) → ErrorLimitedTofLookupTable[NXdetector] → (ess.reduce.time_of_flight.eto_to_tof.detector_time_of_flight_data) → TofDetector[SampleRun] → (ess.imaging.conversion.compute_detector_wavelength) → WavelengthDetector[SampleRun] → (ess.odin.masking.apply_masks) → CorrectedDetector[SampleRun]


Save the final result#

[13]:
from scippneutron.io import save_xye

to_disk = normalized.copy(deep=False)
to_disk.coords['wavelength'] = sc.midpoints(to_disk.coords['wavelength'])

save_xye('fe_bragg_edge.xye', to_disk)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[13], line 3
      1 from scippneutron.io import save_xye
----> 3 to_disk = normalized.copy(deep=False)
      4 to_disk.coords['wavelength'] = sc.midpoints(to_disk.coords['wavelength'])
      6 save_xye('fe_bragg_edge.xye', to_disk)

NameError: name 'normalized' is not defined