diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index 2b317db0c..c08a7617e 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -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