rugnux: name every space group the data cannot separate in the summary

The search already knows when several groups share an absence pattern - it
scores them identically, marks them all in the candidate table and prints
"Best space group: I23 or I213 (indistinguishable from these data)". The
one-line summary then dropped that and reported only the representative, so the
run's headline answer claimed a decision the diffraction had not made.

Carry the alternatives through to the summary. It already has them:
ProcessResult holds the whole SearchSpaceGroupResult.

  Space group:     I23 (No. 197) or I213 (No. 199) - indistinguishable from these data

Some of these pairs are enantiomorphs (P4_1 vs P4_3), where the choice needs
phasing or anomalous signal. Others are not, and are worth naming because they
surprise: I23 vs I2_13 and I222 vs I2_12_12_1 differ only by a screw whose
condition h00: h=2n is ALREADY implied by the I-centering condition
h+k+l=2n, so the screw has no observable signature at all. Checked over 35936
reflections with gemmi, the two absence patterns are identical - not nearly, but
exactly. Of the 65 chiral space groups, 13 classes are indistinguishable this
way, the largest being the four-way P3_112 / P3_121 / P3_212 / P3_221.

The representative stays the lowest space-group number, which is why a cubic
insulin comes out I23 where the deposited convention is I2_13. That choice is a
convention and the summary now says so instead of implying it was measured.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-05 22:20:43 +02:00
co-authored by Claude Opus 5
parent b22e1b6822
commit 24ac1a90fc
+14 -2
View File
@@ -1742,8 +1742,20 @@ static int RunRugnux(int argc, char **argv) {
if (result.space_group_number.has_value() && anything_indexed) {
const gemmi::SpaceGroup *sg = gemmi::find_spacegroup_by_number(
static_cast<int>(result.space_group_number.value()));
std::cout << fmt::format("Space group: {} (No. {})", sg ? sg->short_name() : "?",
result.space_group_number.value()) << std::endl;
std::string line = fmt::format("Space group: {} (No. {})", sg ? sg->short_name() : "?",
result.space_group_number.value());
// Name every group the data cannot separate, not just the representative. Some pairs share
// their whole absence pattern - an enantiomorphic pair (P4_1 vs P4_3), or I23 vs I2_13 and
// I222 vs I2_12_12_1, where the screw condition h00: h=2n is already implied by the
// I-centering - so the choice among them is a convention, not a measurement. The search
// reports the representative as the lowest space-group number; saying so here keeps the
// summary from claiming a decision the diffraction did not make.
if (result.space_group_search.has_value())
for (const auto &alt : result.space_group_search->alternatives)
line += fmt::format(" or {} (No. {})", alt.short_name(), alt.number);
if (result.space_group_search.has_value() && !result.space_group_search->alternatives.empty())
line += " - indistinguishable from these data";
std::cout << line << std::endl;
}
if (result.consensus_cell.has_value() && anything_indexed) {
const auto &c = result.consensus_cell.value();