Files
Jungfraujoch/image_analysis/IntegrationOutcome.h
T
leonarski_f 3882b0f062 Serial stills with a reference or model: crystals their own cells anchor keep that indexing
The per-image reference/model choice of indexing ignored the crystals' refined cells. On a
pseudo-merohedral cell, where at least 90% of the crystals' cells fit the run's cell better as
indexed than after a twin law, the cells decide that law - the rule the data-only resolution
already uses (AnchorOnCells, factored out of ResolveReindexReferenceFree and shared). The resolver
now keeps each image's CC per indexing (IntegrationOutcome::reference_cc); before the stills
merge, AnchorReferenceReindex puts every image back as indexed, applies the cell rule, and lets
the reference choose only among the indexings left open. Exactly merohedral lattices (no metric
signal) are unaffected.

Still battery (REFMAC 0-cycle R-free, model before -> anchored; data-only in brackets):
pal90 0.1883 -> 0.1834 (0.1832), alvra213 0.2073 -> 0.2011 (0.2013), alvra213_int16
0.2081 -> 0.2021 (0.2024); euxfel83 0.2037, euxfel152 0.2452, sacla237, lcls222 unchanged;
lcls242_t 0.2847 -> 0.2952 (0.2942): there the model had moved about half the crystals against
cells that decide all three laws (96/96/100%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
(cherry picked from commit 85c804676ffd15ffd66e2ba05928000d6f4e765f)
2026-10-26 06:49:20 +01:00

42 lines
1.9 KiB
C++

// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <algorithm>
#include <vector>
#include "../common/Reflection.h"
#include "../common/CrystalLattice.h"
#include "../common/DiffractionGeometry.h"
#include "ReflectionArena.h"
// A frame's integrated reflections. The whole-run passes keep them in a ReflectionArena (see there);
// anywhere else the allocator is plain new/delete.
using ReflectionVector = std::vector<Reflection, ArenaAllocator<Reflection>>;
struct IntegrationOutcome {
DiffractionGeometry geom;
CrystalLattice latt;
ReflectionVector reflections;
std::optional<float> mosaicity_deg;
std::optional<float> image_scale_cc;
std::optional<int64_t> image_scale_cc_n;
std::optional<float> image_scale_g;
std::optional<float> image_scale_wedge_deg;
// Stills with a reference: the image's CC with it in each indexing it was offered, as indexed first
// (ReindexAmbiguityResolver::Resolve); empty otherwise.
std::vector<double> reference_cc;
};
// How far apart, in frames, two partials of one raw hkl may sit and still belong to the same rocking
// event. The quantity being bridged is an ANGLE - a reflecting range, a tenth to half a degree - so
// spelling it as a frame count makes the bridge grow with the slicing: at 1 deg per frame the two
// frames that were always allowed are 2 deg of dead rotation, as wide as a whole event, and two
// genuine Ewald crossings fuse into one "full". Half a degree of bridge instead, floored at one frame
// (never cut an event at its own neighbours) and capped at the two frames that were always used. For
// any wedge of 0.25 deg or less the quotient is at least two and the cap returns the literal 2.0f, so
// finely sliced data is bridged exactly as before.
inline float RockingEventFrameGap(float wedge_deg) {
return std::min(2.0f, std::max(1.0f, 0.5f / wedge_deg));
}