writer: clamp the processing VDS to the images actually written

The processing VDS mapping is built from the number of images a run set
out to process, while total_images comes from the end message and is the
number it actually finished. Those differ whenever a run is cancelled or
skips an unreadable frame, and the mismatch was a hard throw - which
NXmx::Finalize catches by deleting the temporary master, so rugnux lost
the entire _process.h5 and every completed image with it. Ctrl-C after
5000 of 100000 images produced no output file at all.

Map what was written and drop the remainder instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 14:44:42 +02:00
co-authored by Claude Opus 5
parent d2d1d78545
commit 1aeb7cfbe3
+13 -6
View File
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <algorithm>
#include <cmath>
#include "HDF5NXmx.h"
@@ -210,9 +211,15 @@ void NXmx::LinkToData_ProcessingVDS(const StartMessage &start, const EndMessage
if (mapping.image_count == 0)
continue;
if (mapping.virtual_first_image + mapping.image_count > total_images)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Processing VDS mapping exceeds output image count");
// The mapping is built from the number of images the run intended to process, while
// total_images is the number it actually finished. A cancelled run, or one that skipped an
// unreadable frame, legitimately ends up with fewer - so map what was written and drop the
// rest. Rejecting the mismatch here would take the whole output file with it.
if (mapping.virtual_first_image >= total_images)
continue;
const hsize_t image_count = std::min(static_cast<hsize_t>(mapping.image_count),
total_images - mapping.virtual_first_image);
const std::string source_dataset = mapping.dataset.empty()
? "/entry/data/data"
@@ -221,14 +228,14 @@ void NXmx::LinkToData_ProcessingVDS(const StartMessage &start, const EndMessage
HDF5DataSpace virtual_data_space({total_images, height, width});
virtual_data_space.SelectHyperslab(
{static_cast<hsize_t>(mapping.virtual_first_image), 0, 0},
{static_cast<hsize_t>(mapping.image_count), height, width}
{image_count, height, width}
);
const hsize_t source_extent_images = mapping.source_first_image + mapping.image_count;
const hsize_t source_extent_images = mapping.source_first_image + image_count;
HDF5DataSpace source_data_space({source_extent_images, height, width});
source_data_space.SelectHyperslab(
{static_cast<hsize_t>(mapping.source_first_image), 0, 0},
{static_cast<hsize_t>(mapping.image_count), height, width}
{image_count, height, width}
);
dcpl.SetVirtual(mapping.filename,