symmetry: enumerate the settings the cell can host, not only the reference ones
The space-group search offered a candidate only if gemmi calls it the reference setting. A setting is a statement about direction, so that restricted the search to the axes the convention chose: a crystal whose 2-fold lies on c had no rung between P1 and 222 and fell to P1, and one whose screws lie on b and c was reported as the group with a single screw on c - the wrong group, not a lower one, because the candidate that predicts a subset of the real absences and nothing else wins on no evidence at all. Both stages now enumerate more, under refusals rather than thresholds. Stage B offers the non-reference settings of the chosen point group. Their rotation set is equal to the chosen one, not merely contained in it, so this cannot raise the symmetry; what it adds is a screw or a centring on the axis the data show it on. A candidate is offered only if the cell's own metric admits the rotations its setting names, and one predicting exactly the absences another candidate already predicts is dropped as the same hypothesis under a second name. A non-reference candidate whose centring class this merge does not contain is refused outright: the reference path may adopt an untested centring because the caller's centred-lattice re-test backs it, and a non-reference setting has no such backing. Stage A offers the rotation sets no reference setting carries - the a-unique and c-unique monoclinic 2-folds, and the two rhombohedral-axes trigonal groups - and only those, so every point group reachable before is still reached by the same group in the same setting. A rung reached only that way may be ADOPTED but does not judge anything else: it is skipped when the reference chi^2 is formed and when a higher promotion's parents are collected. Without that it made higher promotions strictly harder - a promotion answers to the most damning of its parents, and offering two more order-2 subgroups of 222 refused 222 and 432 on crystals that had them. The run report gains SPACE_GROUP_NAME beside SPACE_GROUP_NUMBER, since the number alone does not say which axes a group's symmetry lies on, and a group that is not a reference setting is printed as its extended Hermann-Mauguin name. The two-arm reconciliation compares point groups by that name rather than by number for the same reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3yNBXk4wKdMZy1ak2NY7f
This commit is contained in:
@@ -367,3 +367,126 @@ TEST_CASE("SearchSpaceGroup operator correlation reads symmetry, not the resolut
|
||||
CHECK(std::fabs(two_fold_cc(0.0) - two_fold_cc(6.0)) < 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
// The enumeration reaches the settings gemmi does not call the reference one, and only when the cell
|
||||
// has the axes they name. Both halves are pinned here: `P 1 1 2_1` puts its 2-fold and its screw on
|
||||
// c, which no reference setting can express (Stage A never offers the rotation and Stage B never
|
||||
// offers the group), so without the two options the answer is P1; with them, and with a cell whose
|
||||
// unique axis IS c, it is named; and with a cell whose unique axis is b the same candidate is
|
||||
// refused rather than adopted on axes the crystal does not have.
|
||||
TEST_CASE("SearchSpaceGroup names a non-reference setting only on a cell that hosts it") {
|
||||
const gemmi::SpaceGroup& sg = gemmi::get_spacegroup_by_name("P 1 1 21");
|
||||
const auto merged = GenerateMergedReflectionsForSpaceGroup(sg, 12);
|
||||
|
||||
SearchSpaceGroupOptions opt;
|
||||
opt.merge_friedel = true;
|
||||
|
||||
SECTION("narrow enumeration cannot name it") {
|
||||
const auto result = SearchSpaceGroup(merged, opt);
|
||||
INFO(SearchSpaceGroupResultToText(result));
|
||||
REQUIRE(result.best_space_group.has_value());
|
||||
CHECK(result.best_space_group->number == 1);
|
||||
}
|
||||
|
||||
SECTION("widened enumeration names it on a c-unique cell") {
|
||||
opt.cell = gemmi::UnitCell(40.0, 50.0, 60.0, 90.0, 90.0, 100.0);
|
||||
opt.enumerate_all_settings = true;
|
||||
opt.enumerate_all_rotation_sets = true;
|
||||
const auto result = SearchSpaceGroup(merged, opt);
|
||||
INFO(SearchSpaceGroupResultToText(result));
|
||||
REQUIRE(result.best_space_group.has_value());
|
||||
CHECK(result.best_space_group->xhm() == "P 1 1 21");
|
||||
}
|
||||
|
||||
SECTION("a b-unique cell refuses it") {
|
||||
opt.cell = gemmi::UnitCell(40.0, 50.0, 60.0, 90.0, 100.0, 90.0);
|
||||
opt.enumerate_all_settings = true;
|
||||
opt.enumerate_all_rotation_sets = true;
|
||||
const auto result = SearchSpaceGroup(merged, opt);
|
||||
INFO(SearchSpaceGroupResultToText(result));
|
||||
REQUIRE(result.best_space_group.has_value());
|
||||
CHECK(result.best_space_group->number == 1);
|
||||
}
|
||||
}
|
||||
|
||||
// The screw axes of an orthorhombic crystal can lie on any pair of axes, and only one of the three
|
||||
// namings of #18 is a reference setting. With the narrow enumeration the group that predicts a
|
||||
// SUBSET of the real absences and nothing else wins on no evidence at all, so the reported group is
|
||||
// wrong rather than low - the widening is what makes the correct one available.
|
||||
TEST_CASE("SearchSpaceGroup names an orthorhombic screw pair on the axes it lies on") {
|
||||
const gemmi::SpaceGroup& sg = gemmi::get_spacegroup_by_name("P 2 21 21");
|
||||
const auto merged = GenerateMergedReflectionsForSpaceGroup(sg, 14);
|
||||
|
||||
SearchSpaceGroupOptions opt;
|
||||
opt.merge_friedel = true;
|
||||
opt.lattice_system = gemmi::CrystalSystem::Orthorhombic;
|
||||
|
||||
SECTION("narrow enumeration reports the wrong group") {
|
||||
const auto result = SearchSpaceGroup(merged, opt);
|
||||
INFO(SearchSpaceGroupResultToText(result));
|
||||
REQUIRE(result.best_space_group.has_value());
|
||||
CHECK(result.best_space_group->number != 18);
|
||||
}
|
||||
|
||||
SECTION("widened enumeration reports it") {
|
||||
opt.cell = gemmi::UnitCell(40.0, 50.0, 60.0, 90.0, 90.0, 90.0);
|
||||
opt.enumerate_all_settings = true;
|
||||
const auto result = SearchSpaceGroup(merged, opt);
|
||||
INFO(SearchSpaceGroupResultToText(result));
|
||||
REQUIRE(result.best_space_group.has_value());
|
||||
CHECK(result.best_space_group->xhm() == "P 2 21 21");
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// candidate is now offered, and it has to be adopted from its own absences rather than from the
|
||||
// metric, which cannot tell A from C at all.
|
||||
TEST_CASE("SearchSpaceGroup names an A-centred orthorhombic lattice") {
|
||||
const gemmi::SpaceGroup& sg = gemmi::get_spacegroup_by_name("A 2 2 2");
|
||||
const auto merged = GenerateMergedReflectionsForSpaceGroup(sg, 12);
|
||||
|
||||
SearchSpaceGroupOptions opt;
|
||||
opt.merge_friedel = true;
|
||||
opt.lattice_system = gemmi::CrystalSystem::Orthorhombic;
|
||||
|
||||
SECTION("narrow enumeration cannot name it") {
|
||||
const auto result = SearchSpaceGroup(merged, opt);
|
||||
INFO(SearchSpaceGroupResultToText(result));
|
||||
REQUIRE(result.best_space_group.has_value());
|
||||
CHECK(result.best_space_group->centring_type() != 'A');
|
||||
}
|
||||
|
||||
SECTION("widened enumeration names it") {
|
||||
opt.cell = gemmi::UnitCell(40.0, 50.0, 60.0, 90.0, 90.0, 90.0);
|
||||
opt.enumerate_all_settings = true;
|
||||
const auto result = SearchSpaceGroup(merged, opt);
|
||||
INFO(SearchSpaceGroupResultToText(result));
|
||||
REQUIRE(result.best_space_group.has_value());
|
||||
CHECK(result.best_space_group->xhm() == "A 2 2 2");
|
||||
}
|
||||
}
|
||||
|
||||
// The null the widening has to survive. Stage A's second pass offers the a- and c-unique 2-folds on
|
||||
// any metric that could host them, which is every orthorhombic one - so a genuinely triclinic
|
||||
// crystal sitting on a pseudo-orthorhombic cell is now offered three promotions where it used to be
|
||||
// offered one. It must still be refused all three: the added candidates go through the same operator
|
||||
// correlation as every other, and a rotation the intensities do not have scores nothing.
|
||||
TEST_CASE("SearchSpaceGroup does not promote triclinic data on a pseudo-orthorhombic cell") {
|
||||
const gemmi::SpaceGroup& sg = gemmi::get_spacegroup_by_name("P 1");
|
||||
const auto merged = GenerateMergedReflectionsForSpaceGroup(sg, 10);
|
||||
|
||||
SearchSpaceGroupOptions opt;
|
||||
opt.merge_friedel = true;
|
||||
opt.lattice_system = gemmi::CrystalSystem::Orthorhombic;
|
||||
opt.cell = gemmi::UnitCell(40.0, 50.0, 60.0, 90.0, 90.0, 90.0);
|
||||
opt.enumerate_all_settings = true;
|
||||
opt.enumerate_all_rotation_sets = true;
|
||||
|
||||
const auto result = SearchSpaceGroup(merged, opt);
|
||||
INFO(SearchSpaceGroupResultToText(result));
|
||||
REQUIRE(result.best_space_group.has_value());
|
||||
CHECK(result.best_space_group->number == 1);
|
||||
CHECK(result.point_group_order == 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user