rugnux: don't reuse absent spots on plain DECTRIS datasets
Build Packages / build:windows:nocuda (push) Successful in 15m33s
Build Packages / build:windows:cuda (push) Successful in 17m48s
Build Packages / build:viewer-tgz:cpu (push) Successful in 6m23s
Build Packages / build:viewer-tgz:cuda (push) Successful in 6m53s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 10m12s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 10m35s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 10m51s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 8m47s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 9m30s
Build Packages / build:rpm (rocky8) (push) Successful in 10m18s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 11m5s
Build Packages / build:rpm (rocky9) (push) Successful in 11m5s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 10m40s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 11m14s
Build Packages / Generate python client (push) Successful in 15s
Build Packages / Build documentation (push) Successful in 50s
Build Packages / Create release (push) Skipped
Build Packages / XDS test (durin plugin) (push) Successful in 7m13s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 7m27s
Build Packages / XDS test (neggia plugin) (push) Successful in 6m46s
Build Packages / DIALS test (push) Successful in 10m59s
Build Packages / Unit tests (push) Successful in 1h50m3s
Build Packages / Unit tests (pull_request) Successful in 1h31m40s
Build Packages / build:windows:cuda (pull_request) Successful in 12m14s
Build Packages / build:viewer-tgz:cpu (pull_request) Successful in 6m16s
Build Packages / build:viewer-tgz:cuda (pull_request) Successful in 6m47s
Build Packages / build:rpm (rocky8_nocuda) (pull_request) Successful in 9m25s
Build Packages / build:rpm (rocky9_nocuda) (pull_request) Successful in 10m13s
Build Packages / build:rpm (ubuntu2204_nocuda) (pull_request) Successful in 8m8s
Build Packages / build:rpm (ubuntu2404_nocuda) (pull_request) Successful in 8m36s
Build Packages / build:rpm (rocky8_sls9) (pull_request) Successful in 10m10s
Build Packages / build:rpm (rocky9_sls9) (pull_request) Successful in 10m54s
Build Packages / build:rpm (rocky8) (pull_request) Successful in 9m56s
Build Packages / build:rpm (rocky9) (pull_request) Successful in 10m53s
Build Packages / build:rpm (ubuntu2204) (pull_request) Successful in 9m46s
Build Packages / build:rpm (ubuntu2404) (pull_request) Successful in 10m21s
Build Packages / DIALS test (pull_request) Successful in 12m15s
Build Packages / XDS test (durin plugin) (pull_request) Successful in 7m10s
Build Packages / XDS test (JFJoch plugin) (pull_request) Successful in 7m40s
Build Packages / XDS test (neggia plugin) (pull_request) Successful in 7m1s
Build Packages / Generate python client (pull_request) Successful in 25s
Build Packages / Build documentation (pull_request) Successful in 49s
Build Packages / Create release (pull_request) Skipped
Build Packages / build:windows:nocuda (pull_request) Successful in 16m1s

