diff --git a/image_analysis/rotation_indexer/RotationIndexer.cpp b/image_analysis/rotation_indexer/RotationIndexer.cpp index 540fe022..355e5b00 100644 --- a/image_analysis/rotation_indexer/RotationIndexer.cpp +++ b/image_analysis/rotation_indexer/RotationIndexer.cpp @@ -12,6 +12,14 @@ #include namespace { + // Sub-cell override thresholds used in candidate selection to undo a spurious axis doubling: + // a later candidate replaces the chosen cell when it is smaller by more than this volume ratio + // (a doubling is 2x, well past 1.5) and indexes within this fraction slack of it. The slack is + // far below the indexed-fraction gap a real superstructure opens between its true cell and its + // sub-cell, so genuine large cells are kept. + constexpr float ROT_SUBCELL_VOLUME_RATIO = 1.5f; + constexpr float ROT_SUBCELL_FRAC_SLACK = 0.02f; + // Re-express a primitive hexagonal/trigonal lattice in the conventional hexagonal setting // (a = b, gamma = 120). The Niggli-reduced primitive cell carries the two equal-length axes // at gamma = 60; replacing b with b - a opens that angle to 120 without changing the lattice. @@ -224,6 +232,7 @@ void RotationIndexer::RunIndexing() { // Assemble and select serially, in candidate order - identical to refining them one by one. float best_frac = -1.0f; + float best_vol = 0.0f; bool have_best = false; size_t best_ci = 0; XtalOptimizerData best_data; @@ -264,8 +273,21 @@ void RotationIndexer::RunIndexing() { // marginally-higher alternative from displacing the primary when both index poorly (e.g. a // twin, where the accumulated-spot fraction is a noisy proxy) - only a decisively better // cell (a superstructure's true cell vs its sublattice) takes over. - if (!have_best || (frac > best_frac + 0.05f && frac > 0.15f)) { + // Displace the current best when the candidate indexes clearly more, OR when it is a + // genuine sub-cell: a meaningfully smaller cell that still indexes at least as many spots. + // The sub-cell branch unmasks a spurious supercell (axis doubling): the primitive cell + // always indexes >= its integer multiple, so a doubled cell that wins ci-order by the + // hysteresis margin is overridden by its own primitive. A real superstructure's true + // (larger) cell indexes MORE than its sub-cell and is kept by the clearly-more branch; + // twin lattices share the cell volume, so this never disturbs twin selection. + const float cand_vol = std::abs(data.latt.CalcVolume()); + const bool clearly_more = frac > best_frac + 0.05f && frac > 0.15f; + const bool smaller_subcell = have_best && frac > 0.15f + && frac >= best_frac - ROT_SUBCELL_FRAC_SLACK + && cand_vol < best_vol / ROT_SUBCELL_VOLUME_RATIO; + if (!have_best || clearly_more || smaller_subcell) { best_frac = frac; + best_vol = cand_vol; have_best = true; best_data = std::move(data); best_sr = sr;