# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
# @author Jan-Lukas Wynen
"""Graphs for computing coordinates in time-of-flight neutron scattering.
See :mod:`scippneutron.conversion.tof` for definitions
of the quantities used here.
And the `user guide <../../user-guide/coordinate-transformations.rst>`_
for examples.
Functions in this module come in two categories and return graphs that
- can be used to compute a specific coordinate as identified by the function name
(e.g. ``elastic_energy``) from a given coordinate as given by the ``start`` argument.
These graphs may contain more than one node if necessary.
- can be used to compute multiple coordinates, (``elastic`` and ``kinematic``).
Their ``start`` argument works as in the other functions.
"""
from collections.abc import Callable
from .. import tof as _kernels
Graph = dict[str, Callable]
_GRAPH_DYNAMICS_BY_ORIGIN = {
'energy': {
'dspacing': _kernels.dspacing_from_energy,
'wavelength': _kernels.wavelength_from_energy,
},
'tof': {
'dspacing': _kernels.dspacing_from_tof,
'energy': _kernels.energy_from_tof,
'hkl_vec': _kernels.hkl_vec_from_Q_vec,
('h', 'k', 'l'): _kernels.hkl_elements_from_hkl_vec,
'ub_matrix': _kernels.ub_matrix_from_u_and_b,
'Q': _kernels.Q_from_wavelength,
'Q_vec': _kernels.Q_vec_from_Q_elements,
('Qx', 'Qy', 'Qz'): _kernels.Q_elements_from_wavelength,
'wavelength': _kernels.wavelength_from_tof,
'time_at_sample': _kernels.time_at_sample_from_tof,
},
'Q': {
'wavelength': _kernels.wavelength_from_Q,
},
'wavelength': {
'dspacing': _kernels.dspacing_from_wavelength,
'energy': _kernels.energy_from_wavelength,
'hkl_vec': _kernels.hkl_vec_from_Q_vec,
('h', 'k', 'l'): _kernels.hkl_elements_from_hkl_vec,
'ub_matrix': _kernels.ub_matrix_from_u_and_b,
'Q': _kernels.Q_from_wavelength,
'Q_vec': _kernels.Q_vec_from_Q_elements,
('Qx', 'Qy', 'Qz'): _kernels.Q_elements_from_wavelength,
},
}
def _strip_elastic(start: str, keep: list) -> Graph:
full_graph = elastic(start)
return {key: full_graph[key] for key in keep if key != start}
[docs]
def elastic(start: str) -> Graph:
"""Graph for elastic scattering transformations.
Parameters
----------
start:
Input coordinate. One of 'dspacing', 'energy', 'tof', 'Q',
or 'wavelength'.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return dict(_GRAPH_DYNAMICS_BY_ORIGIN[start])
[docs]
def kinematic(start: str) -> Graph:
"""Graph with pure kinematics.
The returned graph can be used to compute scattering-independent quantities.
Parameters
----------
start:
Input coordinate. Currently, only 'tof' is supported.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return _strip_elastic(start, keep=['wavelength', 'energy'])
[docs]
def elastic_dspacing(start: str) -> Graph:
"""
Graph for elastic scattering transformation to dspacing.
Parameters
----------
start:
Input coordinate. One of 'energy', 'tof', or 'wavelength'.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return _strip_elastic(start, keep=['dspacing'])
[docs]
def elastic_energy(start: str) -> Graph:
"""
Graph for elastic scattering transformation to energy.
Parameters
----------
start:
Input coordinate. One of 'tof' or 'wavelength'.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return _strip_elastic(start, keep=['energy'])
[docs]
def elastic_Q(start: str) -> Graph:
"""
Graph for elastic scattering transformation to Q.
Parameters
----------
start:
Input coordinate. One of 'tof' or 'wavelength'.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return _strip_elastic(start, keep=['Q', 'wavelength'])
[docs]
def elastic_Q_vec(start: str) -> Graph:
"""
Graph for elastic scattering transformation to Q vector.
Parameters
----------
start:
Input coordinate. One of 'tof' or 'wavelength'.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return _strip_elastic(start, keep=[('Qx', 'Qy', 'Qz'), 'Q_vec', 'wavelength'])
[docs]
def elastic_hkl(start: str) -> Graph:
"""
Graph for elastic scattering transformation to Q vector.
Parameters
----------
start:
Input coordinate. One of 'tof' or 'wavelength'.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return _strip_elastic(
start,
keep=[
('Qx', 'Qy', 'Qz'),
'Q_vec',
('h', 'k', 'l'),
'hkl_vec',
'ub_matrix',
'wavelength',
],
)
[docs]
def elastic_wavelength(start: str) -> Graph:
"""
Graph for elastic scattering transformation to wavelength.
Parameters
----------
start:
Input coordinate. One of 'energy', 'tof', or 'Q'.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return _strip_elastic(start, keep=['wavelength'])
[docs]
def direct_inelastic(start: str) -> Graph:
"""
Graph for direct-inelastic scattering transformations.
Parameters
----------
start:
Input coordinate. Currently, only 'tof' is supported.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return {'tof': {'energy_transfer': _kernels.energy_transfer_direct_from_tof}}[start]
[docs]
def indirect_inelastic(start: str) -> Graph:
"""
Graph for indirect-inelastic scattering transformations.
Parameters
----------
start:
Input coordinate. Currently, only 'tof' is supported.
Returns
-------
:
A dict defining a coordinate transformation graph.
"""
return {'tof': {'energy_transfer': _kernels.energy_transfer_indirect_from_tof}}[
start
]