Files
leonarski_fandClaude Opus 5.5 532f795348
Build Packages / build:portable:windows-x86_64:cuda (push) Failing after 24m10s
Build Packages / build:portable:linux-x86_64:cuda (push) Failing after 12m10s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 26m58s
Build Packages / Create release (push) Successful in 7s
Build Packages / build:portable:macos-arm64:nocuda (push) Failing after 2m53s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 24m49s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 30m24s
Build Packages / Build documentation (push) Successful in 44s
Build Packages / build:portable:linux-x86_64:nocuda (push) Failing after 9m10s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 10m45s
Build Packages / build:portable:windows-x86_64:nocuda (push) Failing after 22m23s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 29m4s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 30m18s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 33m38s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 28m38s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 30m36s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 32m54s
Build Packages / Generate python client (push) Successful in 13s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 32m18s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 35m18s
Build Packages / Unit tests (push) Successful in 2h25m46s
Build Packages / Validation Battery (push) Successful in 1h10m47s
module_placement in custom_geometry's words: x0/y0/fast_axis/slow_axis after mirror_y
dataset_settings.module_placement (detector_module_placement) now names a module as custom_geometry's
detector_module does - x0, y0 (image position of the readout pixel 0) and fast_axis, slow_axis
(detector_module_direction) - but in the stored image, after mirror_y; x0_plane, y0_plane and
fast_step, slow_step give where pixel 0 lies on the plane and the plane step per readout pixel. Both
schema descriptions state the frame and the mirror (y0 = H - 1 - y0, Yp <-> Yn).

DetectorModule carries the readout axes in the image (pixel 0 follows from the box): from the
detector geometry after mirror_y (ModulesAsImaged), from how the XFEL reader turns a panel, and
Xp/Yp for a panel read along its own array. They travel in the CBOR detector_modules
(fast_axis/slow_axis) and in the NXmx module_NN groups (readout_fast_axis/readout_slow_axis, beside
the NXmx fields), so a stored file names its modules as the broker does. ModulesPlaced matches the
four keys exactly; a module not of the detector, placed twice or left out is an error (400 at /start).
The rugnux log names modules the same way.

New test ModulePlacement_TurnedMirroredModules (Yp/Xn module, mirror_y on) and PanelLayout_Placement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqXYdJKPUFLoeAchjzv3x4
2026-11-03 11:39:36 +01:00

90 lines
3.9 KiB
C++

// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "NXmxModules.h"
#include <cmath>
#include "../common/JFJochException.h"
namespace {
// A translation dataset as a vector in pixels of the internal frame (x = column, y = row, z = beam).
Coord ReadTranslation_px(HDF5Object &file, const std::string &path, double pixel_size_m, const RotMatrix &delta) {
HDF5DataSet ds(file, path);
std::vector<double> value;
ds.ReadVector(value);
const auto vec = ds.ReadAttrVec("vector");
if (value.empty() || vec.size() != 3)
throw JFJochException(JFJochExceptionCategory::HDF5, path + ": not a translation");
double scale = 1.0;
if (ds.AttrExists("units")) {
const std::string units = ds.ReadAttrStr("units");
if (units == "mm")
scale = 1e-3;
else if (units == "um" || units == "microns")
scale = 1e-6;
}
const double len_px = value[0] * scale / pixel_size_m;
// McStas -> internal is a half turn about z; the discrete image orientation is undone after it.
const Coord mcstas(static_cast<float>(-vec[0]), static_cast<float>(-vec[1]), static_cast<float>(vec[2]));
return delta.transpose() * (static_cast<float>(len_px) * mcstas);
}
} // namespace
std::string NXmxModuleGroup(HDF5Object &file, size_t n) {
// The index is written zero-padded to the width of the last one (module_00 ... module_15); the width is
// the one module 0 has.
for (size_t digits = 1; digits <= 3; digits++) {
const std::string first = "/entry/instrument/detector/module_" + std::string(digits - 1, '0') + "0/";
if (file.Exists(first + "data_origin")) {
std::string index = std::to_string(n);
if (index.size() < digits)
index.insert(0, digits - index.size(), '0');
const std::string base = "/entry/instrument/detector/module_" + index + "/";
return file.Exists(base + "data_origin") ? base : "";
}
}
return "";
}
DetectorModules ReadNXmxModules(HDF5Object &file, double pixel_size_m, const DetectorOrientation &orientation) {
const RotMatrix delta = orientation.Matrix();
std::vector<DetectorModule> out;
for (size_t n = 0;; n++) {
const std::string base = NXmxModuleGroup(file, n);
if (base.empty())
break;
const auto origin = file.ReadVector<int64_t>(base + "data_origin");
const auto size = file.ReadVector<int64_t>(base + "data_size");
if (origin.size() != 2 || size.size() != 2)
throw JFJochException(JFJochExceptionCategory::HDF5, base + ": only two-dimensional modules are read");
const Coord o = ReadTranslation_px(file, base + "module_offset", pixel_size_m, delta);
const Coord f = ReadTranslation_px(file, base + "fast_pixel_direction", pixel_size_m, delta);
const Coord s = ReadTranslation_px(file, base + "slow_pixel_direction", pixel_size_m, delta);
if (std::fabs(o.z) > 1e-3 || std::fabs(f.z) > 1e-6 || std::fabs(s.z) > 1e-6)
throw JFJochException(JFJochExceptionCategory::HDF5, base + ": a module out of the detector plane is not read");
DetectorModule m;
m.y0 = origin[0];
m.x0 = origin[1];
m.height = size[0];
m.width = size[1];
m.ox = o.x;
m.oy = o.y;
m.fx = f.x;
m.fy = f.y;
m.sx = s.x;
m.sy = s.y;
// The readout axes, where the writer says (writer/NXmxModules.cpp); else the module's own array.
if (file.Exists(base + "readout_fast_axis") && file.Exists(base + "readout_slow_axis")) {
m.fast = DirectionFromName(file.GetString(base + "readout_fast_axis"));
m.slow = DirectionFromName(file.GetString(base + "readout_slow_axis"));
}
out.push_back(m);
}
if (out.size() < 2)
return {};
return DetectorModules(out);
}