ice score: per-image ice-ring indicator plumbed through all layers

A single per-image ice_ring_score - the strongest hexagonal-ice ring band/shoulder
intensity ratio from the azimuthal profile (1 = no ice) - computed in the CPU and
FPGA analysis paths and the offline azint worker, then plumbed through every layer:
DataMessage/EndMessage, CBOR (frame_serialize), HDF5 (/entry/MX/iceRingScore),
ScanResult, receiver plots (PlotType::IceRingScore), the OpenAPI spec (plot_type +
scan_result schema, with regenerated broker/gen and frontend client) and
OpenAPIConvert, the reader + Qt viewer, and the React frontend plot. Documented in
docs/CBOR.md, docs/HDF5.md and docs/CPU_DATA_ANALYSIS.md, with the general
"add a per-image quantity" recipe added to CLAUDE.md.

Verified in HDF5: lysoC (weak ice) mean 1.23, EP_cs_01-17 (heavy ice) mean 1.67 /
max 2.23. This is a monitoring quantity - it does not gate scaling (which already
excludes all ice rings) or merging (handled by the CC1/2 ring mask).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 16:37:12 +02:00
co-authored by Claude Fable 5
parent 6ffb16f142
commit f422988cd2
30 changed files with 165 additions and 8 deletions
+31
View File
@@ -190,3 +190,34 @@ React 19 + TypeScript + MUI + Vite (`frontend/`). Data layer is generated from t
(`@hey-api/openapi-ts` → fetch client + TanStack Query hooks + zod schemas). Scripts:
`npm start` (dev server), `npm run build` (tsc + vite), `npm run openapi` (regen client),
`npm run redocly4broker` (regen `broker/redoc-static.html`).
## Adding a per-image scalar quantity
A per-image scalar (e.g. `ice_ring_score`, `bkg_estimate`, `mosaicity`) flows analysis → message → CBOR
→ HDF5 → scan-result/plot → API → viewer/frontend. To add one, mirror an existing float scalar
(`bkg_estimate` is a clean template) at every layer:
1. **Compute** where the azint profile is finalized: `image_analysis/MXAnalysisWithoutFPGA.cpp` (CPU),
`receiver/JFJochReceiverFPGA.cpp` (FPGA), and the offline azint worker in `process/JFJochProcess.cpp`.
2. **Message** (`common/JFJochMessages.h`): `std::optional<float>` in `DataMessage`, `std::vector<float>`
in `EndMessage`.
3. **CBOR**: encode in `frame_serialize/CBORStream2Serializer.cpp` (DataMessage block *and* END block),
decode in `CBORStream2Deserializer.cpp` (both). Optional fields are back-compatible — no version bump.
4. **HDF5** (`writer/HDF5DataFilePluginMX.{h,cpp}`): an `AutoIncrVector<float>` with reserve / per-image
write / `SaveVector("/entry/MX/<name>")` — per-image arrays go in the data-file plugin, not NXmx master.
5. **Scan result**: `common/ScanResult.h` (`ScanResultElem`) + `common/ScanResultGenerator.cpp`
(copy in `Add`, resize+fill in `FillEndMessage`).
6. **Receiver plot**: `common/Plot.h` (`PlotType`) + `common/JFJochReceiverPlots.{h,cpp}` (`StatusVector`
+ Clear / AddElement / GetPlots / GetPlotRaw cases).
7. **API**: add to the `plot_type` enum and the `scan_result` images schema in `broker/jfjoch_api.yaml`,
regenerate the C++ model (`java -jar openapi-generator-cli.jar generate -i broker/jfjoch_api.yaml -o
broker/gen -g cpp-pistache-server`) and the frontend client (`cd frontend && npm run openapi`), then
wire `broker/OpenAPIConvert.cpp` (`ConvertPlotType` string→enum and the `Convert(ScanResult)` setter).
8. **Reader/viewer**: `reader/JFJochReaderDataset.h` + `reader/JFJochHttpReader.cpp` (`GetPlot_i`) and
`viewer/JFJochViewerDatasetInfo.cpp` (combo item + `ExtractMetric`).
9. **Frontend**: `frontend/src/components/DataProcessingPlots.tsx` (`MenuItem`) + `DataProcessingPlot.tsx`
(y-axis label).
10. **Docs**: `docs/CBOR.md`, `docs/HDF5.md`, `docs/CPU_DATA_ANALYSIS.md`.
Gotcha: an existing `build/` dir needs a `cmake .` reconfigure to pick up a newly-added `broker/gen`
source file (the source list is a configure-time glob).
+3
View File
@@ -880,6 +880,7 @@ PlotType ConvertPlotType(const std::optional<std::string>& input) {
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Plot type is compulsory paramater");
if (input == "bkg_estimate") return PlotType::BkgEstimate;
if (input == "ice_ring_score") return PlotType::IceRingScore;
if (input == "azint") return PlotType::AzInt;
if (input == "azint_1d") return PlotType::AzInt1D;
if (input == "spot_count") return PlotType::SpotCount;
@@ -1077,6 +1078,8 @@ org::openapitools::server::model::Scan_result Convert(const ScanResult& input) {
tmp.setSpots(i.spot_count.value());
if (i.spot_count_ice.has_value())
tmp.setSpotsIce(i.spot_count_ice.value());
if (i.ice_ring_score.has_value())
tmp.setIceRingScore(i.ice_ring_score.value());
if (i.spot_count_low_res.has_value())
tmp.setSpotsLowRes(i.spot_count_low_res.value());
if (i.spot_count_indexed.has_value())
+30 -1
View File
@@ -39,6 +39,8 @@ Scan_result_images_inner::Scan_result_images_inner()
m_Spots_indexedIsSet = false;
m_Spots_ice = 0L;
m_Spots_iceIsSet = false;
m_Ice_ring_score = 0.0f;
m_Ice_ring_scoreIsSet = false;
m_Index = 0L;
m_IndexIsSet = false;
m_Latt_count = 0L;
@@ -82,7 +84,7 @@ bool Scan_result_images_inner::validate(std::stringstream& msg, const std::strin
bool success = true;
const std::string _pathPrefix = pathPrefix.empty() ? "Scan_result_images_inner" : pathPrefix;
return success;
}
@@ -122,6 +124,9 @@ bool Scan_result_images_inner::operator==(const Scan_result_images_inner& rhs) c
((!spotsIceIsSet() && !rhs.spotsIceIsSet()) || (spotsIceIsSet() && rhs.spotsIceIsSet() && getSpotsIce() == rhs.getSpotsIce())) &&
((!iceRingScoreIsSet() && !rhs.iceRingScoreIsSet()) || (iceRingScoreIsSet() && rhs.iceRingScoreIsSet() && getIceRingScore() == rhs.getIceRingScore())) &&
((!indexIsSet() && !rhs.indexIsSet()) || (indexIsSet() && rhs.indexIsSet() && getIndex() == rhs.getIndex())) &&
@@ -183,6 +188,8 @@ void to_json(nlohmann::json& j, const Scan_result_images_inner& o)
j["spots_indexed"] = o.m_Spots_indexed;
if(o.spotsIceIsSet())
j["spots_ice"] = o.m_Spots_ice;
if(o.iceRingScoreIsSet())
j["ice_ring_score"] = o.m_Ice_ring_score;
if(o.indexIsSet())
j["index"] = o.m_Index;
if(o.lattCountIsSet())
@@ -252,6 +259,11 @@ void from_json(const nlohmann::json& j, Scan_result_images_inner& o)
j.at("spots_ice").get_to(o.m_Spots_ice);
o.m_Spots_iceIsSet = true;
}
if(j.find("ice_ring_score") != j.end())
{
j.at("ice_ring_score").get_to(o.m_Ice_ring_score);
o.m_Ice_ring_scoreIsSet = true;
}
if(j.find("index") != j.end())
{
j.at("index").get_to(o.m_Index);
@@ -462,6 +474,23 @@ void Scan_result_images_inner::unsetSpots_ice()
{
m_Spots_iceIsSet = false;
}
float Scan_result_images_inner::getIceRingScore() const
{
return m_Ice_ring_score;
}
void Scan_result_images_inner::setIceRingScore(float const value)
{
m_Ice_ring_score = value;
m_Ice_ring_scoreIsSet = true;
}
bool Scan_result_images_inner::iceRingScoreIsSet() const
{
return m_Ice_ring_scoreIsSet;
}
void Scan_result_images_inner::unsetIce_ring_score()
{
m_Ice_ring_scoreIsSet = false;
}
int64_t Scan_result_images_inner::getIndex() const
{
return m_Index;
@@ -125,6 +125,13 @@ public:
bool spotsIceIsSet() const;
void unsetSpots_ice();
/// <summary>
/// Strongest hexagonal-ice ring band/shoulder intensity ratio (1 &#x3D; no ice)
/// </summary>
float getIceRingScore() const;
void setIceRingScore(float const value);
bool iceRingScoreIsSet() const;
void unsetIce_ring_score();
/// <summary>
/// Indexing solution
/// </summary>
int64_t getIndex() const;
@@ -225,6 +232,8 @@ protected:
bool m_Spots_indexedIsSet;
int64_t m_Spots_ice;
bool m_Spots_iceIsSet;
float m_Ice_ring_score;
bool m_Ice_ring_scoreIsSet;
int64_t m_Index;
bool m_IndexIsSet;
int64_t m_Latt_count;
+5
View File
@@ -121,6 +121,7 @@ components:
- image_scale_cc
- image_scale_b
- compression_ratio
- ice_ring_score
roi:
in: query
name: roi
@@ -1578,6 +1579,10 @@ components:
type: integer
format: int64
description: Spot count within common ice ring resolutions
ice_ring_score:
type: number
format: float
description: Strongest hexagonal-ice ring band/shoulder intensity ratio (1 = no ice)
index:
type: integer
format: int64
+26
View File
@@ -3,6 +3,9 @@
#include "AzimuthalIntegrationProfile.h"
#include "JFJochException.h"
#include "Definitions.h"
#include <algorithm>
inline float sum_to_count(float sum, uint64_t count) {
if (count == 0)
@@ -171,6 +174,29 @@ float AzimuthalIntegrationProfile::GetBkgEstimate(const AzimuthalIntegrationSett
return GetMeanValueOfBins(min_bin, max_bin);
}
float AzimuthalIntegrationProfile::GetIceRingScore(const AzimuthalIntegrationSettings &settings,
float half_width_q) const {
// For each hexagonal-ice powder ring, the mean profile intensity in the ring band (+/- half_width in
// q = 2*pi/d) over a baseline from the two shoulders just outside it. Report the strongest ring's
// ratio (1 = no ice); rings whose band+shoulders fall off the measured q-range are skipped.
constexpr float two_pi = 6.283185307f;
const float low_q = settings.GetLowQ_recipA();
const float high_q = settings.GetHighQ_recipA();
float score = 1.0f;
for (const float ice_d : ICE_RING_RES_A) {
const float q = two_pi / ice_d;
if (q - 2 * half_width_q < low_q || q + 2 * half_width_q > high_q)
continue;
const float ring = GetMeanValueOfBins(settings.QToBin(q - half_width_q), settings.QToBin(q + half_width_q));
const float lo = GetMeanValueOfBins(settings.QToBin(q - 2 * half_width_q), settings.QToBin(q - half_width_q));
const float hi = GetMeanValueOfBins(settings.QToBin(q + half_width_q), settings.QToBin(q + 2 * half_width_q));
const float baseline = 0.5f * (lo + hi);
if (std::isfinite(baseline) && baseline > 0.0f && std::isfinite(ring))
score = std::max(score, ring / baseline);
}
return score;
}
AzimuthalIntegrationProfile &AzimuthalIntegrationProfile::operator+=(const AzimuthalIntegrationProfile &other) {
if ((other.bin_to_q != bin_to_q) || (sum.size() != other.sum.size())) {
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
+3
View File
@@ -44,6 +44,9 @@ public:
float GetMeanValueOfBins(uint16_t min_bin, uint16_t max_bin) const;
float GetBkgEstimate(const AzimuthalIntegrationSettings& settings) const;
// Single per-image ice indicator: the strongest hexagonal-ice ring's band/shoulder intensity ratio
// (1 = no ice, >1 = ice above the local background). Max over the rings in range; 1 if none.
float GetIceRingScore(const AzimuthalIntegrationSettings& settings, float half_width_q) const;
MultiLinePlot GetPlot(bool force_1d = false, PlotAzintUnit plot_unit = PlotAzintUnit::Q_recipA) const;
AzimuthalIntegrationProfile& operator+=(const AzimuthalIntegrationProfile& profile); // Not thread safe
};
+2
View File
@@ -119,6 +119,7 @@ struct DataMessage {
std::vector<uint64_t> az_int_profile_count;
std::optional<float> bkg_estimate;
std::optional<float> ice_ring_score; // strongest ice-ring band/shoulder ratio from the azint profile (1 = none)
std::optional<bool> indexing_result;
std::optional<CrystalLattice> indexing_lattice;
@@ -376,6 +377,7 @@ struct EndMessage {
std::vector<float> image_scale_cc;
std::vector<float> image_scale_b_factor;
std::vector<float> image_scale_mosaicity;
std::vector<float> ice_ring_score;
};
struct MetadataMessage {
+8
View File
@@ -61,6 +61,7 @@ void JFJochReceiverPlots::Setup(const DiffractionExperiment &experiment, const A
xfel_event_code.reserve(r);
}
bkg_estimate.Clear(r);
ice_ring_score.Clear(r);
spot_count.Clear(r);
spot_count_low_res.Clear(r);
spot_count_indexed.Clear(r);
@@ -127,6 +128,7 @@ void JFJochReceiverPlots::Setup(const DiffractionExperiment &experiment, const A
void JFJochReceiverPlots::Add(const DataMessage &msg, const AzimuthalIntegrationProfile &profile) {
bkg_estimate.AddElement(msg.number, msg.bkg_estimate);
ice_ring_score.AddElement(msg.number, msg.ice_ring_score);
resolution_estimate.AddElement(msg.number, msg.resolution_estimate);
spot_count.AddElement(msg.number, msg.spot_count);
spot_count_low_res.AddElement(msg.number, msg.spot_count_low_res);
@@ -275,6 +277,9 @@ MultiLinePlot JFJochReceiverPlots::GetPlots(const PlotRequest &request) {
case PlotType::BkgEstimate:
ret = bkg_estimate.GetMeanPlot(nbins, start, incr, request.fill_value);
break;
case PlotType::IceRingScore:
ret = ice_ring_score.GetMeanPlot(nbins, start, incr, request.fill_value);
break;
case PlotType::ResolutionEstimate:
ret = resolution_estimate.GetMeanPlot(nbins, start, incr, request.fill_value);
break;
@@ -534,6 +539,9 @@ void JFJochReceiverPlots::GetPlotRaw(std::vector<float> &v, PlotType type, const
case PlotType::BkgEstimate:
v = bkg_estimate.ExportArray();
break;
case PlotType::IceRingScore:
v = ice_ring_score.ExportArray();
break;
case PlotType::ResolutionEstimate:
v = resolution_estimate.ExportArray();
break;
+1
View File
@@ -42,6 +42,7 @@ class JFJochReceiverPlots {
AutoIncrVector<uint64_t> xfel_event_code;
StatusVector bkg_estimate;
StatusVector ice_ring_score;
StatusVector spot_count;
StatusVector spot_count_low_res;
StatusVector spot_count_indexed;
+1 -1
View File
@@ -15,7 +15,7 @@ enum class PlotType {
ROISum, ROIMean, ROIMaxCount, ROIPixels, ROIWeightedX, ROIWeightedY, PacketsReceived, MaxValue,
ResolutionEstimate, ProfileRadius, Mosaicity, BFactor, PixelSum, StrongPixels,
RefinementBeamX, RefinementBeamY, ImageProcessingTime, IntegratedReflections,
ImageScaleFactor, ImageScaleCC, ImageScaleBFactor, CompressionRatio, IndexingLatticeCount
ImageScaleFactor, ImageScaleCC, ImageScaleBFactor, CompressionRatio, IndexingLatticeCount, IceRingScore
};
enum class PlotAzintUnit {
+1
View File
@@ -42,6 +42,7 @@ struct ScanResultElem {
std::optional<int64_t> integrated_reflections;
std::optional<float> image_scale_factor;
std::optional<float> image_scale_cc;
std::optional<float> ice_ring_score;
};
struct ScanResult {
+3
View File
@@ -62,6 +62,7 @@ void ScanResultGenerator::Add(const DataMessage &message) {
v[image_number].integrated_reflections = message.integrated_reflections;
v[image_number].image_scale_factor = message.image_scale_factor;
v[image_number].image_scale_cc = message.image_scale_cc;
v[image_number].ice_ring_score = message.ice_ring_score;
if (message.lattice_type)
v[image_number].niggli_class = message.lattice_type->niggli_class;
}
@@ -103,6 +104,7 @@ void ScanResultGenerator::FillEndMessage(EndMessage &message) const {
message.error_pixel_count.resize(n);
message.image_scale_factor.resize(n);
message.image_scale_cc.resize(n);
message.ice_ring_score.resize(n);
message.integrated_reflections.resize(n);
message.niggli_class.resize(n);
message.pixel_sum.resize(n);
@@ -133,6 +135,7 @@ void ScanResultGenerator::FillEndMessage(EndMessage &message) const {
message.error_pixel_count[number] = static_cast<int32_t>(value_or_zero(e.err_pixels));
message.image_scale_factor[number] = e.image_scale_factor.value_or(NAN);
message.image_scale_cc[number] = e.image_scale_cc.value_or(NAN);
message.ice_ring_score[number] = e.ice_ring_score.value_or(NAN);
message.integrated_reflections[number] = static_cast<int32_t>(value_or_zero(e.integrated_reflections));
message.niggli_class[number] = static_cast<uint8_t>(value_or_zero(e.niggli_class));
message.pixel_sum[number] = value_or_zero(e.pixel_sum);
+2
View File
@@ -215,6 +215,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 band/shoulder intensity ratio from the azint profile (1 = no ice) | | |
| beam_corr_x | float | Beam center correction X applied during processing \[pixel\] | | X |
| beam_corr_y | float | Beam center correction Y applied during processing \[pixel\] | | X |
| image_scale_factor | float | Scaling result: Image scale factor (g) | | X |
@@ -305,6 +306,7 @@ See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/s
| spot_count_indexed | Array(int32) | Per-image number of spots fitting indexing solution | |
| image_indexed | Array(uint8) | Per-image indexing result; 0 = not indexed, nonzero = indexed | |
| v_bkg_estimate | Array(float) | Per-image background estimate | |
| ice_ring_score | Array(float) | Per-image strongest ice-ring band/shoulder intensity ratio (1 = no ice) | |
| profile_radius | Array(float) | Per-image profile radius \[Angstrom^-1\] | |
| mosaicity | Array(float) | Per-image mosaicity \[degree\] | |
| bFactor | Array(float) | Per-image estimated B-factor \[Angstrom^2\] | |
+2
View File
@@ -180,6 +180,8 @@ Special cases:
Spot finding can be restricted to a resolution range $[d_\mathrm{high}, d_\mathrm{low}]$ by masking pixels outside the range. Optionally, pixels in identified ice-ring regions can be tagged so that subsequent indexing/refinement may include or exclude them (see §4 and §6).
A single per-image **ice-ring score** is derived from the azimuthally-integrated radial profile: for each hexagonal-ice powder ring (positions $d$ from Moreau *et al.*, Acta Cryst D77, 2021), the mean profile intensity in the ring band ($\pm$ ice-ring half-width in $q$) is divided by a baseline interpolated from the two shoulders just outside it, and the strongest ring's ratio is reported (1 = no ice, $>1$ = ice above background). It is stored per image (`ice_ring_score`, HDF5 `/entry/MX/iceRingScore`) as a monitoring quantity. Note this is distinct from the merge-time ice masking, which is data-driven from the per-ring merged CC1/2 rather than this background ratio.
A further optional safeguard removes isolated high-resolution “spur” spots by detecting large gaps in $1/d$ (or $q$) space and discarding spots beyond the gap. This is intended for macromolecular diffraction where edge-of-detector backgrounds can be extremely low.
### 3.3 Connected-component labeling (CCL)
+1
View File
@@ -251,6 +251,7 @@ In legacy/VDS mode these live in the data files and are linked/virtual-stacked i
| `resolutionEstimate` | Å | diffraction resolution estimate |
| `integratedReflections` | | number of integrated reflections |
| `bkgEstimate` | photons | mean background in the 35 Å resolution band |
| `iceRingScore` | ratio | strongest hexagonal-ice ring band/shoulder intensity ratio (1 = no ice) |
| `beam_corr_x`, `beam_corr_y` | pixel | beam-center correction applied during processing |
| `imageScaleFactor` | | on-the-fly per-image scale factor *g* |
| `imageScaleCC` | | on-the-fly scaling correlation coefficient |
@@ -814,6 +814,8 @@ namespace {
message.packets_received = GetCBORUInt(value);
else if (key == "bkg_estimate")
message.bkg_estimate = GetCBORFloat(value);
else if (key == "ice_ring_score")
message.ice_ring_score = GetCBORFloat(value);
else if (key == "adu_histogram")
GetCBORUInt64Array(value, message.adu_histogram);
else if (key == "beam_corr_x")
@@ -863,6 +863,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_score);
CBOR_ENC(mapEncoder, "adu_histogram", message.adu_histogram);
CBOR_ENC(mapEncoder, "roi_integrals", message.roi);
CBOR_ENC(mapEncoder, "beam_corr_x", message.beam_corr_x);
+8 -3
View File
@@ -1010,6 +1010,10 @@ export type scan_result = {
* Spot count within common ice ring resolutions
*/
spots_ice?: number;
/**
* Strongest hexagonal-ice ring band/shoulder intensity ratio (1 = no ice)
*/
ice_ring_score?: number;
/**
* Indexing solution
*/
@@ -1696,7 +1700,8 @@ export const plot_type = {
IMAGE_SCALE_FACTOR: 'image_scale_factor',
IMAGE_SCALE_CC: 'image_scale_cc',
IMAGE_SCALE_B: 'image_scale_b',
COMPRESSION_RATIO: 'compression_ratio'
COMPRESSION_RATIO: 'compression_ratio',
ICE_RING_SCORE: 'ice_ring_score'
} as const;
/**
@@ -2948,7 +2953,7 @@ export type getPreviewPlotData = {
/**
* Type of requested plot
*/
type: 'bkg_estimate' | 'azint' | 'azint_1d' | 'spot_count' | 'spot_count_low_res' | 'spot_count_indexed' | 'spot_count_ice' | 'indexing_rate' | 'indexing_lattice_count' | 'indexing_unit_cell_length' | 'indexing_unit_cell_angle' | 'profile_radius' | 'mosaicity' | 'b_factor' | 'error_pixels' | 'saturated_pixels' | 'image_collection_efficiency' | 'receiver_delay' | 'receiver_free_send_buf' | 'strong_pixels' | 'roi_sum' | 'roi_mean' | 'roi_max_count' | 'roi_pixels' | 'roi_weighted_x' | 'roi_weighted_y' | 'packets_received' | 'max_pixel_value' | 'resolution_estimate' | 'pixel_sum' | 'processing_time' | 'beam_center_x' | 'beam_center_y' | 'integrated_reflections' | 'image_scale_factor' | 'image_scale_cc' | 'image_scale_b' | 'compression_ratio';
type: 'bkg_estimate' | 'azint' | 'azint_1d' | 'spot_count' | 'spot_count_low_res' | 'spot_count_indexed' | 'spot_count_ice' | 'indexing_rate' | 'indexing_lattice_count' | 'indexing_unit_cell_length' | 'indexing_unit_cell_angle' | 'profile_radius' | 'mosaicity' | 'b_factor' | 'error_pixels' | 'saturated_pixels' | 'image_collection_efficiency' | 'receiver_delay' | 'receiver_free_send_buf' | 'strong_pixels' | 'roi_sum' | 'roi_mean' | 'roi_max_count' | 'roi_pixels' | 'roi_weighted_x' | 'roi_weighted_y' | 'packets_received' | 'max_pixel_value' | 'resolution_estimate' | 'pixel_sum' | 'processing_time' | 'beam_center_x' | 'beam_center_y' | 'integrated_reflections' | 'image_scale_factor' | 'image_scale_cc' | 'image_scale_b' | 'compression_ratio' | 'ice_ring_score';
/**
* Fill value for elements that were missed during data collection
*
@@ -2995,7 +3000,7 @@ export type getPreviewPlotBinData = {
/**
* Type of requested plot
*/
type: 'bkg_estimate' | 'azint' | 'azint_1d' | 'spot_count' | 'spot_count_low_res' | 'spot_count_indexed' | 'spot_count_ice' | 'indexing_rate' | 'indexing_lattice_count' | 'indexing_unit_cell_length' | 'indexing_unit_cell_angle' | 'profile_radius' | 'mosaicity' | 'b_factor' | 'error_pixels' | 'saturated_pixels' | 'image_collection_efficiency' | 'receiver_delay' | 'receiver_free_send_buf' | 'strong_pixels' | 'roi_sum' | 'roi_mean' | 'roi_max_count' | 'roi_pixels' | 'roi_weighted_x' | 'roi_weighted_y' | 'packets_received' | 'max_pixel_value' | 'resolution_estimate' | 'pixel_sum' | 'processing_time' | 'beam_center_x' | 'beam_center_y' | 'integrated_reflections' | 'image_scale_factor' | 'image_scale_cc' | 'image_scale_b' | 'compression_ratio';
type: 'bkg_estimate' | 'azint' | 'azint_1d' | 'spot_count' | 'spot_count_low_res' | 'spot_count_indexed' | 'spot_count_ice' | 'indexing_rate' | 'indexing_lattice_count' | 'indexing_unit_cell_length' | 'indexing_unit_cell_angle' | 'profile_radius' | 'mosaicity' | 'b_factor' | 'error_pixels' | 'saturated_pixels' | 'image_collection_efficiency' | 'receiver_delay' | 'receiver_free_send_buf' | 'strong_pixels' | 'roi_sum' | 'roi_mean' | 'roi_max_count' | 'roi_pixels' | 'roi_weighted_x' | 'roi_weighted_y' | 'packets_received' | 'max_pixel_value' | 'resolution_estimate' | 'pixel_sum' | 'processing_time' | 'beam_center_x' | 'beam_center_y' | 'integrated_reflections' | 'image_scale_factor' | 'image_scale_cc' | 'image_scale_b' | 'compression_ratio' | 'ice_ring_score';
/**
* Name of ROI for which plot is requested
*/
+7 -3
View File
@@ -415,6 +415,7 @@ export const zScanResult = z.object({
spots_low_res: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
spots_indexed: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
spots_ice: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
ice_ring_score: z.number().optional(),
index: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
latt_count: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
pr: z.number().optional(),
@@ -814,7 +815,8 @@ export const zPlotType = z.enum([
'image_scale_factor',
'image_scale_cc',
'image_scale_b',
'compression_ratio'
'compression_ratio',
'ice_ring_score'
]);
/**
@@ -1111,7 +1113,8 @@ export const zGetPreviewPlotQuery = z.object({
'image_scale_factor',
'image_scale_cc',
'image_scale_b',
'compression_ratio'
'compression_ratio',
'ice_ring_score'
]),
fill: z.number().optional(),
experimental_coord: z.boolean().optional().default(false),
@@ -1166,7 +1169,8 @@ export const zGetPreviewPlotBinQuery = z.object({
'image_scale_factor',
'image_scale_cc',
'image_scale_b',
'compression_ratio'
'compression_ratio',
'ice_ring_score'
]),
roi: z.string().min(1).optional()
});
@@ -50,6 +50,8 @@ 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_SCORE:
return "Ratio";
case plot_type.AZINT:
case plot_type.AZINT_1D:
case plot_type.BKG_ESTIMATE:
@@ -50,6 +50,7 @@ function DataProcessingPlots({type: initialType, height}: MyProps) {
<MenuItem value={plot_type.SPOT_COUNT_LOW_RES}>Spot count low res.</MenuItem>
<MenuItem value={plot_type.SPOT_COUNT_INDEXED}>Spot count indexed</MenuItem>
<MenuItem value={plot_type.SPOT_COUNT_ICE}>Spot count ice ring</MenuItem>
<MenuItem value={plot_type.ICE_RING_SCORE}>Ice ring score</MenuItem>
<MenuItem value={plot_type.AZINT}>Azimuthal integration profile</MenuItem>
<MenuItem value={plot_type.AZINT_1D}>Azimuthal integration profile (1D)</MenuItem>
<MenuItem value={plot_type.BKG_ESTIMATE}>Background estimate</MenuItem>
+2
View File
@@ -113,6 +113,8 @@ void MXAnalysisWithoutFPGA::Analyze(DataMessage &output,
output.az_int_profile_std = profile.GetStd();
output.bkg_estimate = profile.GetBkgEstimate(integration.Settings());
output.ice_ring_score = profile.GetIceRingScore(integration.Settings(),
spot_finding_settings.ice_ring_width_Q_recipA);
}
void MXAnalysisWithoutFPGA::RebuildROI() {
+2
View File
@@ -409,6 +409,8 @@ ProcessResult JFJochProcess::Run(JFJochProcessObserver *observer) {
msg.az_int_profile_count = profile.GetPixelCount();
msg.az_int_profile_std = profile.GetStd();
msg.bkg_estimate = profile.GetBkgEstimate(mapping.Settings());
msg.ice_ring_score = profile.GetIceRingScore(mapping.Settings(),
config_.spot_finding.ice_ring_width_Q_recipA);
msg.run_number = experiment_.GetRunNumber();
msg.run_name = experiment_.GetRunName();
+1
View File
@@ -175,6 +175,7 @@ std::shared_ptr<JFJochReaderDataset> JFJochHttpReader::UpdateDataset_i() {
dataset->experiment.FluorescenceSpectrum(msg->start_message->fluorescence_spectrum);
dataset->bkg_estimate = GetPlot_i("bkg_estimate");
dataset->ice_ring_score = GetPlot_i("ice_ring_score");
dataset->spot_count = GetPlot_i("spot_count");
dataset->spot_count_ice_rings = GetPlot_i("spot_count_ice");
dataset->spot_count_low_res = GetPlot_i("spot_count_low_res");
+1
View File
@@ -36,6 +36,7 @@ struct JFJochReaderDataset {
std::vector<float> indexing_result;
std::vector<float> indexing_lattice_count;
std::vector<float> bkg_estimate;
std::vector<float> ice_ring_score;
std::vector<float> resolution_estimate;
std::vector<float> efficiency;
std::vector<float> profile_radius;
+2
View File
@@ -434,6 +434,8 @@ void JFJochReceiverFPGA::FrameTransformationThread(uint32_t threadid) {
if (force_cpu_azint)
message.az_int_profile_std = az_int_profile_image.GetStd();
message.bkg_estimate = az_int_profile_image.GetBkgEstimate(experiment.GetAzimuthalIntegrationSettings());
message.ice_ring_score = az_int_profile_image.GetIceRingScore(
experiment.GetAzimuthalIntegrationSettings(), spot_finding_settings.ice_ring_width_Q_recipA);
scan_result.Add(message);
+2
View File
@@ -132,6 +132,7 @@ void JFJochViewerDatasetInfo::UpdateLabels() {
if (this->dataset) {
combo_box->addItem("Background estimate", 0);
combo_box->addItem("Ice ring score", 14);
combo_box->addItem("Resolution estimate", 7);
combo_box->addItem("Spot count", 1);
combo_box->addItem("Spot count (indexed)", 2);
@@ -233,6 +234,7 @@ std::vector<float> JFJochViewerDatasetInfo::ExtractMetric(const JFJochReaderData
else if (val == 11) data = ds.image_scale_cc;
else if (val == 12) data = ds.integrated_reflections;
else if (val == 13) data = ds.indexing_lattice_count;
else if (val == 14) data = ds.ice_ring_score;
else if (val >= 100) {
const int roi_index = (val - 100) / 4;
if (val % 4 == 0) {
+5
View File
@@ -53,6 +53,7 @@ HDF5DataFilePluginMX::HDF5DataFilePluginMX(const StartMessage &msg)
void HDF5DataFilePluginMX::OpenFile(HDF5File &data_file, const DataMessage &msg, size_t images_per_file) {
bkg_estimate.reserve(images_per_file);
ice_ring_score.reserve(images_per_file);
if (max_spots == 0)
return;
@@ -98,6 +99,8 @@ void HDF5DataFilePluginMX::OpenFile(HDF5File &data_file, const DataMessage &msg,
void HDF5DataFilePluginMX::Write(const DataMessage &msg, uint64_t image_number) {
if (msg.bkg_estimate.has_value())
bkg_estimate[image_number] = msg.bkg_estimate.value();
if (msg.ice_ring_score.has_value())
ice_ring_score[image_number] = msg.ice_ring_score.value();
if (max_spots == 0)
return;
@@ -245,6 +248,8 @@ void HDF5DataFilePluginMX::WriteFinal(HDF5File &data_file) {
if (!bkg_estimate.empty())
data_file.SaveVector("/entry/MX/bkgEstimate", bkg_estimate.vec());
if (!ice_ring_score.empty())
data_file.SaveVector("/entry/MX/iceRingScore", ice_ring_score.vec());
if (!profile_radius.empty())
data_file.SaveVector("/entry/MX/profileRadius", profile_radius.vec())->Units("Angstrom^-1");
if (!mosaicity_deg.empty())
+1
View File
@@ -46,6 +46,7 @@ class HDF5DataFilePluginMX : public HDF5DataFilePlugin {
// bkg_estimate
AutoIncrVector<float> bkg_estimate{NAN};
AutoIncrVector<float> ice_ring_score{NAN};
// resolution_estimation
AutoIncrVector<float> resolution_estimate{NAN};