Record whether the image is mirrored in Y, and label the .poni orientation

Which way the detector's rows run was decided once, in the module assembly, and
never stated again: not on the wire, not in the file, nowhere a consumer could
read it. mirror_y was consumed inside the DetectorGeometryModular constructor and
discarded. It is now a declared property of the detector setup, carried into the
start message, written to HDF5 under detectorSpecific, and read back. Absence
means true, which is the MX convention and the only thing Jungfraujoch has ever
produced.

Deliberately a boolean and not a corner enum: the assembled image can only be
flipped in Y, so a four-corner value would encode states that cannot occur.

DECTRIS stream2 has no field for this - checked against the specification - so
the key is new rather than an extension of theirs, and a consumer that does not
know it skips it and behaves exactly as before.

The .poni file gains pyFAI's orientation. Without it pyFAI applies its own
default, 3 (bottom left), and believes increasing row means physically upwards.
The numbers still agreed - a mirror preserves 2theta, so radial integration was
never affected - but the azimuth came out with the opposite sense, which matters
for cake and sector integration.

Declaring orientation 2 is not a one-line addition: it re-anchors Poni1 to the
top edge and reverses rot2 and rot3, a row flip being improper. Measured against
pyFAI 2026.5.0 by searching all four orientations, both Poni1 anchorings and all
eight sign combinations: exactly two combinations reproduce the lab position
DiffractionGeometry computes to 1.4e-17 m - the unlabelled form written before,
and (orientation 2, Poni1 = height-1-beam_y, +rot1/+rot2/-rot3), which is now
written. Calibration_PoniFileAxisConvention pins it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-22 22:54:50 +02:00
co-authored by Claude Opus 5
parent 5e05515165
commit d2f57975e8
33 changed files with 5276 additions and 28 deletions
+5
View File
@@ -701,6 +701,11 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
DetectorSetup detector = DetDECTRIS(image_size_x, image_size_y, detector_name, {});
detector.PixelSize_um(master_file->GetFloat("/entry/instrument/detector/x_pixel_size") * 1e6);
// Whether the stored image is mirrored in Y. A file written before this was recorded is
// mirrored - that is the only thing Jungfraujoch has ever produced - so absence means true.
detector.MirrorY(master_file
->GetOptBool("/entry/instrument/detector/detectorSpecific/mirror_y")
.value_or(true));
// Sensor thickness/material drive the parallax/absorption model, so take them from the file
// rather than the DetectorSetup default (NXmx stores thickness in metres).
if (master_file->Exists("/entry/instrument/detector/sensor_thickness"))