Open a file whose sample axis does not turn

The reader looks for the goniometer by walking every leaf of /entry/sample/transformations and
calling ReadAxis on each, stopping early only at an axis that is scanning. Not every leaf is an
axis: the writer's own AXISNAME_end and the two rotation-width scalars carry units and nothing else,
and ReadAxis threw when transformation_type was absent.

A sweep survived on alphabetical order alone - omega sorts before omega_end, so the walk stopped
before reaching it. A stationary axis never stopped, reached omega_end, and the open failed
outright with "Cannot open attribute transformation_type". This branch is what made that reachable,
by giving a still and a grid scan a spindle that stands still: rugnux read a grid-scan master, got a
stationary axis, wrote _process.h5 through the goniometer path - which does emit omega_end - and
could no longer open its own output.

ReadAxis now treats a missing transformation_type as "not a transformation" and skips it, which is
also what makes the search safe against anything a third party leaves in that group.

JFJochReader_AxisRecovery covers what the reader has to recover: a sweep, a sweep about an axis that
is not called omega, a spindle that does not turn, a grid scan alone and under a turning spindle,
and a sweep with the head at a Smargon position.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VfYvJT5Nb71suJCowRBn5z
This commit is contained in:
2026-08-23 12:39:00 +02:00
co-authored by Claude Opus 5
parent d57f66a0b6
commit d0ac559e64
2 changed files with 125 additions and 1 deletions
+7 -1
View File
@@ -1124,7 +1124,13 @@ std::optional<GoniometerAxis> HDF5MetadataSource::ReadAxis(HDF5Object *file, con
if (angle.empty())
return {};
if (dataset.ReadAttrStr("transformation_type") != "rotation")
// Not everything in the group is an axis. The writer's own AXISNAME_end and the two rotation
// width scalars carry only units, and a file from anywhere else may hold whatever it likes.
// Missing attribute means "not a transformation", so skip it rather than throwing: the search
// for the goniometer walks every leaf and only stops early on an axis that turns, so a master
// whose axis was stationary reached omega_end and could not be opened at all.
if (!dataset.AttrExists("transformation_type")
|| (dataset.ReadAttrStr("transformation_type") != "rotation"))
return {};
std::vector<double> end = file->ReadOptVector<double>(dname + "_end");