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:
@@ -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).
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 = 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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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\] | |
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 3–5 Å 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);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user