Space group: a screw one axial reflection could not confirm is reported open

A screw candidate whose absent class no reflection contradicts, refused only because its
evidence could not reach min_screw_absence_evidence even with every predicted-absent axial
reflection at the floor (one such reflection reaches 6.9 nats; a row without a control class
reaches nothing), left the symmorphic group adopted and the report saying the group follows from
the absences. Such a row is now SPACE_GROUP_SCREW_UNDETERMINED, with the screw group in
SPACE_GROUP_ALTERNATIVES. Report only: the result is kept apart (unreached_screws) and folded in
after every decision, so the written group and files do not change.

Measured on a monoclinic set whose merge holds five 0k0 reflections at noise level, one of them
predicted absent: P2 was reported as decided by the absences against a deposited P21; it is now
P2 with the b screw open and P21 offered. Over the full battery's candidate tables the condition
holds on that set and one private set (the same P2/P21 case) only. ci tier: no change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
This commit is contained in:
2026-10-13 14:43:44 +02:00
co-authored by Claude Opus 5.5
parent 52136a74f0
commit e5b69647c5
4 changed files with 111 additions and 0 deletions
+38
View File
@@ -551,6 +551,44 @@ TEST_CASE("SearchSpaceGroup says which axis a missing row left the screw open on
}
}
// One predicted-absent axial reflection cannot confirm a screw however weak it is (it reaches 6.9 nats,
// under the claim bar), so a symmorphic group adopted against it is a default, not a determination:
// the screw is reported open, beside the decision, which it does not touch.
TEST_CASE("SearchSpaceGroup leaves a screw open that one axial reflection could not confirm",
"[SearchSpaceGroup]") {
const gemmi::SpaceGroup& sg = gemmi::get_spacegroup_by_name("P 1 21 1");
const auto merged = GenerateMergedReflectionsForSpaceGroup(sg, 12);
SearchSpaceGroupOptions opt;
opt.merge_friedel = true;
SECTION("the whole 0k0 row - the screw is claimed") {
const auto result = SearchSpaceGroup(merged, opt);
INFO(SearchSpaceGroupResultToText(result));
REQUIRE(result.best_space_group.has_value());
CHECK(result.best_space_group->xhm() == "P 1 21 1");
CHECK(result.unreached_screws.empty());
}
SECTION("0k0 holds k = 2, 3, 4 only - the screw is open") {
std::vector<MergedReflection> short_row;
for (const auto& r : merged)
if (r.h != 0 || r.l != 0 || (std::abs(r.k) >= 2 && std::abs(r.k) <= 4))
short_row.push_back(r);
const auto result = SearchSpaceGroup(short_row, opt);
INFO(SearchSpaceGroupResultToText(result));
REQUIRE(result.best_space_group.has_value());
CHECK(result.best_space_group->xhm() == "P 1 2 1");
REQUIRE(result.unreached_screws.size() == 1);
CHECK(result.unreached_screws[0].axis == 'b');
CHECK(result.unreached_screws[0].n_absent == 1);
REQUIRE(result.unreached_screw_groups.size() == 1);
CHECK(result.unreached_screw_groups[0].xhm() == "P 1 21 1");
CHECK(result.undetermined_screws.empty());
CHECK(result.alternatives.empty());
}
}
// The centring half of the same widening. A, B and C centring on one orthorhombic cell are three
// different lattices, and only C is a reference setting, so an A-centred crystal used to have its
// centring refused (its absent class is not the one C predicts) and came out primitive. The