Files
Jungfraujoch/docs/DETECTOR_GEOMETRY.md
T
leonarski_fandClaude Opus 5 c1b6030c4f Set the azimuthal reference in the .poni file
An image integrated in pyFAI through our .poni came out with every chi 180 degrees from where it
belongs. pyFAI's in-plane axes are the negatives of ours, so Rot3 needs a half turn on top of the
sign flip. Being a rotation about the beam it leaves 2theta alone - which is why radial integration
was right all along and only the azimuth was wrong, and why a powder-ring check could never have
caught it.

The half turn is needed for the orientation-3 form written before rc.162 as well, so it is not an
artefact of declaring the orientation - the file has been 180 degrees out for as long as it has been
written.

Verified against pyFAI 2026.5.0 on a tilted detector with an off-centre beam, against the lab
positions of the NXmx chain: 2theta to 3.6e-15 deg and chi to 2.8e-14 deg. Then end to end, by
integrating an image in jfjoch's own layout through a .poni the code actually writes: chi lands
within 0.15 deg of physical truth on a 0.5 deg cake bin.

Withdraws two changelog claims. The .poni does negate Rot3, and declaring orientation did not fix
the azimuth: pyFAI's orientation is numerically inert here, so the file was relabelled and not
corrected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VfYvJT5Nb71suJCowRBn5z
2026-08-23 11:00:42 +02:00

54 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Detector geometry
At the moment Jungfraujoch supports solely flat detectors. The default option is to place modules in their actual location
vs. detector frame. It is not recommended to place detector modules stacked.
The simplest case is detector perpendicular to the beam. In this case it is enough to provide beam center, detector distance
and wavelength.
For more complex case, one can provide tilt of the detector rotation in PyFAI convention.
This convention uses Point Of Nominal Interaction (PONI) definition. Beam X and Y would correspond to the location on the detector,
where beam from the sample is perpendicular to the detector surface and not to the actual direct beam location. Then tilt of the detector
is defined with three rotation angles: `rot1` (rotating detector right), `rot2` (rotating detector downwards), `rot3` (rotating detector clockwise).
See [PyFAI documentation](https://pyfai.readthedocs.io/en/stable/) for more details.
## What a pixel coordinate means: (0, 0) is the centre of the first pixel
Pixel coordinates in Jungfraujoch and rugnux are **0-based and pixel-centred**: an integer coordinate
is the *centre* of that pixel, so pixel *i* covers [*i* 0.5, *i* + 0.5) and the sensor spans
0.5 … width 0.5. A beam centre of 948.0 × 546.0 sits in the middle of pixel [546][948], not on any
of its corners; 948.5 is the boundary between pixel 948 and 949.
This holds throughout the code: spot and reflection centroids are intensity-weighted sums of the
integer pixel indices, the resolution and azimuthal-bin maps evaluate pixel (col, row) at exactly
(col, row), and a fractional coordinate is turned back into a pixel index by rounding, not by
truncation. The same convention applies to every coordinate the system exposes — the beam centre
(`beam_x_pxl`/`beam_y_pxl` in the API and broker configuration, `--beam-x`/`--beam-y` in rugnux,
`beam_center_x`/`beam_center_y` in NXmx and in the CBOR stream), the spot and predicted-reflection
positions written to HDF5, and the PONI reported by `--mode calibration`.
Other programs place the origin differently, and the difference is worth half a pixel — enough to
matter when a geometry is copied between programs and then refined:
| Convention | Beam centre equivalent to our *x* = 948.0 |
|---|---|
| Jungfraujoch, rugnux | 948.0 |
| XDS (`ORGX`/`ORGY`) | 949.0 — also pixel-centred, but pixels are numbered from 1 |
| Measured from the edge of the sensor, in length units — pyFAI (`Poni2`, fast axis), DIALS/dxtbx | (948.0 + 0.5) × pixel size, because the centre of pixel *i* is at (*i* + 0.5) × pixel size from the edge |
| pyFAI `Poni1` (slow axis) | (height 1 *y* + 0.5) × pixel size — pyFAI measures the slow axis from the opposite edge, and the `.poni` declares `orientation: 2` to say so |
The `.poni` file written by `rugnux --mode calibration` is in pyFAI's frame and so already carries
that half pixel; the pixel values the same run reports are ours. `Rot3` in that file is our rot3
negated and turned by 180°: the half turn sets the azimuthal reference, because pyFAI's in-plane axes
are the negatives of ours. It leaves 2θ untouched, so it moves only the azimuth.
## Macromolecular crystallography convention for the vertical direction
One place of confusion is the convention to have point (0,0) of the detector in the top left corner of the detector,
with Y values increasing downwards. This is also consistent with computer image formats.
However, other techniques (as well as internal operation of PSI X-ray detectors) might follow convention, for point (0,0)
being in the bottom left corner and Y values increasing upwards. Such a convention is used, for example, by PyFAI.
In general, convention is controlled in Jungfraujoch with a setting in the JSON configuration file, which allows mirroring detector in Y.
Extra care has to be taken by the user to ensure that no errors are made.