diff --git a/writer/HDF5NXmx.cpp b/writer/HDF5NXmx.cpp index 21f740cc..0497e96e 100644 --- a/writer/HDF5NXmx.cpp +++ b/writer/HDF5NXmx.cpp @@ -46,7 +46,6 @@ NXmx::NXmx(const StartMessage &start) Facility(start); Detector(start); - Metrology(start); Beam(start); Attenuator(start); UserData(start); @@ -332,8 +331,8 @@ void NXmx::Detector(const StartMessage &start) { group.NXClass("NXdetector"); SaveScalar(group, "depends_on", "/entry/instrument/detector/transformations/rot3"); - SaveScalar(group, "beam_center_x", start.beam_center_x)->Units("pixel"); - SaveScalar(group, "beam_center_y", start.beam_center_y)->Units("pixel"); + // beam_center_x/y and the transformations chain (translation + rot1/2/3) are the refinable geometry; + // they are written once at Finalize (see Metrology) from the values refined by the offline analysis. SaveScalar(group, "distance", start.detector_distance)->Units("m"); SaveScalar(group, "detector_distance", start.detector_distance)->Units("m"); @@ -574,12 +573,27 @@ void NXmx::Fluorescence(const StartMessage &start) { group.SaveVector("data", start.fluorescence_spectrum.GetData()); } -void NXmx::Metrology(const StartMessage &start) { +void NXmx::Metrology(const StartMessage &start, const EndMessage &end) { + // The beam centre and detector rotations may have been refined by the offline analysis (rugnux). + // The master file is streamed but never read before Finalize, so the whole geometry is written + // once here, at the end, from the refined values when present and the StartMessage values + // otherwise - simpler than an open-time write followed by an in-place overwrite. The broker + // leaves the refined fields empty, so the user-provided StartMessage geometry is used unchanged. + const float beam_center_x = end.refined_beam_center_x.value_or(start.beam_center_x); + const float beam_center_y = end.refined_beam_center_y.value_or(start.beam_center_y); + const double rot1 = end.refined_poni_rot1.value_or(start.poni_rot1.value_or(0.0f)); + const double rot2 = end.refined_poni_rot2.value_or(start.poni_rot2.value_or(0.0f)); + const double rot3 = end.refined_poni_rot3.value_or(start.poni_rot3.value_or(0.0f)); + + HDF5Group detector(*hdf5_file, "/entry/instrument/detector"); + SaveScalar(detector, "beam_center_x", beam_center_x)->Units("pixel"); + SaveScalar(detector, "beam_center_y", beam_center_y)->Units("pixel"); + HDF5Group transformations(*hdf5_file, "/entry/instrument/detector/transformations"); transformations.NXClass("NXtransformations"); - std::vector vector{start.beam_center_x * start.pixel_size_x, - start.beam_center_y * start.pixel_size_y, + std::vector vector{beam_center_x * start.pixel_size_x, + beam_center_y * start.pixel_size_y, start.detector_distance}; double vector_length = sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]); @@ -596,10 +610,6 @@ void NXmx::Metrology(const StartMessage &start) { std::vector size = {static_cast(start.image_size_y), static_cast(start.image_size_x)}; - const double rot1 = start.poni_rot1.value_or(0.0); - const double rot2 = start.poni_rot2.value_or(0.0); - const double rot3 = start.poni_rot3.value_or(0.0); - SaveScalar(transformations, "rot1", rot1)-> Transformation("rad", "/entry/instrument/detector/transformations/translation", @@ -888,24 +898,12 @@ void NXmx::Finalize(const EndMessage &end) { Detector(start_message, end); Sample(start_message, end); - // Overwrite the master-file geometry with values refined by the offline analysis (rugnux). The - // datasets were written from the StartMessage at file open (the file is streamed, so the geometry - // could not wait for refinement); re-write them in place now that the refined values are known. - // The broker leaves these empty, keeping the user-provided geometry. - const auto overwrite = [&](const std::string &path, auto val) { - HDF5DataSet ds(*hdf5_file, path); // open the existing dataset - ds.Write(HDF5DataType(val), &val); - }; - if (end.refined_beam_center_x) - overwrite("/entry/instrument/detector/beam_center_x", *end.refined_beam_center_x); - if (end.refined_beam_center_y) - overwrite("/entry/instrument/detector/beam_center_y", *end.refined_beam_center_y); - if (end.refined_poni_rot1) - overwrite("/entry/instrument/detector/transformations/rot1", static_cast(*end.refined_poni_rot1)); - if (end.refined_poni_rot2) - overwrite("/entry/instrument/detector/transformations/rot2", static_cast(*end.refined_poni_rot2)); - if (end.refined_poni_rot3) - overwrite("/entry/instrument/detector/transformations/rot3", static_cast(*end.refined_poni_rot3)); + // Write the refinable detector geometry (beam centre + transformations) now, from the values + // refined by the offline analysis when present and the StartMessage otherwise. The master file + // is never read before this point, so writing once here is simpler than an open-time write plus + // an in-place overwrite - and, unlike the old overwrite, it also updates the translation vector + // (which encodes the beam centre in the NXmx geometry chain). + Metrology(start_message, end); AzimuthalIntegration(start_message, end); ADUHistogram(end); diff --git a/writer/HDF5NXmx.h b/writer/HDF5NXmx.h index 427f1895..f79d09d6 100644 --- a/writer/HDF5NXmx.h +++ b/writer/HDF5NXmx.h @@ -44,7 +44,7 @@ class NXmx { void Facility(const StartMessage &start); void Beam(const StartMessage &start); - void Metrology(const StartMessage &start); + void Metrology(const StartMessage &start, const EndMessage &end); void Sample(const StartMessage &start, const EndMessage &end); void Attenuator(const StartMessage &start);