From 40da1aab13966e23401783cfae9858bf62312fc5 Mon Sep 17 00:00:00 2001 From: leonarski_f Date: Mon, 15 Jun 2026 20:35:03 +0200 Subject: [PATCH] IndexAndRefine: conventionalise de-novo lattice when cell+SG are given Providing both a unit cell (-C) and space group (-S) silently broke the de-novo indexers (FFT/FFTW) -> 0% indexing, while each flag alone worked. Root cause: the `sg && GetUnitCell()` branch fed the indexer's raw lattice straight into symmetry-constrained refinement. FFBIDX returns the lattice already in the reference setting, but FFT/FFTW return an arbitrarily-oriented Niggli-primitive cell; enforcing the crystal system on its mis-assigned axes rejects every frame. Fix: for de-novo indexers only, reduce the lattice to the conventional setting (LatticeSearch) before refinement. FFBIDX keeps using its raw lattice as-is, so it is byte-identical to before (no regression). niggli_class stays unassigned (0) in this branch - it is a property of the primitive cell incl. centering, which LatticeSearch cannot recover from a user-supplied (possibly centered, e.g. C2) cell. A proper primitive-cell indexing path (CrystFEL-style) is deferred. Validation (lyso, -C79,79,38 -S96): crystal 2 FFT : 0% -> 94.9% (CC1/2 95.8, CCref 92.7) = de-novo quality crystal 2 FFBIDX: 71.4% (CC1/2 94.6) - byte-identical jet FFBIDX: 27.2% (CC1/2 91.9) - byte-identical (Jet FFT stays 0% - that is a separate, still-open issue: de-novo finds no consensus on the weak serial-still frames, 0% even without -S; to investigate.) Co-Authored-By: Claude Opus 4.8 --- image_analysis/IndexAndRefine.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/image_analysis/IndexAndRefine.cpp b/image_analysis/IndexAndRefine.cpp index ebe85b0c..7b9563f8 100644 --- a/image_analysis/IndexAndRefine.cpp +++ b/image_analysis/IndexAndRefine.cpp @@ -101,16 +101,27 @@ IndexAndRefine::IndexingOutcome IndexAndRefine::DetermineLatticeAndSymmetry(Data auto latt = indexer_result.lattice[0]; if (latt.CalcVolume() > 1.0) { auto sg = experiment.GetGemmiSpaceGroup(); + const auto algorithm = experiment.GetIndexingAlgorithm(); + const bool de_novo = (algorithm == IndexingAlgorithmEnum::FFT + || algorithm == IndexingAlgorithmEnum::FFTW); - // If space group and cell provided => always enforce symmetry in refinement - // If space group not provided => guess symmetry + // If space group and cell provided => enforce that symmetry in refinement. + // If not => detect the symmetry from the lattice. if (sg && experiment.GetUnitCell()) { + // FFBIDX returns the lattice already in the reference setting, so use it + // as-is. De-novo indexers (FFT/FFTW) return an arbitrarily-oriented primitive + // cell that must first be reduced to the conventional setting, else enforcing + // the crystal system on mis-assigned axes rejects every frame. Centering is + // taken from the user's space group; niggli_class is left unassigned (0) - it + // is a property of the primitive cell incl. centering, which LatticeSearch + // cannot recover from a cell the user may have given centered (e.g. C2). A + // proper primitive-cell indexing path (CrystFEL-style) is deferred. outcome.symmetry = LatticeMessage{ .centering = sg->centring_type(), .niggli_class = 0, .crystal_system = sg->crystal_system() }; - outcome.lattice_candidate = latt; + outcome.lattice_candidate = de_novo ? LatticeSearch(latt).conventional : latt; } else { auto sym_result = LatticeSearch(latt); outcome.symmetry = LatticeMessage{