Merge rc167 into the detection-score branch
Both lanes added a per-image scalar to the same eleven files, so every conflict was two
additions competing for one line. All were resolved by keeping both, with three that needed
more than that:
- ScanResultGenerator: rc167 changed the per-image float vectors to resize(n, NAN) so a frame
that never arrived does not read back as a real 0. v_protein_score and v_ice_score are exactly
that case - 0 is a real answer ("nothing detected here") - so they take the NAN default too.
- HDF5MetadataSource: rc167 established that NaN in a stored per-image array means "no value" and
the optional must come back absent. The two detection scores now follow it, which they did not
before the merge; without the guard a missing score would come back as a NaN that a threshold
would silently compare against.
- CBORTest: designated initialisers must follow member declaration order, so spindle_blind_fraction
precedes the two scores in the DataMessage aggregate.
Verified after the merge that every CBOR key that is encoded is also decoded (198 encoded keys,
one intentional exception: the "type" discriminator), that both lanes' fields survive in the
writer, the reader, the plots and the API, and that a stored file still round-trips.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
This commit is contained in:
@@ -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,11 +1375,20 @@ 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)
|
||||
// Same convention as spindle_blind_fraction above: a frame that produced no score is stored as
|
||||
// NaN, and 0 is a real answer ("nothing detected here"), so a missing one has to come back absent
|
||||
// rather than as a NaN that a threshold would then compare against.
|
||||
if (dataset->protein_score.size() > image_number
|
||||
&& std::isfinite(dataset->protein_score[image_number]))
|
||||
message.protein_score = dataset->protein_score[image_number];
|
||||
if (dataset->ice_score.size() > image_number)
|
||||
if (dataset->ice_score.size() > image_number && std::isfinite(dataset->ice_score[image_number]))
|
||||
message.ice_score = dataset->ice_score[image_number];
|
||||
if (dataset->efficiency.size() > image_number)
|
||||
message.image_collection_efficiency = dataset->efficiency[image_number];
|
||||
|
||||
Reference in New Issue
Block a user