Merge branch 'rc167' into worktree-agent-ab3a3e173261a95d8

# Conflicts:
#	common/JFJochMessages.h
#	common/ScanResultGenerator.cpp
#	docs/CBOR.md
#	docs/HDF5.md
#	frame_serialize/CBORStream2Deserializer.cpp
#	frame_serialize/CBORStream2Serializer.cpp
#	frontend/src/components/DataProcessingPlot.tsx
#	tests/CBORTest.cpp
#	writer/HDF5DataFilePluginMX.cpp
#	writer/HDF5DataFilePluginMX.h
#	writer/HDF5NXmx.cpp
This commit is contained in:
2026-09-08 00:24:42 +02:00
283 changed files with 10759 additions and 1730 deletions
+42 -1
View File
@@ -325,6 +325,13 @@ bool ReadReflectionsFromGroup(HDF5Object &file,
// Written since the merge stopped back-deriving it; older _process.h5 do not carry it.
auto var_bkg = file.ReadOptVector<float>(image_group_name + "/background_variance");
auto lp = file.ReadOptVector<float>(image_group_name + "/lp");
// The sensor-efficiency part of lp, written since the correction existed. Absent in every
// older file, and there the whole of lp is taken for Lorentz-polarization - which is what
// those files mean, since nothing had corrected for the sensor when they were written.
auto qe = file.ReadOptVector<float>(image_group_name + "/qe");
// The flight path, in the same reciprocal convention. Absent in every older file, and there
// it is taken as 1: nothing had corrected for the flight path when those files were written.
auto flight = file.ReadOptVector<float>(image_group_name + "/flight");
auto partiality = file.ReadOptVector<float>(image_group_name + "/partiality");
auto phi = file.ReadOptVector<float>(image_group_name + "/delta_phi");
auto zeta = file.ReadOptVector<float>(image_group_name + "/zeta");
@@ -350,6 +357,14 @@ bool ReadReflectionsFromGroup(HDF5Object &file,
if (lp.size() > i && lp[i] != 0.0f)
lp_val = 1.0f / lp[i];
float qe_val = 1.0f;
if (qe.size() > i && qe[i] != 0.0f)
qe_val = 1.0f / qe[i];
float flight_val = 1.0f;
if (flight.size() > i && flight[i] != 0.0f)
flight_val = 1.0f / flight[i];
float partiality_val = -1.0f;
if (partiality.size() > i && partiality[i] >= 0.0f)
partiality_val = partiality[i];
@@ -396,7 +411,9 @@ bool ReadReflectionsFromGroup(HDF5Object &file,
.bkg = bkg.at(i),
.var_bkg = var_bkg_val,
.sigma = int_err.at(i),
.rlp = lp_val,
.prescaling_corr = lp_val,
.qe_corr = qe_val,
.flight_corr = flight_val,
.partiality = partiality_val,
.zeta = zeta_val,
.image_scale_corr = image_scale_corr_val
@@ -557,6 +574,7 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
dataset->indexing_result = master_file->ReadOptVector<float>("/entry/MX/imageIndexed");
dataset->bkg_estimate = master_file->ReadOptVector<float>("/entry/MX/bkgEstimate");
dataset->spindle_blind_fraction = master_file->ReadOptVector<float>("/entry/MX/spindleBlindFraction");
dataset->ice_ring_score = master_file->ReadOptVector<float>("/entry/MX/iceRingScore");
dataset->protein_score = master_file->ReadOptVector<float>("/entry/MX/proteinScore");
dataset->ice_score = master_file->ReadOptVector<float>("/entry/MX/iceScore");
@@ -704,6 +722,10 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
data_file, "/entry/MX/bkgEstimate",
number_of_images, fimages);
ReadVector(dataset->spindle_blind_fraction,
data_file, "/entry/MX/spindleBlindFraction",
number_of_images, fimages);
ReadVector(dataset->ice_ring_score,
data_file, "/entry/MX/iceRingScore",
number_of_images, fimages);
@@ -1013,11 +1035,25 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
.value_or(0))));
// Sensor thickness/material drive the parallax/absorption model, so take them from the file
// rather than the DetectorSetup default.
// Say so when they are absent rather than let the DetectorSetup default stand in silently.
// The default (320 um Si) is right for every JUNGFRAU and wrong for the 450 um Si and 750 um
// CdTe sensors this code also reads, and both the angle-dependent efficiency correction and
// the parallax variance term are computed from these two numbers - so a file that omits them
// gets a correction derived from an assumption, which the user should be told about. The
// correction is still applied: a 320 um Si assumption is closer to every real sensor than
// switching the physics off, and declining would silently disagree with the same data read
// from a file that does state its sensor.
if (master_file->Exists("/entry/instrument/detector/sensor_thickness"))
detector.SensorThickness_um(
ReadLength_m(*master_file, "/entry/instrument/detector/sensor_thickness") * 1e6);
else
Logger("HDF5Reader").Warning("No sensor_thickness in the file; assuming {:.0f} um for the "
"sensor absorption model", detector.GetSensorThickness_um());
if (master_file->Exists("/entry/instrument/detector/sensor_material"))
detector.SensorMaterial(master_file->GetString("/entry/instrument/detector/sensor_material"));
else
Logger("HDF5Reader").Warning("No sensor_material in the file; assuming {} for the sensor "
"absorption model", detector.GetSensorMaterial());
// Optional, because a file that states no saturation value anywhere is a real and common
// thing: an Eiger master links saturation_value into a companion _meta.h5, and a deposited
// dataset frequently does not include that file, leaving neither the NXmx name nor the
@@ -1339,6 +1375,11 @@ void HDF5MetadataSource::FillPerImage(DataMessage &message, int64_t requested_im
message.indexing_lattice_count = dataset->indexing_lattice_count[image_number];
if (dataset->bkg_estimate.size() > image_number)
message.bkg_estimate = dataset->bkg_estimate[image_number];
// NaN is how the file stores a frame with no value; the optional must come back absent, not
// carrying a NaN, because absence is the CANNOT-SAY trigger state and a value is not.
if (dataset->spindle_blind_fraction.size() > image_number
&& std::isfinite(dataset->spindle_blind_fraction[image_number]))
message.spindle_blind_fraction = dataset->spindle_blind_fraction[image_number];
if (dataset->ice_ring_score.size() > image_number)
message.ice_ring_score = dataset->ice_ring_score[image_number];
if (dataset->protein_score.size() > image_number)