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 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 20:35:03 +02:00
parent ecdb7048a0
commit 40da1aab13
+14 -3
View File
@@ -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{