rugnux: keep merohedral twins in true symmetry, robustify twinning stats
SearchSpaceGroup: drop the log10(chi2_ref) widening of the point-group chi^2-ratio bound and tighten max_merge_chi2_ratio 2.0 -> 1.85. The variance-floor fix removed the error-model miscalibration the widening compensated for, so the widening now only let a partial merohedral twin through (Ins_H_2 R3->R32 twin 2-fold: ratio 2.01). Every genuine high symmetry across the rotation-test battery stays within ~1.7x (worst real case Thau P41212 at 1.71), so 1.85 keeps R3 in its true lower symmetry. TwinningAnalysis: make the <I^2>/<I>^2 second moment robust - skip noise-only shells (<I/sigma> < 1) and reject Wilson outliers (E^2 > 8) with one shell-mean re-iteration, so a single strong reflection in a collapsed-mean shell no longer dominates the moment. Add MerohedralTwinningPossible: in a holohedral Laue class (4/mmm, 6/mmm, m-3m, rhombohedral -3m) no twin law exists, so a low <|L|> there is a statistical artefact and is no longer flagged as twinning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -388,17 +388,17 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
int best_pg_order = 0;
|
||||
double best_pg_min_cc = -2.0;
|
||||
for (const auto& c : pg_cands) {
|
||||
// Widen the chi^2 ratio bound by the error-model miscalibration. When even the best candidate's
|
||||
// reduced chi^2 (chi2_ref) is far above 1 (weak data / uncorrected decay), the ratio grows with
|
||||
// point-group order for genuine high symmetry too, so the fixed bound wrongly rejects it. Adding
|
||||
// log10(chi2_ref) lets the bound exceed max_merge_chi2_ratio by ~the order of magnitude of the
|
||||
// miscalibration - enough to keep the true group (F432 weak data: chi2_ref~150, ratio 3.2 vs
|
||||
// bound ~4.2) while STILL rejecting a false operator whose ratio is far worse than its subgroup's
|
||||
// (a twin/pseudo R32 on calibrated data: chi2_ref~2.6, ratio 4.2 vs bound ~2.4). The bound stays
|
||||
// a per-candidate chi^2 test, so chi^2 still arbitrates between candidates - it is never bypassed.
|
||||
const double allowed_ratio = opt.max_merge_chi2_ratio + std::log10(std::max(1.0, chi2_ref));
|
||||
// A genuine symmetry operator merges equivalent reflections, so it barely changes the reduced
|
||||
// chi^2 relative to the best subgroup - across the whole rotation-test battery every correct
|
||||
// point group stays within ~1.7x, even on weak or badly-integrated data (F432 chi2_ref 8.3 ->
|
||||
// 1.15; Thau P41212 -> 1.71). A twin law or pseudo-symmetry forces non-equivalent reflections
|
||||
// together, so its ratio is markedly higher (Ins_H_2's twin 2-fold: R3 3.02 -> R32 6.07, ratio
|
||||
// 2.01). max_merge_chi2_ratio sits between the two. (An earlier log10(chi2_ref) widening
|
||||
// compensated for an under-calibrated error model that inflated real-symmetry ratios with data
|
||||
// weakness; the variance-floor fix removed that inflation, and the widening now only let the
|
||||
// twin through, so it is gone.)
|
||||
const bool consistent = c.pg->rotations.empty() || !std::isfinite(c.chi2) ||
|
||||
!std::isfinite(chi2_ref) || c.chi2 <= chi2_ref * allowed_ratio;
|
||||
!std::isfinite(chi2_ref) || c.chi2 <= chi2_ref * opt.max_merge_chi2_ratio;
|
||||
if (!consistent)
|
||||
continue;
|
||||
if (c.order > best_pg_order || (c.order == best_pg_order && c.min_class_cc > best_pg_min_cc)) {
|
||||
|
||||
Reference in New Issue
Block a user