Give the Smargon axes one entry per image
A reader takes the number of images from the innermost axis of the sample chain when no axis varies,
and chi/phi are innermost whenever they are present. Written as scalars, a still or a grid scan with
a recorded head position imported as ONE image however many were collected - dxtbx falls back to
nxsample.depends_on and takes num_images = len(scan_axis).
NXmx has no attribute that would say otherwise: there is no "equipment", and equipment_component
identifies a rigid assembly ("detector_arm", "detector_module"), which dxtbx reads only for the
detector module hierarchy. The axis length is what carries the image count.
Both writer paths are fixed, and the goniometer in BuildTransformationChain now takes its container
whenever the image count is known, as the writer already did - a stationary spindle sent over CBOR
had the same one-image shape.
Verified against DIALS 3.27: same file, chi/phi as scalars imports as 1 image, as per-image arrays
as 5.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VfYvJT5Nb71suJCowRBn5z
This commit is contained in:
@@ -1487,7 +1487,7 @@ std::vector<DetectorTransformation> DiffractionExperiment::BuildTransformationCh
|
||||
if (const auto goniometer = GetGoniometer()) {
|
||||
DetectorTransformation axis(goniometer->GetName(), TransformationType::Rotation,
|
||||
goniometer->GetAxis());
|
||||
if (goniometer->IsScanning() && (image_num > 0))
|
||||
if (image_num > 0)
|
||||
axis.Values(to_float(goniometer->GetAngleContainer(image_num)));
|
||||
else
|
||||
axis.Value(goniometer->GetStart_deg());
|
||||
@@ -1498,12 +1498,15 @@ std::vector<DetectorTransformation> DiffractionExperiment::BuildTransformationCh
|
||||
add(DetectorTransformation("omega", TransformationType::Rotation, {-1, 0, 0}).Value(0.0f));
|
||||
}
|
||||
|
||||
// Smargon chi and phi are ordinary axes that happen not to move.
|
||||
// Smargon chi and phi are ordinary axes that happen not to move. They still carry one entry per
|
||||
// image: a reader takes the number of images from the innermost axis of the sample chain when no
|
||||
// axis varies, and chi/phi are innermost whenever they are present.
|
||||
if (const auto smargon = dataset.GetSmargonPosition()) {
|
||||
const auto n = static_cast<size_t>(std::max<int64_t>(image_num, 1));
|
||||
add(DetectorTransformation("chi", TransformationType::Rotation, smargon->chi_axis)
|
||||
.Value(smargon->chi_deg));
|
||||
.Values(std::vector<float>(n, smargon->chi_deg)));
|
||||
add(DetectorTransformation("phi", TransformationType::Rotation, smargon->phi_axis)
|
||||
.Value(smargon->phi_deg));
|
||||
.Values(std::vector<float>(n, smargon->phi_deg)));
|
||||
}
|
||||
|
||||
return chain;
|
||||
|
||||
@@ -14,6 +14,7 @@ This is an UNSTABLE release. It includes many experimental features, as well as
|
||||
* Tests: a tilted detector is now cross-checked against pyFAI and DIALS, in the unit tests and against a written file in CI.
|
||||
* rugnux: the detector geometry is also logged in XDS's convention (`ORGX`/`ORGY`, detector axis vectors, rotation axis), so it can be compared directly with an XDS refinement.
|
||||
* HDF5: a data file missing next to a VDS master now reads as the error-pixel marker instead of zero counts, so those frames are masked rather than silently integrated as blank.
|
||||
* HDF5: a still or grid scan recorded with a Smargon head position is no longer read back as a single image.
|
||||
* The writer refuses a stream whose start message declares a different pixel format than its images carry, instead of writing a master that does not describe its own data.
|
||||
* HDF5: `module_offset` is written as a float with a proper unit vector, and every transformation offset declares `offset_units`, so a reader does not fall back to the axis's own units - degrees on a rotation - when interpreting a length.
|
||||
* The image stream can carry the sample transformation chain (`transformations`, in the END message) in mounting order, so a goniometer axis, the Smargon chi/phi and a grid stage are described together and unambiguously. It is optional - a producer that does not send it gets the same chain built by the writer - so no metadata version change is needed.
|
||||
|
||||
+10
-3
@@ -758,15 +758,22 @@ void NXmx::Sample(const StartMessage &start, const EndMessage &end) {
|
||||
|
||||
// Smargon chi/phi are static positioners closest to the sample, so they are appended
|
||||
// at the innermost end of the transformation chain (whatever depends_on currently is).
|
||||
auto write_smargon = [&start](HDF5Group& transformations, std::string& depends_on) {
|
||||
auto write_smargon = [&start, &end](HDF5Group& transformations, std::string& depends_on) {
|
||||
if (!start.smargon_position)
|
||||
return;
|
||||
SaveScalar(transformations, "chi", start.smargon_position->chi_deg)->
|
||||
|
||||
// One entry per image, even though neither angle moves. A reader takes the number of images
|
||||
// from the innermost axis of the sample chain when no axis varies - chi and phi are innermost
|
||||
// whenever they are present - so written as scalars, a still with a head position reads back
|
||||
// as a single image however many were collected.
|
||||
const auto n = static_cast<size_t>(std::max<int64_t>(end.max_image_number, 1));
|
||||
|
||||
SaveVector(transformations, "chi", std::vector<double>(n, start.smargon_position->chi_deg))->
|
||||
Transformation("deg", depends_on, "", "", "rotation",
|
||||
{start.smargon_position->chi_axis.x, start.smargon_position->chi_axis.y,
|
||||
start.smargon_position->chi_axis.z}, {0, 0, 0}, "");
|
||||
depends_on = "/entry/sample/transformations/chi";
|
||||
SaveScalar(transformations, "phi", start.smargon_position->phi_deg)->
|
||||
SaveVector(transformations, "phi", std::vector<double>(n, start.smargon_position->phi_deg))->
|
||||
Transformation("deg", depends_on, "", "", "rotation",
|
||||
{start.smargon_position->phi_axis.x, start.smargon_position->phi_axis.y,
|
||||
start.smargon_position->phi_axis.z}, {0, 0, 0}, "");
|
||||
|
||||
Reference in New Issue
Block a user