writer: write refined detector geometry once at the end, incl. translation
The refined beam centre / detector rotations were written from the StartMessage at file open and then partially overwritten in Finalize - but the overwrite updated beam_center_x/y and rot1/2/3 and NOT the transformations/translation vector, which is what actually encodes the beam centre in the NXmx geometry chain read by XDS/DIALS/dxtbx. The reprocessed _process.h5 therefore had an internally inconsistent geometry (refined rotations combined with the stale, unrefined beam centre). The master file is never read before Finalize, so write the whole refinable geometry (beam_center_x/y + translation + rot1/2/3) once there, from the refined values when present and the StartMessage otherwise. This drops the open-time write plus in-place overwrite and, unlike the old path, keeps translation consistent with the refined beam centre. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-28
@@ -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<double> vector{start.beam_center_x * start.pixel_size_x,
|
||||
start.beam_center_y * start.pixel_size_y,
|
||||
std::vector<double> 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<int32_t> size = {static_cast<int32_t>(start.image_size_y),
|
||||
static_cast<int32_t>(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<double>(*end.refined_poni_rot1));
|
||||
if (end.refined_poni_rot2)
|
||||
overwrite("/entry/instrument/detector/transformations/rot2", static_cast<double>(*end.refined_poni_rot2));
|
||||
if (end.refined_poni_rot3)
|
||||
overwrite("/entry/instrument/detector/transformations/rot3", static_cast<double>(*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);
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user