Skip to content

Commit 8309302

Browse files
committed
Initial docs
1 parent ac756f2 commit 8309302

File tree

4 files changed

+64
-1
lines changed

4 files changed

+64
-1
lines changed
32 KB
Loading
40.5 KB
Loading
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Psi scanning
2+
3+
On a reflectometer, the Psi parameter is a rotation of the sample, side-to-side as viewed from a neutron's perspective.
4+
The Psi parameter causes the reflected beam profile, which can be approximated as an elongated ellipse, to be rotated
5+
when viewed at the detector. Reflectometers want to align the beam such that the beam profile is parallel to detector
6+
pixels (or, in other words, a horizontal line as seen on the detector).
7+
8+
Psi does not get _redefined_ to zero when the beam is level, but reflectometers do need to know what value of Psi
9+
_causes_ the beam to be level. The value of Psi which causes the beam to be level will be different for each sample,
10+
because of differences in how each sample is mounted.
11+
12+
## Linear detectors
13+
14+
This section applies to instruments like OFFSPEC and POLREF, which have a 1-D bank of detectors. These pixels are much
15+
wider than they are tall; representative pixel sizes are `0.5 x 50mm` on OFFSPEC or `0.5 x 30mm` on POLREF.
16+
17+
A linear pixel configuration means that as Psi is varied, the beam profile (dark blue) hits a different number of
18+
detector pixels (light orange):
19+
20+
![Psi not equal to zero - broad peak](psi_scan_linear/psi_neq_0.png)
21+
22+
![Psi equal to zero - narrow peak](psi_scan_linear/psi_eq_0.png)
23+
24+
For a linear detector, the optimum value of Psi occurs when the beam profile is focused onto the smallest number of
25+
pixels (when the beam profile is parallel to the pixels).
26+
27+
A Psi scan on a linear detector is therefore implemented using the following steps:
28+
- Physically scan over Psi
29+
- At each scan point:
30+
- Count & acquire a spectrum-data map describing the counts in all detector pixels
31+
- Filter to a range of interesting pixels (for example using {py:obj}`~ibex_bluesky_core.utils.centred_pixel`)
32+
- Map those pixels to relative angle (to account for pixel spacing which may not be exactly even between all pixels)
33+
- Optionally divide counts in each pixel by a flood map, to account for different pixel efficiencies
34+
- Fit angle against counts using a Gaussian curve
35+
- Return the 'width' parameter of the Gaussian fit as the data from each scan point
36+
- Plotting the returned 'width' parameter at each scan point against the scanned variable, Psi, the optimum value of Psi
37+
occurs when the width is minimised.
38+
- This is performed using an 'outer' {py:obj}`~ibex_bluesky_core.fitting.Gaussian` fit, using standard
39+
[callbacks and fitting](../../callbacks/fitting/fitting) infrastructure. This is expected to give a negative
40+
{py:obj}`~ibex_bluesky_core.fitting.Gaussian` curve, where the `x0` parameter describes the optimum value of Psi.

src/ibex_bluesky_core/devices/reflectometry.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import asyncio
44
import logging
55

6+
import numpy as np
7+
import numpy.typing as npt
8+
import scipp as sc
69
from bluesky.protocols import NamedMovable
710
from ophyd_async.core import (
811
AsyncStatus,
@@ -15,11 +18,12 @@
1518
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw, epics_signal_w
1619

1720
from ibex_bluesky_core.devices import NoYesChoice
21+
from ibex_bluesky_core.devices.simpledae import Reducer
1822
from ibex_bluesky_core.utils import get_pv_prefix
1923

2024
logger = logging.getLogger(__name__)
2125

22-
__all__ = ["ReflParameter", "ReflParameterRedefine", "refl_parameter"]
26+
__all__ = ["AngleMappingReducer", "ReflParameter", "ReflParameterRedefine", "refl_parameter"]
2327

2428

2529
class ReflParameter(StandardReadable, NamedMovable[float]):
@@ -125,3 +129,22 @@ def refl_parameter(
125129
return ReflParameter(
126130
prefix=prefix, name=name, changing_timeout_s=changing_timeout_s, has_redefine=has_redefine
127131
)
132+
133+
134+
class AngleMappingReducer(Reducer, StandardReadable):
135+
"""Reflectometry angle-mapping reducer."""
136+
137+
def __init__(
138+
self,
139+
*,
140+
prefix: str,
141+
detectors: npt.NDArray[np.int32],
142+
angle_map: npt.NDArray[np.float64],
143+
flood: sc.Variable | None = None,
144+
) -> None:
145+
"""Angle-mapping :py:obj:`Reducer` for use by reflectometers.
146+
147+
This :py:obj:`Reducer` fits the counts on each pixel of a detector,
148+
against the relative angular positions of those pixels.
149+
"""
150+
super().__init__()

0 commit comments

Comments
 (0)