reuse_rotation_spots defaults on, so the two-pass first phase read stored spots
from /entry/MX. A standard DECTRIS dataset has no spot-finding results, so every
frame came back with zero spots and indexing failed ("Two-pass rotation indexing
failed") instead of falling back to finding spots.

Add HasStoredSpots() to the reader (HDF5MetadataSource::HasSpots): spots exist if
/entry/MX/nPeaks is present in the master (integrated _process.h5) or the per-image
data file (VDS/legacy) - the same master-first-then-source path ReadSpots uses.
Rugnux reuses stored spots only when they exist, otherwise it runs spot finding
(and logs it); --redo-rotation-spots already forced finding.

Verified: a true no-/entry/MX rotation dataset fails on the old code and indexes
(P21) on the new; all jungfraujoch files (spots in master or in data files) still
reuse unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-11 22:56:28 +02:00
co-authored by Claude Opus 4.8
parent f737424bdf
commit 0dfaf0a58a
5 changed files with 31 additions and 1 deletions
+13
View File
@@ -1180,3 +1180,16 @@ std::vector<SpotToSave> HDF5MetadataSource::ReadSpots(int64_t requested_image) c
return tmp_message.spots;
}
bool HDF5MetadataSource::HasSpots() const {
// Stored spots (jungfraujoch spot finding) live under /entry/MX; a plain DECTRIS file has none,
// so ReadSpots would silently return nothing and the caller must find them itself. ReadSpots
// reads /entry/MX/nPeaks master-first-then-source, so check both: the integrated _process.h5
// keeps it in the master, while a VDS/legacy dataset keeps the per-image arrays in the data file.
if (!master_file || number_of_images == 0)
return false;
if (master_file->Exists("/entry/MX/nPeaks"))
return true;
const auto loc = ResolveMeta(0);
return loc.file && loc.file->Exists("/entry/MX/nPeaks");
}
+2
View File
@@ -52,6 +52,8 @@ public:
const std::shared_ptr<const JFJochReaderDataset> &dataset) const;
std::vector<SpotToSave> ReadSpots(int64_t image) const;
// True if the file stores spot-finding results (/entry/MX); a plain DECTRIS file has none.
[[nodiscard]] bool HasSpots() const;
std::vector<IntegrationOutcome> ReadReflections(size_t start_image, std::optional<size_t> end_image) const;
CompressedImage ReadCalibration(std::vector<uint8_t> &tmp, const std::string &name) const;
+5
View File
@@ -123,6 +123,11 @@ std::vector<SpotToSave> JFJochHDF5Reader::ReadSpots(int64_t image) const {
return active_metadata_->ReadSpots(image);
}
bool JFJochHDF5Reader::HasStoredSpots() const {
std::unique_lock ul(hdf5_mutex);
return active_metadata_ && active_metadata_->HasSpots();
}
CompressedImage JFJochHDF5Reader::ReadCalibration(std::vector<uint8_t> &tmp, const std::string &name) const {
std::unique_lock ul(hdf5_mutex);
+3
View File
@@ -52,6 +52,9 @@ public:
std::vector<IntegrationOutcome> ReadReflections(size_t start_image = 0, std::optional<size_t> end_image = {}) const;
std::vector<SpotToSave> ReadSpots(int64_t image) const override;
// True if the loaded file stores spot-finding results; false for a plain DECTRIS dataset (no
// /entry/MX), so callers can find spots instead of reusing absent ones.
[[nodiscard]] bool HasStoredSpots() const;
CompressedImage ReadCalibration(std::vector<uint8_t> &tmp, const std::string &name) const;
+8 -1
View File
@@ -200,6 +200,13 @@ ProcessResult Rugnux::Run(RugnuxObserver *observer) {
return std::nullopt;
};
// Reuse stored spots only if the file actually has them: a plain DECTRIS dataset carries no
// spot-finding results, so "reusing" would leave every frame with zero spots and fail
// indexing - find them instead. (--redo-rotation-spots already forces finding.)
const bool reuse_spots = config_.reuse_rotation_spots && reader_.HasStoredSpots();
if (config_.reuse_rotation_spots && !reuse_spots)
logger.Info("Dataset has no stored spots - running spot finding for first-pass rotation indexing");
// Spots for a first-pass image ordinal, cached (a frame is reused across schemes and the
// validation set; re-finding is expensive on the --redo-rotation-spots path). Accessed only
// from single-threaded sections (the scheme feed and the validation loop), so no locking.
@@ -211,7 +218,7 @@ ProcessResult Rugnux::Run(RugnuxObserver *observer) {
const int image_idx = start_image + ordinal * config_.stride;
std::vector<SpotToSave> spots;
try {
if (config_.reuse_rotation_spots) {
if (reuse_spots) {
spots = reader_.ReadSpots(image_idx);
} else if (auto img = reader_.GetRawImage(image_idx)) {
DataMessage m{};