rugnux: the short-axis pass checks the premise it was built on

The pass exists because a lattice row shorter than the standard search floor is reported at
its first harmonic above it, so a cell it may rightfully adopt has a reduced axis below that
floor. It never checked that. Where the alternative has no axis under the floor, the floor
cannot be what the standard pass tripped over, and the integer volume ratio is an ordinary
axis multiple - the question the axis-harmonic arbiter settles a few lines above, on the
occupancy of the class the multiple adds. That is evidence about the reflections in dispute,
where the tie this pass reads is a frame count a sublattice cannot lose; where the two
disagree the arbiter is right, and this block was overruling it.

Two crystals were being halved on that account. Both keep their cells now, and the small
molecules the pass was written for are untouched: their reduced axes are all under the floor,
which is the premise holding.

The environment variable that held the pass off is gone with it. It was there to diagnose
exactly this, and a check in the code is the better answer than a switch nobody sets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
This commit is contained in:
2026-09-06 07:10:43 +02:00
co-authored by Claude Opus 5
parent 77390a847b
commit a452797ce5
+29 -10
View File
@@ -584,15 +584,6 @@ static void LogXDSGeometry(const DiffractionExperiment &experiment) {
// ROTATION ONLY. Stills fire the FFT once per image across every worker and routinely run with a
// known cell, so widening the transform there would cost the whole serial run for nothing; the
// caller applies this inside its rotation branch.
// The short-axis first-pass hypothesis below runs by default; RUGNUX_SHORT_AXIS_PASS=0 holds it off.
// It is an extra pair of FFTs on a rotation run and it can only replace the standard answer when the
// alternative both has a near-integer smaller primitive volume and ties the standard cell on the
// validation frames, so the escape hatch is here for diagnosis rather than for routine use.
static bool ShortAxisPassEnabled() {
const char *v = std::getenv("RUGNUX_SHORT_AXIS_PASS");
return v == nullptr || std::string(v) != "0";
}
static void RaiseFFTBoundForKnownCell(IndexingSettings &settings, const DiffractionExperiment &x) {
const auto cell = x.GetUnitCell();
if (!cell.has_value() || !cell->is_finite())
@@ -3088,7 +3079,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
// its settings from experiment_, which never carried the RAISED max bound, so on a cell
// longer than the default search it could only ever return a harmonic of what was asked
// for, tie on frames the way a sub-multiple always does, and be adopted over it.
if (!cancelled_ && !experiment_.GetUnitCell().has_value() && ShortAxisPassEnabled()) {
if (!cancelled_ && !experiment_.GetUnitCell().has_value()) {
auto short_settings = experiment_.GetIndexingSettings();
short_settings.FFT_MinUnitCell_A(IndexingSettings::fft_min_unit_cell_limit_A);
short_settings.IndexingThreads(2);
@@ -3140,6 +3131,34 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
continue;
}
// The premise of this pass, checked rather than asserted. The pass exists because a
// lattice row shorter than the standard floor is reported at its first HARMONIC
// above it, so a cell it may rightfully adopt has a reduced axis BELOW that floor -
// and the floor is applied to the reduced cell, which is the one to measure. Where
// the alternative has no such axis the floor cannot be what the standard pass
// tripped over, and an integer volume ratio is then an ordinary axis multiple: the
// question the axis-harmonic arbiter settles just above on the occupancy of the
// class the multiple adds. That is intensity-class evidence about the very
// reflections in dispute, where the tie below is a frame count this comment already
// says a sublattice cannot lose - so where the two disagree the arbiter is right and
// this block must not overrule it. Measured on the corpus: the four small-molecule
// sets the pass is for all have a reduced axis under 9 A, and the two protein sets
// it halved have 35 and 47 A.
const auto alt_reduced = alt.lattice.ToPrimitive(alt.search_result.centering)
.NiggliReduce().GetUnitCell();
const float alt_shortest = std::min({alt_reduced.a, alt_reduced.b, alt_reduced.c});
const float standard_floor = experiment_.GetIndexingSettings().GetFFT_MinUnitCell_A();
if (alt_shortest >= standard_floor) {
logger.Info("Short-axis pass (floor {:.0f} A), scheme '{}': a {:.0f}x sub-cell "
"(vol {:.0f} A^3), but its shortest reduced axis is {:.1f} A, above "
"the {:.1f} A floor the standard pass already searched - the floor "
"is not why that cell was missed, so this is an ordinary axis "
"multiple and not this pass's to decide; keeping the standing cell",
IndexingSettings::fft_min_unit_cell_limit_A, name, nearest, vol,
alt_shortest, standard_floor);
continue;
}
const int alt_score = count_indexed(*indexer, alt);
// A pass that indexes nothing is not tied with a pass that indexes nothing. Without
// this, 0 against 0 passes the tie band and the volume ratio alone decides, which is