|
1 | 1 | # SPDX-License-Identifier: BSD-3-Clause |
2 | 2 | # Copyright (c) 2023 Scipp contributors (https://github.com/scipp) |
3 | 3 | import scipp as sc |
4 | | -from scipp.constants import h, m_n, pi |
| 4 | +from scipp.constants import pi |
5 | 5 | from scippneutron._utils import elem_dtype, elem_unit |
| 6 | +from scippneutron.conversion.beamline import scattering_angle_in_yz_plane |
6 | 7 | from scippneutron.conversion.graph import beamline, tof |
7 | 8 |
|
8 | 9 | from .types import ( |
|
22 | 23 |
|
23 | 24 |
|
24 | 25 | def theta( |
25 | | - gravity: sc.Variable, |
26 | | - wavelength: sc.Variable, |
| 26 | + incident_beam: sc.Variable, |
27 | 27 | scattered_beam: sc.Variable, |
| 28 | + wavelength: sc.Variable, |
| 29 | + gravity: sc.Variable, |
28 | 30 | sample_rotation: sc.Variable, |
29 | 31 | ) -> sc.Variable: |
30 | | - """ |
31 | | - Compute the theta angle, including gravity correction, |
32 | | - This is similar to the theta calculation in SANS (see |
33 | | - https://docs.mantidproject.org/nightly/algorithms/Q1D-v2.html#q-unit-conversion), |
34 | | - but we ignore the horizontal `x` component. |
35 | | - See the schematic in Fig 5 of doi: 10.1016/j.nima.2016.03.007. |
| 32 | + r"""Compute the scattering angle w.r.t. the sample plane. |
| 33 | +
|
| 34 | + This function uses the definition given in |
| 35 | + :func:`scippneutron.conversion.beamline.scattering_angle_in_yz_plane` |
| 36 | + and includes the sample rotation :math:`\omega`: |
| 37 | +
|
| 38 | + .. math:: |
| 39 | +
|
| 40 | + \mathsf{tan}(\gamma) &= \frac{|y_d^{\prime}|}{z_d} \\ |
| 41 | + \theta = \gamma - \omega |
| 42 | +
|
| 43 | + with |
| 44 | +
|
| 45 | + .. math:: |
| 46 | +
|
| 47 | + y'_d = y_d + \frac{|g| m_n^2}{2 h^2} L_2^{\prime\, 2} \lambda^2 |
| 48 | +
|
| 49 | + Attention |
| 50 | + --------- |
| 51 | + The above equation for :math:`y'_d` approximates :math:`L_2 \approx L'_2`. |
| 52 | + See :func:`scippneutron.conversion.beamline.scattering_angle_in_yz_plane` |
| 53 | + for more information. |
36 | 54 |
|
37 | 55 | Parameters |
38 | 56 | ---------- |
39 | | - gravity: |
40 | | - The three-dimensional vector describing gravity. |
| 57 | + incident_beam: |
| 58 | + Beam from source to sample. Expects ``dtype=vector3``. |
| 59 | + scattered_beam: |
| 60 | + Beam from sample to detector. Expects ``dtype=vector3``. |
41 | 61 | wavelength: |
42 | | - Wavelength values for the events. |
43 | | - scatter_beam: |
44 | | - Vector for scattered beam. |
| 62 | + Wavelength of neutrons. |
| 63 | + gravity: |
| 64 | + Gravity vector. |
45 | 65 | sample_rotation: |
46 | | - Rotation of sample wrt to incoming beam. |
| 66 | + The sample rotation angle :math:`\omega`. |
47 | 67 |
|
48 | 68 | Returns |
49 | 69 | ------- |
50 | 70 | : |
51 | | - Theta values, accounting for gravity. |
| 71 | + The polar scattering angle :math:`\theta`. |
52 | 72 | """ |
53 | | - grav = sc.norm(gravity) |
54 | | - L2 = sc.norm(scattered_beam) |
55 | | - y = sc.dot(scattered_beam, gravity) / grav |
56 | | - y_correction = sc.to_unit(wavelength, elem_unit(L2), copy=True) |
57 | | - y_correction *= y_correction |
58 | | - drop = L2**2 |
59 | | - drop *= grav * (m_n**2 / (2 * h**2)) |
60 | | - # Optimization when handling either the dense or the event coord of binned data: |
61 | | - # - For the event coord, both operands have same dims, and we can multiply in place |
62 | | - # - For the dense coord, we need to broadcast using non in-place operation |
63 | | - if set(drop.dims).issubset(set(y_correction.dims)): |
64 | | - y_correction *= drop |
65 | | - else: |
66 | | - y_correction = y_correction * drop |
67 | | - y_correction += y |
68 | | - out = sc.abs(y_correction, out=y_correction) |
69 | | - out /= L2 |
70 | | - out = sc.asin(out, out=out) |
71 | | - out -= sc.to_unit(sample_rotation, "rad") |
72 | | - return out |
| 73 | + angle = scattering_angle_in_yz_plane( |
| 74 | + incident_beam=incident_beam, |
| 75 | + scattered_beam=scattered_beam, |
| 76 | + wavelength=wavelength, |
| 77 | + gravity=gravity, |
| 78 | + ) |
| 79 | + angle -= sample_rotation.to(unit=elem_unit(angle)) |
| 80 | + return angle |
73 | 81 |
|
74 | 82 |
|
75 | 83 | def reflectometry_q(wavelength: sc.Variable, theta: sc.Variable) -> sc.Variable: |
|
0 commit comments