RESULT-CHANGING: the centred-lattice test asks only about a class the lattice metric hosts
The centred-lattice test (run after a triclinic indexing whose search arms could test no operator) takes its candidate from the Niggli character table, which names a class on 3 deg relative tolerances. Le Page, which measures each two-fold's obliquity on the lattice itself at the 1 deg bound every other metric question of the run uses, can find fewer rotations than that class carries. The test then adopted a group the lattice does not host: on a triclinic crystal whose C-monoclinic two-fold is 1.7 deg oblique (XDS refines the same angle), C2 was adopted at operator CC 0.46 and written on a cell the group cannot describe, and the Le Page re-ask that follows cannot demote. The test now runs only where Le Page carries the candidate's holohedry; otherwise it logs why and leaves the question to the Le Page re-ask. Over the rc181 open, in-house and private battery rows this was the one final point group with more rotations than the metric hosts; every other centred-test adoption is hosted and unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UqXYdL4qBGYRiSXSGXfCLu
This commit is contained in:
1 parent
48d895d715
commit
486c0b06d1
2 files changed
+40
-1
No files matched your search
@@ -2642,6 +2642,23 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) {
|
|||||||
// The metric only generates the candidate; the intensity CC gate below makes the decision
|
// The metric only generates the candidate; the intensity CC gate below makes the decision
|
||||||
// (a false pseudo-symmetry is rejected by the correlation).
|
// (a false pseudo-symmetry is rejected by the correlation).
|
||||||
const auto cand = LatticeSearch(*end_msg.rotation_lattice);
|
const auto cand = LatticeSearch(*end_msg.rotation_lattice);
|
||||||
|
// The candidate comes from the Niggli character table, which names a class on relative
|
||||||
|
// tolerances (3 deg). Le Page measures each two-fold's obliquity on the lattice itself, at
|
||||||
|
// the bound every other metric question of the run is put at (LATTICE_MAX_OBLIQUITY_DEG).
|
||||||
|
// Where it finds fewer rotations than the candidate's holohedry the lattice does not host
|
||||||
|
// that class, and what the intensities could confirm is a pseudo-symmetry: measured on a
|
||||||
|
// triclinic crystal whose C-monoclinic two-fold is 1.7 deg oblique (the angle XDS refines
|
||||||
|
// as well), C2 was adopted at operator CC 0.46 and written on a cell the group cannot
|
||||||
|
// describe. So it is not asked here; the metric re-ask below asks Le Page's own class.
|
||||||
|
const auto metric = LePageLattice(*end_msg.rotation_lattice);
|
||||||
|
const bool metric_hosts = metric
|
||||||
|
&& static_cast<size_t>(metric->n_operators) >= HolohedryRotations(cand.system);
|
||||||
|
if (!metric_hosts && cand.system != gemmi::CrystalSystem::Triclinic)
|
||||||
|
logger.Info("Centred-lattice test: the cell's character names a {} lattice, but some of its two-folds "
|
||||||
|
"are more than {:.1f} deg oblique (Le Page finds {} rotation(s)), so the lattice "
|
||||||
|
"does not host it and it is not offered to the intensities",
|
||||||
|
gemmi::crystal_system_str(cand.system), LATTICE_MAX_OBLIQUITY_DEG,
|
||||||
|
metric ? metric->n_operators : 0);
|
||||||
// Exact integer reindex from the indexed setting straight to the candidate conventional
|
// Exact integer reindex from the indexed setting straight to the candidate conventional
|
||||||
// setting: P[i][j] = conv_real[i] . rot_reciprocal[j]. Derived from the two lattices, so it
|
// setting: P[i][j] = conv_real[i] . rot_reciprocal[j]. Derived from the two lattices, so it
|
||||||
// holds even if the refined indexed cell is not exactly LatticeSearch's Niggli cell (whose
|
// holds even if the refined indexed cell is not exactly LatticeSearch's Niggli cell (whose
|
||||||
@@ -2657,7 +2674,7 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) {
|
|||||||
reindex.a[i][j] = std::round(v);
|
reindex.a[i][j] = std::round(v);
|
||||||
reindex_res = std::max(reindex_res, std::fabs(v - reindex.a[i][j]));
|
reindex_res = std::max(reindex_res, std::fabs(v - reindex.a[i][j]));
|
||||||
}
|
}
|
||||||
if (cand.system != gemmi::CrystalSystem::Triclinic && reindex_res < 0.1) {
|
if (metric_hosts && cand.system != gemmi::CrystalSystem::Triclinic && reindex_res < 0.1) {
|
||||||
std::vector<MergedReflection> merged_c = sm.merged;
|
std::vector<MergedReflection> merged_c = sm.merged;
|
||||||
for (auto &m : merged_c)
|
for (auto &m : merged_c)
|
||||||
reindex_hkl(m, reindex);
|
reindex_hkl(m, reindex);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "../common/Coord.h"
|
#include "../common/Coord.h"
|
||||||
#include "../common/UnitCell.h"
|
#include "../common/UnitCell.h"
|
||||||
#include "../image_analysis/lattice_search/LePageLattice.h"
|
#include "../image_analysis/lattice_search/LePageLattice.h"
|
||||||
|
#include "../image_analysis/lattice_search/LatticeSearch.h"
|
||||||
#include "gemmi/symmetry.hpp"
|
#include "gemmi/symmetry.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -207,3 +208,24 @@ TEST_CASE("LePageLattice - a filtered group may not rest on an operator the filt
|
|||||||
});
|
});
|
||||||
CHECK(!r.has_value());
|
CHECK(!r.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("LePageLattice - a two-fold the character table admits but the metric does not host") {
|
||||||
|
// A C-centred monoclinic description whose alpha is 1.7 deg off 90: the Niggli character table,
|
||||||
|
// on its relative tolerances, still names the class, while the two-fold is more oblique than
|
||||||
|
// Le Page's bound, so the lattice does not host it. The centred-lattice test in the rotation merge
|
||||||
|
// asks the intensities about a class only where both agree. A lattice 0.3 deg off is hosted.
|
||||||
|
const CrystalLattice oblique = CrystalLattice(120, 100, 70, 88.3, 115, 90).ToPrimitive('C');
|
||||||
|
const auto named = LatticeSearch(oblique);
|
||||||
|
CHECK(named.system == gemmi::CrystalSystem::Monoclinic);
|
||||||
|
CHECK(named.centering == 'C');
|
||||||
|
const auto metric = LePageLattice(oblique);
|
||||||
|
REQUIRE(metric.has_value());
|
||||||
|
CHECK(metric->n_operators == 1);
|
||||||
|
CHECK(metric->system == gemmi::CrystalSystem::Triclinic);
|
||||||
|
|
||||||
|
const auto hosted = LePageLattice(CrystalLattice(120, 100, 70, 89.7, 115, 90).ToPrimitive('C'));
|
||||||
|
REQUIRE(hosted.has_value());
|
||||||
|
CHECK(hosted->n_operators == 2);
|
||||||
|
CHECK(hosted->system == gemmi::CrystalSystem::Monoclinic);
|
||||||
|
CHECK(hosted->centering == 'C');
|
||||||
|
}
|
||||||
Reference in new issue
Block a user