reader: open a master whose beamline writes standards-correct NXmx differently
Four defects, hit in sequence, that between them stopped eight masters from one beamline before any geometry question was reached. The files are correct NeXus; the reader was assuming one writer's conventions. ReadScalar demanded rank 0. NXmx puts no rank on distance, saturation_value, two_theta or det_z, and these files write them as shape (1,). Any dataspace holding exactly one element is now accepted; a genuine vector is still refused. frame_time was read unconditionally and NXmx does not require it. A virtual-dataset source filename of "." was resolved as a relative path, giving <dir>/. - but "." is HDF5's spelling for THIS file, and these masters compose /entry/data/data as a virtual dataset over datasets in themselves that are external links to the data files. The virtual source's DATASET PATH was parsed and then ignored in favour of a hardcoded /entry/data/data, while these data files keep their images at the root. Fixing that exposed a fifth: the positional-read fast path used the master's own path, but a dataset reached through an external link lives in another file and a chunk address is an offset into THAT file. An audit of all 38 masters in the non-SLS corpus finds exactly these eight need the change and the other 30 need nothing. Corpus A/B over 73 dataset pairs, base and patched back to back: 64 byte-identical results, 9 identical failures, none differing. The blast radius is bounded by construction - JFJochReader is linked only by rugnux and jfjoch_viewer, and the one shared header this touches only ever accepts more, so nothing that opened before can read differently. With it the eight files read completely: 12850 images, no decode errors, seven of them spanning two source datasets through the master's own external links. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lc5JG6kJqZoCWaoZ43JGTW
This commit is contained in:
@@ -844,13 +844,19 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
|
||||
detector.ReadOutTime(std::chrono::nanoseconds(0));
|
||||
dataset->experiment.Detector(detector);
|
||||
|
||||
// frame_time is the period between frames, count_time the exposure within one. NXmx requires
|
||||
// neither, and a master written outside the DECTRIS toolchain often carries only count_time;
|
||||
// falling back to it says "no dead time", which is the honest reading of a file that does not
|
||||
// state one. What is read here is metadata - the one place frame time is divided by is the
|
||||
// JUNGFRAU summation, which a dataset read from a DECTRIS-style file never reaches.
|
||||
const float count_time_s = master_file->GetFloat("/entry/instrument/detector/count_time");
|
||||
dataset->experiment.FrameTime(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::duration<float>(
|
||||
master_file->GetFloat("/entry/instrument/detector/frame_time"))),
|
||||
master_file->GetOptFloat("/entry/instrument/detector/frame_time")
|
||||
.value_or(count_time_s))),
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::duration<float>(
|
||||
master_file->GetFloat("/entry/instrument/detector/count_time")))
|
||||
std::chrono::duration<float>(count_time_s))
|
||||
);
|
||||
|
||||
if (master_file->Exists("/entry/instrument/detector/calibration")) {
|
||||
@@ -878,9 +884,14 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
|
||||
ReadROIMetadata(*master_file, *dataset);
|
||||
|
||||
// Resolve VDS mapping filenames to absolute paths so the image source's locator only ever
|
||||
// deals with real paths, then report the layout to the caller.
|
||||
// deals with real paths, then report the layout to the caller. "." is HDF5's spelling for
|
||||
// "the file this dataset is in", not a relative path - a master is allowed to compose its
|
||||
// VDS over datasets in ITSELF, which are then external links to the data files. Resolved as
|
||||
// a path it became <dir>/. and no image could be opened at all.
|
||||
for (auto &m : vds_data_mappings)
|
||||
m.filename = ResolveRelativeToMaster(master_file_directory, m.filename);
|
||||
m.filename = (m.filename == ".")
|
||||
? master_filename
|
||||
: ResolveRelativeToMaster(master_file_directory, m.filename);
|
||||
|
||||
dataset->experiment.ImagesPerTrigger(number_of_images);
|
||||
cached_geom = dataset->experiment.GetDiffractionGeometry();
|
||||
|
||||
Reference in New Issue
Block a user