// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "NXmxModules.h" #include #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 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(-vec[0]), static_cast(-vec[1]), static_cast(vec[2])); return delta.transpose() * (static_cast(len_px) * mcstas); } } // namespace DetectorModules ReadNXmxModules(HDF5Object &file, double pixel_size_m, const DetectorOrientation &orientation) { const RotMatrix delta = orientation.Matrix(); std::vector out; for (size_t n = 0;; n++) { const std::string base = "/entry/instrument/detector/module_" + std::to_string(n) + "/"; if (!file.Exists(base + "data_origin")) break; const auto origin = file.ReadVector(base + "data_origin"); const auto size = file.ReadVector(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; out.push_back(m); } if (out.size() < 2) return {}; return DetectorModules(out); }