rugnux: de-double spurious rotation supercell in candidate selection
The two-pass rotation indexer could keep a doubled-axis supercell over its own primitive sub-cell: when the supercell won candidate (ci) order and the primitive cleared the indexed-fraction hysteresis by less than the 0.05 "prefer-earlier" margin, the supercell survived. On pding4_003 full data this gave P222 on a 65.6x131.3x173 cell instead of P422 on 65.6x65.6x173 - the orthorhombic metric forecloses the 4-fold before the symmetry search ever runs. The FFT peak-finding is correct (the primitive cell is among the candidates, just out-ranked). Add a sub-cell override to the selection loop: adopt a later candidate that is a genuinely smaller cell (> ROT_SUBCELL_VOLUME_RATIO=1.5x smaller volume; a doubling is 2x) indexing at least as many spots (within ROT_SUBCELL_FRAC_SLACK=0.02). That is the signature of a spurious doubling - the primitive always indexes >= its integer multiple, whereas a real superstructure's larger cell indexes MORE (kept by the existing clearly-more branch) and twins share the cell volume (untouched). Full 24-crystal rotation battery (pre-fix vs post-fix): pding4_003 P222->P422 (cell halves to 65.6x65.6x173, R_meas 6.6% CC1/2 99.9% ISa 11.9), pding4_001 holds P422, all 22 other crystals bit-identical in space group. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,14 @@
|
||||
#include <future>
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user