diff --git a/broker/OpenAPIConvert.cpp b/broker/OpenAPIConvert.cpp index cf40188ca..978feaa42 100644 --- a/broker/OpenAPIConvert.cpp +++ b/broker/OpenAPIConvert.cpp @@ -899,7 +899,9 @@ PlotType ConvertPlotType(const std::optional& input) { "Plot type is compulsory paramater"); if (input == "bkg_estimate") return PlotType::BkgEstimate; if (input == "spindle_blind_fraction") return PlotType::SpindleBlindFraction; - if (input == "ice_ring_score") return PlotType::IceRingRatio; + // ice_ring_score is the retired spelling of ice_ring_ratio, kept so existing clients keep + // working; both name the same plot. + if (input == "ice_ring_ratio" || input == "ice_ring_score") return PlotType::IceRingRatio; if (input == "protein_score") return PlotType::ProteinScore; if (input == "ice_score") return PlotType::IceScore; if (input == "azint") return PlotType::AzInt; diff --git a/broker/jfjoch_api.yaml b/broker/jfjoch_api.yaml index bac2f7cd2..da6a44297 100644 --- a/broker/jfjoch_api.yaml +++ b/broker/jfjoch_api.yaml @@ -121,6 +121,7 @@ components: - image_scale_factor - image_scale_cc - compression_ratio + - ice_ring_ratio - ice_ring_score - protein_score - ice_score diff --git a/common/JFJochMessages.h b/common/JFJochMessages.h index 514d2863d..26ec7521d 100644 --- a/common/JFJochMessages.h +++ b/common/JFJochMessages.h @@ -129,7 +129,8 @@ struct DataMessage { std::optional bkg_estimate; // Strongest ice ring over the smooth radial background. A RATIO, so 1 means no ice and it is // unbounded above - the opposite convention to the two *_score members below, which is why the - // name says ratio. Still travels the wire and the file as "ice_ring_score"/iceRingScore. + // name says ratio. Wire key "ice_ring_ratio", dataset /entry/MX/iceRingRatio; both readers also + // accept the retired "ice_ring_score"/iceRingScore spellings. std::optional ice_ring_ratio; // How much of a single sweep's blind cone this orientation makes unrecoverable: 0 = one sweep // about the spindle reaches everything the point group can give, 1 = a short lattice row lies on @@ -477,7 +478,7 @@ struct EndMessage { // v_bkg_estimate above - it was shipped that way and the CBOR key is part of the stream format. std::vector ice_ring_ratio; // Run mean of the above, the single "how icy was this dataset" number (1 = no ice). The - // bkg_estimate scalar's counterpart; written to /entry/MX/iceRingScoreMean. + // bkg_estimate scalar's counterpart; written to /entry/MX/iceRingRatioMean. std::optional ice_ring_ratio_mean; // Per-image sweep-quality code: 0 = the image falls in no flagged range, otherwise 1 + the diff --git a/common/ScalingSettings.h b/common/ScalingSettings.h index ac25888c9..364701d4d 100644 --- a/common/ScalingSettings.h +++ b/common/ScalingSettings.h @@ -70,7 +70,7 @@ class ScalingSettings { // better on weak. --no-expected-variance-merge restores the old observed-sigma weighting. bool expected_variance_merge = true; - // Minimum measured ice strength (iceRingScore, 1 = no ice) before any ice-ring handling is applied + // Minimum measured ice strength (iceRingRatio, 1 = no ice) before any ice-ring handling is applied // at all. The eleven fixed hexagonal bands cover 16-26% of the unique reflections at typical // resolutions REGARDLESS of whether the crystal has ice, so flagging unconditionally taxes clean // data for nothing. 0 disables the gate (always handle ice, the previous behaviour). diff --git a/docs/CBOR.md b/docs/CBOR.md index 4837df0b4..f3cf80a99 100644 --- a/docs/CBOR.md +++ b/docs/CBOR.md @@ -226,7 +226,7 @@ See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/s | packets_expected | uint64 | Number of packets expected per image (in units of 2 kB) | | | | packets_received | uint64 | Number of packets received per image (in units of 2 kB) | | | | bkg_estimate | float | Mean value for pixels in resolution range from 3.0 to 5.0 A \[photons\] | | | -| ice_ring_score | float | Strongest hexagonal-ice ring intensity over the smooth radial background (1 = no ice) | | | +| ice_ring_ratio | float | Strongest hexagonal-ice ring intensity over the smooth radial background (1 = no ice). Was `ice_ring_score`; readers accept both, producers emit this one | | | | protein_score | float | Protein diffraction detection score, 0 to 1, saturating (0 = none, 1 = certain) | | | | ice_score | float | Crystalline ice detection score, 0 to 1, saturating (0 = none, 1 = certain) | | | | spindle_blind_fraction | float | Fraction (0-1) of a rotation sweep's blind cone this orientation makes unrecoverable, as a lone-2-fold worst-case bound; >= 0.5 should engage a recovery protocol, and ABSENT means the frame could not be assessed, which automation must treat the same way | | | @@ -324,11 +324,11 @@ See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/s | image_indexed | Array(uint8) | Per-image indexing result; 0 = not indexed, nonzero = indexed | | | v_bkg_estimate | Array(float) | Per-image background estimate | | | v_spindle_blind_fraction | Array(float) | Per-image spindle_blind_fraction; NaN where the frame had no value (which is "cannot say", not zero) | | -| ice_ring_score | Array(float) | Per-image strongest ice-ring intensity over the smooth radial background (1 = no ice) | | +| ice_ring_ratio | Array(float) | Per-image strongest ice-ring intensity over the smooth radial background (1 = no ice). Was `ice_ring_score`; readers accept both | | | v_protein_score | Array(float) | Per-image protein diffraction detection score, 0 to 1 | | | v_ice_score | Array(float) | Per-image crystalline ice detection score, 0 to 1 | | | spot_count_ice_control | Array(float) | Per-image spot count in the ice-free flanks beside the hexagonal rings, rescaled to the ring bands' q width | | -| ice_ring_score_mean | float | Mean ice-ring score for the whole run (1 = no ice) | | +| ice_ring_ratio_mean | float | Mean ice-ring ratio for the whole run (1 = no ice). Was `ice_ring_score_mean`; readers accept both | | | protein_score | float | Mean protein detection score for the whole run | | | ice_score | float | Mean ice detection score for the whole run | | | profile_radius | Array(float) | Per-image profile radius \[Angstrom^-1\] | | diff --git a/docs/CPU_DATA_ANALYSIS.md b/docs/CPU_DATA_ANALYSIS.md index 062beb5be..3c9bca136 100644 --- a/docs/CPU_DATA_ANALYSIS.md +++ b/docs/CPU_DATA_ANALYSIS.md @@ -48,10 +48,18 @@ the same physics on opposite conventions and the name is the only thing that say So a score is bounded and 1 is certainty; a ratio is unbounded and 1 is nothing. `ice_score` and `ice_ring_ratio` both describe ice and their extremes are opposite ends of the scale. -The C++ identifiers follow this. The **stored and wire names do not yet**: the ratio is still -written as `/entry/MX/iceRingScore`, sent as the CBOR key `ice_ring_score`, served as the -`ice_ring_score` plot type, and gated by `--ice-min-score`. Renaming those changes stored files, the -stream format, the REST API and a CLI flag, so it is a separate decision. +The identifiers, the stored names and the wire names all follow it. The ice ring ratio was called +`ice_ring_score` before; nothing that reads an old file or an old stream lost anything in the rename: + +| Surface | Now | Retired spelling | +|---|---|---| +| HDF5 | `/entry/MX/iceRingRatio`, `iceRingRatioMean` | `iceRingScore`, `iceRingScoreMean` — **still read**, so a file written before the rename still opens | +| CBOR | `ice_ring_ratio`, `ice_ring_ratio_mean` | `ice_ring_score`, `ice_ring_score_mean` — **still decoded**, so an older producer's stream still reads | +| REST `plot_type` | `ice_ring_ratio` | `ice_ring_score` — **still accepted**, and not scheduled for removal; both name the same plot | +| CLI | `--ice-min-score` | *unchanged*, deliberately. It is a threshold on the ratio, and it is the one surface a user types | + +Writers and producers emit the new spelling only. The old one is read but never written, which is +what makes the rename a one-way migration rather than a flag day. ## References diff --git a/docs/CPU_DATA_ANALYSIS_IMAGE.md b/docs/CPU_DATA_ANALYSIS_IMAGE.md index 9e08c3799..f54a207fa 100644 --- a/docs/CPU_DATA_ANALYSIS_IMAGE.md +++ b/docs/CPU_DATA_ANALYSIS_IMAGE.md @@ -427,7 +427,7 @@ Both constants carry a mechanism. A quantile from the middle of the distribution ### 3.7 Detection scores: is there protein here, is there ice here -`iceRingScore` above is a *magnitude* — a ratio, unbounded, answering "how strong is the worst ring". +`iceRingRatio` above is a *magnitude* — a ratio, unbounded, answering "how strong is the worst ring". Two further per-image scalars answer a different question, the one a grid scan actually asks: `proteinScore` and `iceScore`, both in $[0,1]$ and both **saturating**, so a superb crystal and a barely-diffracting one score the same. They are **detection** scores, not quality measures — the @@ -450,7 +450,7 @@ the pooled false-positive rate over the remaining ones below $5\times10^{-4}$. **Ice** reaches the frame two ways, and they need different evidence, so two channels are computed and the stronger one wins. The *radial* channel reads the plain azimuthal profile — not the -peak-excluded background the `iceRingScore` uses, because it needs the profile's own standard +peak-excluded background the `iceRingRatio` uses, because it needs the profile's own standard deviation, which that background does not carry. Each band is read as an excess over a running median (half-window 6 bins, which rejects a 3–5 bin powder ring but follows the ~40-bin vitreous halo, so the halo cannot score), in units of the bin mean's own error $\sigma/\sqrt{n}$ smoothed diff --git a/docs/HDF5.md b/docs/HDF5.md index 771ff4e65..1267fab6c 100644 --- a/docs/HDF5.md +++ b/docs/HDF5.md @@ -345,7 +345,7 @@ In legacy/VDS mode these live in the data files and are linked/virtual-stacked i | `resolutionEstimate` | Å | resolution the merged data are predicted to reach, from this image's spots alone | | `integratedReflections` | | number of integrated reflections | | `bkgEstimate` | photons | mean background in the 3–5 Å resolution band | -| `iceRingScore` | ratio | strongest hexagonal-ice ring intensity over the smooth radial background (1 = no ice) | +| `iceRingRatio` | ratio | strongest hexagonal-ice ring intensity over the smooth radial background (1 = no ice). Was `iceRingScore`; the reader accepts either, so a file written before the rename still opens | | `proteinScore` | | protein diffraction detection score, 0 to 1, saturating (0 = none, 1 = certain) | | `iceScore` | | crystalline ice detection score, 0 to 1, saturating (0 = none, 1 = certain) | | `spindleBlindFraction` | fraction (0-1) | how much of a rotation sweep's blind cone this orientation makes unrecoverable, as a lone-2-fold worst-case bound; NaN = the frame could not be assessed, which automation must treat like a value at or above the 0.5 trigger, never as 0 | @@ -372,7 +372,7 @@ variants. | `bkgEstimateMean` | photons | mean background over the run | | `spindleBlindFractionMean` | fraction (0-1) | mean `spindleBlindFraction` over the frames that had one | | `spindleLostUniqueFraction` | fraction (0-1) | unique reflections (to the run's resolution limit) the mounting made unmeasurable, exact under the measured point group and indexed orientation; offline (rugnux) only | -| `iceRingScoreMean` | ratio | mean `iceRingScore` over the run — the single "how icy was this dataset" number (1 = no ice) | +| `iceRingRatioMean` | ratio | mean `iceRingRatio` over the run — the single "how icy was this dataset" number (1 = no ice). Was `iceRingScoreMean` | | `proteinScoreMean` | | mean `proteinScore` over the run | | `iceScoreMean` | | mean `iceScore` over the run | | `scoreBeamCenterX`, `scoreBeamCenterY` | pixel | the beam centre `proteinScore` and `iceScore` were computed with. Both read *d* out of the geometry, and `/entry/instrument/detector/beam_center_x`/`_y` carries the **refined** centre where refinement ran, so without this pair a rescoring could not tell an algorithm disagreement from a geometry one | diff --git a/frame_serialize/CBORStream2Deserializer.cpp b/frame_serialize/CBORStream2Deserializer.cpp index b37f54281..ec4d6577a 100644 --- a/frame_serialize/CBORStream2Deserializer.cpp +++ b/frame_serialize/CBORStream2Deserializer.cpp @@ -822,7 +822,9 @@ namespace { message.packets_received = GetCBORUInt(value); else if (key == "bkg_estimate") message.bkg_estimate = GetCBORFloat(value); - else if (key == "ice_ring_score") + // Renamed from ice_ring_score; both spellings are accepted so a stream from an older + // producer still decodes. Optional keys are back-compatible either way, so no version bump. + else if (key == "ice_ring_ratio" || key == "ice_ring_score") message.ice_ring_ratio = GetCBORFloat(value); else if (key == "protein_score") message.protein_score = GetCBORFloat(value); @@ -1517,11 +1519,12 @@ namespace { message.ice_score = GetCBORFloat(value); else if (key == "v_spindle_blind_fraction") GetCBORFloatArray(value, message.v_spindle_blind_fraction); - else if (key == "ice_ring_score") + // Both spellings, as in the image block above. + else if (key == "ice_ring_ratio" || key == "ice_ring_score") GetCBORFloatArray(value, message.ice_ring_ratio); else if (key == "spot_count_ice_control") GetCBORFloatArray(value, message.spot_count_ice_control); - else if (key == "ice_ring_score_mean") + else if (key == "ice_ring_ratio_mean" || key == "ice_ring_score_mean") message.ice_ring_ratio_mean = GetCBORFloat(value); else if (key == "profile_radius") GetCBORFloatArray(value, message.profile_radius); diff --git a/frame_serialize/CBORStream2Serializer.cpp b/frame_serialize/CBORStream2Serializer.cpp index fb7c5f2ff..9a6a8f8e4 100644 --- a/frame_serialize/CBORStream2Serializer.cpp +++ b/frame_serialize/CBORStream2Serializer.cpp @@ -829,8 +829,8 @@ void CBORStream2Serializer::SerializeSequenceEnd(const EndMessage& message) { CBOR_ENC(mapEncoder, "protein_score", message.protein_score); CBOR_ENC(mapEncoder, "ice_score", message.ice_score); CBOR_ENC(mapEncoder, "v_spindle_blind_fraction", message.v_spindle_blind_fraction); - CBOR_ENC(mapEncoder, "ice_ring_score", message.ice_ring_ratio); - CBOR_ENC(mapEncoder, "ice_ring_score_mean", message.ice_ring_ratio_mean); + CBOR_ENC(mapEncoder, "ice_ring_ratio", message.ice_ring_ratio); + CBOR_ENC(mapEncoder, "ice_ring_ratio_mean", message.ice_ring_ratio_mean); CBOR_ENC(mapEncoder, "spot_count_ice_control", message.spot_count_ice_control); CBOR_ENC(mapEncoder, "profile_radius", message.profile_radius); CBOR_ENC(mapEncoder, "mosaicity", message.mosaicity); @@ -926,7 +926,7 @@ void CBORStream2Serializer::SerializeImageInternal(CborEncoder &mapEncoder, cons CBOR_ENC(mapEncoder, "packets_expected", message.packets_expected); CBOR_ENC(mapEncoder, "packets_received", message.packets_received); CBOR_ENC(mapEncoder, "bkg_estimate", message.bkg_estimate); - CBOR_ENC(mapEncoder, "ice_ring_score", message.ice_ring_ratio); + CBOR_ENC(mapEncoder, "ice_ring_ratio", message.ice_ring_ratio); CBOR_ENC(mapEncoder, "protein_score", message.protein_score); CBOR_ENC(mapEncoder, "ice_score", message.ice_score); CBOR_ENC(mapEncoder, "spindle_blind_fraction", message.spindle_blind_fraction); diff --git a/frontend/src/components/DataProcessingPlot.tsx b/frontend/src/components/DataProcessingPlot.tsx index aa3745f8f..ce7e2c7c0 100644 --- a/frontend/src/components/DataProcessingPlot.tsx +++ b/frontend/src/components/DataProcessingPlot.tsx @@ -52,6 +52,7 @@ function AxisTypeY(plot: plot_type) : string | ReactNode { case plot_type.SPOT_COUNT_INDEXED: case plot_type.SPOT_COUNT_ICE: return "Count"; + case plot_type.ICE_RING_RATIO: case plot_type.ICE_RING_SCORE: return "Ratio"; case plot_type.PROTEIN_SCORE: diff --git a/frontend/src/components/DataProcessingPlots.tsx b/frontend/src/components/DataProcessingPlots.tsx index f9a1ab602..a5dba85f4 100644 --- a/frontend/src/components/DataProcessingPlots.tsx +++ b/frontend/src/components/DataProcessingPlots.tsx @@ -50,7 +50,7 @@ function DataProcessingPlots({type: initialType, height}: MyProps) { Spot count low res. Spot count indexed Spot count ice ring - Ice ring score + Ice ring ratio Protein detection score Ice detection score Azimuthal integration profile diff --git a/reader/HDF5MetadataSource.cpp b/reader/HDF5MetadataSource.cpp index 30653eeed..71fee6092 100644 --- a/reader/HDF5MetadataSource.cpp +++ b/reader/HDF5MetadataSource.cpp @@ -575,7 +575,12 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen dataset->indexing_result = master_file->ReadOptVector("/entry/MX/imageIndexed"); dataset->bkg_estimate = master_file->ReadOptVector("/entry/MX/bkgEstimate"); dataset->spindle_blind_fraction = master_file->ReadOptVector("/entry/MX/spindleBlindFraction"); - dataset->ice_ring_ratio = master_file->ReadOptVector("/entry/MX/iceRingScore"); + // Renamed from iceRingScore. Accept either for backward compatibility - a file + // written before the rename must still read, and --mode scale reads this to + // reproduce the ice gate the writing run applied. + dataset->ice_ring_ratio = master_file->ReadOptVector("/entry/MX/iceRingRatio"); + if (dataset->ice_ring_ratio.empty()) + dataset->ice_ring_ratio = master_file->ReadOptVector("/entry/MX/iceRingScore"); dataset->protein_score = master_file->ReadOptVector("/entry/MX/proteinScore"); dataset->ice_score = master_file->ReadOptVector("/entry/MX/iceScore"); dataset->score_beam_center_x = master_file->GetOptFloat("/entry/MX/scoreBeamCenterX"); @@ -726,8 +731,11 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen data_file, "/entry/MX/spindleBlindFraction", number_of_images, fimages); + // Either spelling, as in the master above. ReadVector(dataset->ice_ring_ratio, - data_file, "/entry/MX/iceRingScore", + data_file, + data_file.Exists("/entry/MX/iceRingRatio") + ? "/entry/MX/iceRingRatio" : "/entry/MX/iceRingScore", number_of_images, fimages); ReadVector(dataset->protein_score, diff --git a/tests/CBORTest.cpp b/tests/CBORTest.cpp index 0a49a535a..df38b145b 100644 --- a/tests/CBORTest.cpp +++ b/tests/CBORTest.cpp @@ -1483,3 +1483,73 @@ TEST_CASE("CBORSerialize_End_Transformations", "[CBOR]") { CHECK(!chain[1].IsConstant()); CHECK(chain[2].IsConstant()); } + +namespace { + // "ice_ring_ratio" replaced its predecessor "ice_ring_score", and the two spellings are the same + // length - so an old-format stream is made from a new one by substituting the key text in place, + // leaving the CBOR text-string length prefix correct. That is what lets one test drive the + // deserializer with a stream this serializer can no longer produce. + void RetireIceRingKeySpelling(std::vector &buffer, size_t size) { + const std::string now = "ice_ring_ratio"; + const std::string before = "ice_ring_score"; + REQUIRE(now.size() == before.size()); + for (size_t i = 0; i + now.size() <= size; i++) + if (std::equal(now.begin(), now.end(), buffer.begin() + i)) + std::copy(before.begin(), before.end(), buffer.begin() + i); + } + + // Serialize an image and an end message carrying the ice ring ratio, optionally rewriting the key + // to the retired spelling, and return what the deserializer makes of each. + std::pair IceRingRatioRoundTrip(bool old_spelling) { + std::vector pixels(512, 7); + const CompressedImage image(pixels, 256, 2); + + std::vector buffer(1024 * 1024); + CBORStream2Serializer image_serializer(buffer.data(), buffer.size()); + DataMessage msg{.number = 11, .image = image}; + msg.ice_ring_ratio = 2.75f; + REQUIRE_NOTHROW(image_serializer.SerializeImage(msg)); + if (old_spelling) + RetireIceRingKeySpelling(buffer, image_serializer.GetBufferSize()); + auto image_out = CBORStream2Deserialize(buffer.data(), image_serializer.GetBufferSize()); + REQUIRE(image_out); + REQUIRE(image_out->data_message); + + std::vector end_buffer(1024 * 1024); + CBORStream2Serializer end_serializer(end_buffer.data(), end_buffer.size()); + EndMessage end{.max_image_number = 3}; + end.ice_ring_ratio = {1.0f, 2.5f, 4.25f}; + end.ice_ring_ratio_mean = 2.5f; + REQUIRE_NOTHROW(end_serializer.SerializeSequenceEnd(end)); + if (old_spelling) + RetireIceRingKeySpelling(end_buffer, end_serializer.GetBufferSize()); + auto end_out = CBORStream2Deserialize(end_buffer.data(), end_serializer.GetBufferSize()); + REQUIRE(end_out); + REQUIRE(end_out->end_message); + + return {*image_out->data_message, *end_out->end_message}; + } +} + +// The spelling this serializer emits. +TEST_CASE("CBORDeserialize_IceRingRatio_NewSpelling", "[CBOR]") { + const auto [image, end] = IceRingRatioRoundTrip(false); + REQUIRE(image.ice_ring_ratio.has_value()); + CHECK(image.ice_ring_ratio.value() == Catch::Approx(2.75f)); + CHECK(end.ice_ring_ratio == std::vector{1.0f, 2.5f, 4.25f}); + REQUIRE(end.ice_ring_ratio_mean.has_value()); + CHECK(end.ice_ring_ratio_mean.value() == Catch::Approx(2.5f)); +} + +// A stream from a producer that predates the rename. It must still decode, and into the same members: +// the writer turns these into the per-image datasets, and rugnux --mode scale reads them back to +// reproduce the ice gate the writing run applied. Silently losing them would not fail here - it would +// change which reflections leave the scale fit, months later. +TEST_CASE("CBORDeserialize_IceRingRatio_RetiredSpelling", "[CBOR]") { + const auto [image, end] = IceRingRatioRoundTrip(true); + REQUIRE(image.ice_ring_ratio.has_value()); + CHECK(image.ice_ring_ratio.value() == Catch::Approx(2.75f)); + CHECK(end.ice_ring_ratio == std::vector{1.0f, 2.5f, 4.25f}); + REQUIRE(end.ice_ring_ratio_mean.has_value()); + CHECK(end.ice_ring_ratio_mean.value() == Catch::Approx(2.5f)); +} diff --git a/writer/HDF5DataFilePluginMX.cpp b/writer/HDF5DataFilePluginMX.cpp index 71696afe8..e8e34dfec 100644 --- a/writer/HDF5DataFilePluginMX.cpp +++ b/writer/HDF5DataFilePluginMX.cpp @@ -260,7 +260,7 @@ void HDF5DataFilePluginMX::WriteFinal(HDF5File &data_file) { if (!bkg_estimate.empty()) data_file.SaveVector("/entry/MX/bkgEstimate", bkg_estimate.vec()); if (!ice_ring_ratio.empty()) - data_file.SaveVector("/entry/MX/iceRingScore", ice_ring_ratio.vec()); + data_file.SaveVector("/entry/MX/iceRingRatio", ice_ring_ratio.vec()); if (!protein_score.empty()) data_file.SaveVector("/entry/MX/proteinScore", protein_score.vec()); if (!ice_score.empty()) diff --git a/writer/HDF5NXmx.cpp b/writer/HDF5NXmx.cpp index 6fa941a91..85765e5a4 100644 --- a/writer/HDF5NXmx.cpp +++ b/writer/HDF5NXmx.cpp @@ -1187,7 +1187,7 @@ void NXmx::Finalize(const EndMessage &end) { end.spindle_lost_unique_fraction.value()); } if (end.ice_ring_ratio_mean) { - SaveScalar(*hdf5_file, "/entry/MX/iceRingScoreMean", end.ice_ring_ratio_mean.value()); + SaveScalar(*hdf5_file, "/entry/MX/iceRingRatioMean", end.ice_ring_ratio_mean.value()); } if (end.protein_score) { SaveScalar(*hdf5_file, "/entry/MX/proteinScoreMean", end.protein_score.value()); @@ -1282,7 +1282,7 @@ void NXmx::EndResultVectors(const EndMessage &end) { SaveVectorIfMissing(*hdf5_file, "/entry/MX/proteinScore", end.v_protein_score); SaveVectorIfMissing(*hdf5_file, "/entry/MX/iceScore", end.v_ice_score); SaveVectorIfMissing(*hdf5_file, "/entry/MX/spindleBlindFraction", end.v_spindle_blind_fraction); - SaveVectorIfMissing(*hdf5_file, "/entry/MX/iceRingScore", end.ice_ring_ratio); + SaveVectorIfMissing(*hdf5_file, "/entry/MX/iceRingRatio", end.ice_ring_ratio); SaveVectorIfMissing(*hdf5_file, "/entry/MX/profileRadius", end.profile_radius, "Angstrom^-1"); SaveVectorIfMissing(*hdf5_file, "/entry/MX/mosaicity", end.mosaicity, "deg"); SaveVectorIfMissing(*hdf5_file, "/entry/MX/bFactor", end.bFactor, "Angstrom^2");