Merge branch 'c-cubic-fix' into pool-C
This commit is contained in:
commit
af7809ea0e
3 files changed
+184
-67
No files matched your search
@@ -1115,6 +1115,45 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
result.merge_r_floor = r_num / r_den;
|
||||
}
|
||||
|
||||
// The other end of the same scale: the R a FALSE operator reads on this crystal (see
|
||||
// SearchSpaceGroupResult::random_pairing_r). Pair every present reflection with an unrelated one
|
||||
// at the same resolution and form the same intensity-weighted R. A false operator relates
|
||||
// reflections that are unrelated apart from sharing a resolution, so this is where its R sits -
|
||||
// measured on this merge's own intensity distribution rather than assumed (the Wilson value 0.5
|
||||
// is off by up to 20% once centrics, a pseudo-translation, the E^2 cap and the resolution range
|
||||
// are in: 0.41 to 0.66 over the rotation corpus).
|
||||
//
|
||||
// Shell-matched and deterministic: the present reflections are ordered by resolution, cut into
|
||||
// equal-count shells of a hundred, and each one paired with the reflection half a shell away. No
|
||||
// RNG, so two runs over the same merge read the same number.
|
||||
{
|
||||
std::vector<size_t> order;
|
||||
order.reserve(n);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
if (pass_cc[i])
|
||||
order.push_back(i);
|
||||
std::sort(order.begin(), order.end(),
|
||||
[&](size_t a, size_t b) { return merged[a].d > merged[b].d; });
|
||||
const size_t per = 100;
|
||||
double r_num = 0.0, r_den = 0.0;
|
||||
for (size_t lo = 0; lo < order.size(); lo += per) {
|
||||
const size_t hi = std::min(order.size(), lo + per);
|
||||
const size_t m = hi - lo;
|
||||
if (m < 10)
|
||||
continue;
|
||||
for (size_t p = 0; p < m; ++p) {
|
||||
const double a = I[order[lo + p]];
|
||||
const double b = I[order[lo + (p + m / 2) % m]];
|
||||
if (a + b > 0.0) {
|
||||
r_num += std::fabs(a - b);
|
||||
r_den += a + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (r_den > 0.0)
|
||||
result.random_pairing_r = r_num / r_den;
|
||||
}
|
||||
|
||||
// --- Stage A: score each distinct rotation operator once ---
|
||||
auto score_operator = [&](const gemmi::Op& op, std::vector<uint32_t>& visited,
|
||||
uint32_t& epoch) -> SpaceGroupOperatorScore {
|
||||
@@ -1602,21 +1641,24 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
// a pseudo-symmetric cascade the parent-normalised gates wave through (a 2 -> 222 -> 422 built
|
||||
// by pooling one real 2-fold with false ones), and it is the only gate that can act on the
|
||||
// first step out of P1. Not rescuable: the b rescue above only lifts a chi^2-borderline case.
|
||||
// - When the data confirm more than one operator, judge the added operators' R against the
|
||||
// GLOBALLY best-agreeing operator (the smallest R anywhere): on a genuine group every
|
||||
// operator agrees about as well as that best one, so the ratio is ~1; a false operator -
|
||||
// even one whose group's members all agree among themselves, like a pseudo-tetragonal 4 -
|
||||
// sits far above it (see max_operator_r_over_best).
|
||||
// - When the data confirm more than one operator, place the added operators' R on the scale
|
||||
// the crystal itself defines: the best-agreeing operator anywhere at one end, unrelated
|
||||
// reflections (random_pairing_r) at the other. A genuine operator sits at the clean end,
|
||||
// a false one at the random end (see min_operator_r_contrast).
|
||||
// - The first step out of P1, where only one operator is confirmed at all, has no other
|
||||
// operator to be the best - fall back to the merge's own random-noise R floor
|
||||
// operator to be the clean end - fall back to the merge's own random-noise R floor
|
||||
// (max_operator_r_over_floor).
|
||||
bool r_refused = false;
|
||||
double r_gate_value = std::numeric_limits<double>::quiet_NaN();
|
||||
if (std::isfinite(c.r_added)) {
|
||||
if (n_confirmed_ops >= 2 && std::isfinite(global_best_r) && global_best_r > 0.0) {
|
||||
r_gate_value = c.r_added / global_best_r;
|
||||
r_refused = r_gate_value > opt.max_operator_r_over_best;
|
||||
} else if (std::isfinite(result.merge_r_floor) && result.merge_r_floor > 0.0) {
|
||||
if (n_confirmed_ops >= 2 && std::isfinite(global_best_r) && global_best_r > 0.0
|
||||
&& std::isfinite(result.random_pairing_r)
|
||||
&& global_best_r <= opt.max_reference_over_random * result.random_pairing_r) {
|
||||
r_gate_value = (result.random_pairing_r - c.r_added)
|
||||
/ (result.random_pairing_r - global_best_r);
|
||||
r_refused = r_gate_value < opt.min_operator_r_contrast;
|
||||
} else if (n_confirmed_ops < 2
|
||||
&& std::isfinite(result.merge_r_floor) && result.merge_r_floor > 0.0) {
|
||||
r_gate_value = c.r_added / result.merge_r_floor;
|
||||
r_refused = r_gate_value > opt.max_operator_r_over_floor;
|
||||
}
|
||||
@@ -1648,15 +1690,23 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
"fraction " + FormatDouble(std::max(0.0, 0.5 - h_added), 2)
|
||||
+ " or more would";
|
||||
else if (r_refused)
|
||||
refused_why = "its added operators' intensity-weighted R is "
|
||||
+ FormatDouble(r_gate_value, 1) + "x "
|
||||
+ (n_confirmed_ops >= 2
|
||||
? "the best-agreeing operator in the data (bound "
|
||||
+ FormatDouble(opt.max_operator_r_over_best, 1)
|
||||
: "the merge's own random-noise floor (bound "
|
||||
+ FormatDouble(opt.max_operator_r_over_floor, 1))
|
||||
+ "x) - at full resolution the added operator relates reflections that "
|
||||
"disagree far beyond measurement error, i.e. it is not a real symmetry";
|
||||
refused_why = n_confirmed_ops >= 2
|
||||
? "its added operators' intensity-weighted R ("
|
||||
+ FormatDouble(c.r_added, 3) + ") lies only "
|
||||
+ FormatDouble(100.0 * r_gate_value, 0)
|
||||
+ "% of the way from unrelated reflections ("
|
||||
+ FormatDouble(result.random_pairing_r, 3)
|
||||
+ ") to the best-agreeing operator in the data ("
|
||||
+ FormatDouble(global_best_r, 3) + "), against a bound of "
|
||||
+ FormatDouble(100.0 * opt.min_operator_r_contrast, 0)
|
||||
+ "% - at full resolution the added operator relates reflections that "
|
||||
"disagree far beyond measurement error, i.e. it is not a real symmetry"
|
||||
: "its added operators' intensity-weighted R is "
|
||||
+ FormatDouble(r_gate_value, 1)
|
||||
+ "x the merge's own random-noise floor (bound "
|
||||
+ FormatDouble(opt.max_operator_r_over_floor, 1)
|
||||
+ "x) - at full resolution the added operator relates reflections that "
|
||||
"disagree far beyond measurement error, i.e. it is not a real symmetry";
|
||||
else if (std::isfinite(c.chi2) && std::isfinite(chi2_ref))
|
||||
refused_why = "merge chi^2 is " + FormatDouble(c.chi2 / chi2_ref, 2)
|
||||
+ "x the subgroup's (bound " + FormatDouble(opt.max_merge_chi2_ratio, 2) + ")";
|
||||
@@ -1717,11 +1767,15 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
result.h_ratio = c.h_ratio;
|
||||
result.r_added = c.r_added;
|
||||
result.r_over_floor = c.r_over_floor;
|
||||
if (std::isfinite(c.r_added) && std::isfinite(global_best_r) && global_best_r > 0.0)
|
||||
if (std::isfinite(c.r_added) && std::isfinite(global_best_r) && global_best_r > 0.0) {
|
||||
result.r_over_best = c.r_added / global_best_r;
|
||||
if (std::isfinite(result.random_pairing_r) && result.random_pairing_r > global_best_r)
|
||||
result.r_contrast = (result.random_pairing_r - c.r_added)
|
||||
/ (result.random_pairing_r - global_best_r);
|
||||
}
|
||||
}
|
||||
result.h_ratio_bound = opt.max_operator_h_ratio;
|
||||
result.r_over_best_bound = opt.max_operator_r_over_best;
|
||||
result.r_contrast_bound = opt.min_operator_r_contrast;
|
||||
result.r_over_floor_bound = opt.max_operator_r_over_floor;
|
||||
|
||||
// The FINALIST LEDGER: every operator-confirmed hypothesis with its evidence vector, adopted and
|
||||
@@ -1741,8 +1795,12 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
e.b_over_parent = c.b_extra / c.parent_b_used;
|
||||
e.h_ratio = c.h_ratio;
|
||||
e.r_added = c.r_added;
|
||||
if (std::isfinite(c.r_added) && std::isfinite(global_best_r) && global_best_r > 0.0)
|
||||
if (std::isfinite(c.r_added) && std::isfinite(global_best_r) && global_best_r > 0.0) {
|
||||
e.r_over_best = c.r_added / global_best_r;
|
||||
if (std::isfinite(result.random_pairing_r) && result.random_pairing_r > global_best_r)
|
||||
e.r_contrast = (result.random_pairing_r - c.r_added)
|
||||
/ (result.random_pairing_r - global_best_r);
|
||||
}
|
||||
e.r_over_floor = c.r_over_floor;
|
||||
e.adopted = (c.pg == best_pg);
|
||||
e.eligible = c.eligible;
|
||||
@@ -2484,13 +2542,18 @@ std::string FinalistLedgerToText(const SearchSpaceGroupResult& result) {
|
||||
<< " (no half-set intensities in this merge, so no random-noise floor;\n every R/floor "
|
||||
"reads '-' and the first step out of P1 is not measurable here).\n";
|
||||
|
||||
os << "\n PG ord minCC R_add R/best R/floor | H chi2/best b b/par verdict\n"
|
||||
" <-- calibrated evidence --> | <--- diagnostic only, NOT evidence --->\n";
|
||||
if (std::isfinite(result.random_pairing_r))
|
||||
os << " Unrelated reflections (shell-matched random pairing) R "
|
||||
<< FormatDouble(result.random_pairing_r, 4)
|
||||
<< " - the far end of the contrast scale.\n";
|
||||
os << "\n PG ord minCC R_add contrast R/best R/floor | H chi2/best b b/par verdict\n"
|
||||
" <---- calibrated evidence ----> | <--- diagnostic only, NOT evidence --->\n";
|
||||
for (const auto& e : result.point_group_ledger) {
|
||||
os << " " << std::left << std::setw(8) << e.point_group_hm << std::right << std::setw(3)
|
||||
<< e.order
|
||||
<< std::setw(8) << LedgerCell(e.min_class_cc, 3)
|
||||
<< std::setw(8) << LedgerCell(e.r_added, 4)
|
||||
<< std::setw(9) << LedgerCell(e.r_contrast, 2)
|
||||
<< std::setw(7) << LedgerCell(e.r_over_best, 2)
|
||||
<< std::setw(8) << LedgerCell(e.r_over_floor, 2)
|
||||
<< " |" << std::setw(6) << LedgerCell(e.h_ratio, 2)
|
||||
@@ -2501,9 +2564,9 @@ std::string FinalistLedgerToText(const SearchSpaceGroupResult& result) {
|
||||
<< (e.refused_reason.empty() ? "" : " (" + e.refused_reason + ")");
|
||||
// The margin, on the row that decided. The value alone does not say how close the run came to
|
||||
// being refused, and on this corpus that distance is small enough to matter (below).
|
||||
if (e.adopted && std::isfinite(e.r_over_best) && result.r_over_best_bound > 0.0)
|
||||
os << " [" << FormatDouble(result.r_over_best_bound - e.r_over_best, 2)
|
||||
<< " below the " << FormatDouble(result.r_over_best_bound, 2) << " bound]";
|
||||
if (e.adopted && std::isfinite(e.r_contrast) && result.r_contrast_bound > 0.0)
|
||||
os << " [" << FormatDouble(e.r_contrast - result.r_contrast_bound, 2)
|
||||
<< " above the " << FormatDouble(result.r_contrast_bound, 2) << " bound]";
|
||||
else if (e.adopted && std::isfinite(e.r_over_floor) && result.r_over_floor_bound > 0.0)
|
||||
os << " [" << FormatDouble(result.r_over_floor_bound - e.r_over_floor, 2)
|
||||
<< " below the " << FormatDouble(result.r_over_floor_bound, 2) << " floor bound]";
|
||||
@@ -2512,14 +2575,17 @@ std::string FinalistLedgerToText(const SearchSpaceGroupResult& result) {
|
||||
|
||||
// How to read it. Every sentence here is something the numbers above have been measured to NOT
|
||||
// support, and each was a wrong reading somebody actually made.
|
||||
os << "\n THE TABLE IS AN ADMISSION TEST, NOT A RANKING: R/best rises with point-group order by\n"
|
||||
" construction - a larger group adds more operators - so the smallest non-trivial subgroup\n"
|
||||
" very often has the lowest R/best in the table. Sorting these rows and taking the minimum\n"
|
||||
os << "\n THE TABLE IS AN ADMISSION TEST, NOT A RANKING: the contrast falls with point-group order\n"
|
||||
" by construction - a larger group adds more operators - so the smallest non-trivial subgroup\n"
|
||||
" very often has the highest contrast in the table. Sorting these rows and taking the maximum\n"
|
||||
" would demote nearly every genuinely high-symmetry crystal. The question the table answers\n"
|
||||
" is 'the largest order whose R/best stays near 1', not 'which row is smallest'.\n"
|
||||
"\n ONLY R/best AND R/floor ARE EVIDENCE. They are ratios to a reference a false hypothesis\n"
|
||||
" cannot move: the best-agreeing operator anywhere in the data, and the crystal's own\n"
|
||||
" half-set noise floor. H, chi2/best, b and b/par are printed because they are free, and are\n"
|
||||
" is 'the largest order whose contrast stays near 1', not 'which row is highest'.\n"
|
||||
"\n ONLY contrast AND R/floor ARE EVIDENCE. Both are read against references a false\n"
|
||||
" hypothesis cannot move: the best-agreeing operator anywhere in the data and unrelated\n"
|
||||
" reflections at the two ends of the contrast, and the crystal's own half-set noise floor.\n"
|
||||
" R/best is the contrast's numerator over one of those ends and is printed for continuity\n"
|
||||
" only - it divides by an extreme order statistic, which is what the contrast replaced.\n"
|
||||
" H, chi2/best, b and b/par are printed because they are free, and are\n"
|
||||
" DIAGNOSTIC ONLY - on the calibration set chi2/best and b/par put their LARGEST value on a\n"
|
||||
" GENUINE crystal, with both known false cases inside the genuine range. A ratio to the\n"
|
||||
" PARENT is contaminated whenever the parent is itself false, which is exactly the case\n"
|
||||
@@ -2530,10 +2596,13 @@ std::string FinalistLedgerToText(const SearchSpaceGroupResult& result) {
|
||||
" merge, and a wrong LATTICE is not a hypothesis here at all - the cell is an input to this\n"
|
||||
" search, not something it weighs. A crystal whose only error is one of those has a\n"
|
||||
" clean-looking ledger, and a clean ledger is therefore not a clean bill of health.\n"
|
||||
"\n MARGIN. On the corpus this was calibrated on, the largest R/best ever seen on a GENUINE\n"
|
||||
" adopted group is 1.90, against a refusal bound of 2.00. The accept side has ~0.10 of\n"
|
||||
" headroom, not the wide gap the refused rows suggest, so a margin printed above as small is\n"
|
||||
" a real one - and any future tightening of this bound has almost none to spend.\n";
|
||||
"\n MARGIN. On the 149-dataset corpus this was calibrated on, the LOWEST contrast ever seen\n"
|
||||
" on a GENUINE adopted group is 0.77, against a refusal bound of 0.72, and the highest on a\n"
|
||||
" false one is 0.695. Both sides have ~0.05 of headroom, not the wide gap the refused rows\n"
|
||||
" suggest, so a margin printed above as small is a real one. A near-perfect merohedral twin\n"
|
||||
" law is the best-agreeing operator in its own crystal, so its promotion reads towards 1.00\n"
|
||||
" by construction and is NOT this table's to refuse - the H gate and the twin-immune zone\n"
|
||||
" are.\n";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
@@ -2607,14 +2676,17 @@ std::string SearchSpaceGroupResultToText(const SearchSpaceGroupResult& result,
|
||||
os << " H ratio not available (no parent group to normalise against, or too few pairs).\n";
|
||||
// R = intensity-weighted sum|I1-I2|/sum(I1+I2) across the operator's pairs (strong reflections
|
||||
// dominate, where the median H under-weights them). The merge-degradation gate scores the added
|
||||
// operators' R against a clean reference - the globally best-agreeing operator, or the merge's
|
||||
// own random-noise R floor (from the half-dataset merges) on the first step out of P1 - so it
|
||||
// catches false symmetry the parent-normalised H waves through.
|
||||
// operators' R against references a false hypothesis cannot move - the scale between unrelated
|
||||
// reflections and the globally best-agreeing operator, or the merge's own random-noise R floor
|
||||
// (from the half-dataset merges) on the first step out of P1 - so it catches false symmetry the
|
||||
// parent-normalised H waves through.
|
||||
if (std::isfinite(result.r_added)) {
|
||||
os << " Added-operator R " << FormatDouble(result.r_added, 3);
|
||||
if (std::isfinite(result.r_over_best))
|
||||
os << " = " << FormatDouble(result.r_over_best, 2) << "x the best operator ("
|
||||
<< FormatDouble(result.global_best_operator_r, 3) << ")";
|
||||
if (std::isfinite(result.r_contrast))
|
||||
os << " = contrast " << FormatDouble(result.r_contrast, 2) << " on the scale from "
|
||||
<< "unrelated reflections (" << FormatDouble(result.random_pairing_r, 3)
|
||||
<< ") to the best operator (" << FormatDouble(result.global_best_operator_r, 3)
|
||||
<< "), bound " << FormatDouble(result.r_contrast_bound, 2);
|
||||
if (std::isfinite(result.r_over_floor))
|
||||
os << ", " << FormatDouble(result.r_over_floor, 2) << "x the random-noise floor ("
|
||||
<< FormatDouble(result.merge_r_floor, 3) << ")";
|
||||
|
||||
@@ -410,16 +410,52 @@ struct SearchSpaceGroupOptions {
|
||||
// Two references, each scale-free so the bound means the same on every crystal (the property an
|
||||
// absolute R bound lacks):
|
||||
//
|
||||
// - max_operator_r_over_best: when the data confirm more than one operator, the added operators'
|
||||
// mean R over the GLOBALLY best-agreeing operator (the smallest r_stat anywhere). A genuine
|
||||
// operator relates equal intensities, so a small R is itself the signature of a real operator
|
||||
// and the smallest one is the cleanest reference the data hold. On a genuine group EVERY
|
||||
// operator agrees about as well as the best (ratio ~1.0-1.2 measured, cubic 3-folds and a
|
||||
// genuine 6 included); a false operator sits far above it, INCLUDING one whose own group's
|
||||
// members agree among themselves (a pseudo-tetragonal 4 reads ~3.8, because the reference is
|
||||
// the true 2-fold OUTSIDE that group). Measured over the probe crystals: genuine promotions
|
||||
// reach 1.2, the false 2-fold/4-fold/6-fold-holohedry steps 2.2-3.8. 2.0 sits in that gap.
|
||||
double max_operator_r_over_best = 2.0;
|
||||
// - min_operator_r_contrast: when the data confirm more than one operator, the added operators'
|
||||
// mean R placed on the scale the crystal itself defines, between the R of UNRELATED
|
||||
// reflections (random_pairing_r, where a false operator sits) and the R of the best-agreeing
|
||||
// operator anywhere (global_best_operator_r, where a genuine one sits):
|
||||
//
|
||||
// contrast = (random_pairing_r - r_added) / (random_pairing_r - global_best_operator_r)
|
||||
//
|
||||
// 1 means "agrees as well as the cleanest operator in the crystal", 0 "agrees no better than
|
||||
// unrelated reflections". Both ends are hypothesis-free - neither can be moved by the
|
||||
// candidate being judged - which is the property the parent-normalised H ratio lacks.
|
||||
//
|
||||
// This replaced a plain ratio r_added/global_best_operator_r, bound 2.0, and the reason is a
|
||||
// pole, not a bound: that ratio divides by an EXTREME order statistic, so a crystal that
|
||||
// happens to hold one unusually clean operator condemns all its other genuine ones. It is not
|
||||
// rare. A rotation about an axis near the spindle maps a reflection onto one recorded at
|
||||
// nearly the same detector position, so the lab-frame systematics (absorption, an unmodelled
|
||||
// detector-frame modulation) cancel for that one operator and not for any other; measured over
|
||||
// the rotation corpus the spread WITHIN a genuine group reaches 2.0-2.6x, i.e. the whole width
|
||||
// of the old bound. A weak cubic crystal whose near-spindle 3-fold read 0.074 while its ten
|
||||
// other genuine operators read 0.16-0.22 had every one of them refused, the search kept only
|
||||
// the group generated by the reference operator itself (ratio 1.00 by construction), and the
|
||||
// same 222 hypothesis on the same merge read 2.45 under a cubic enumeration and 1.02 under an
|
||||
// orthorhombic one - a statistic that moves with which OTHER operators were enumerated.
|
||||
// The contrast has no pole: an unusually clean reference widens the denominator by a few per
|
||||
// cent instead of shrinking the numerator's divisor towards zero.
|
||||
//
|
||||
// Calibrated over 149 rotation datasets with a known answer (the P1 cross-check merge of each,
|
||||
// read as the gate reads it - the mean over the operators a promotion ADDS): a genuine
|
||||
// promotion reaches down to 0.77 and the worst false one on the corpus reads 0.695. That false
|
||||
// one is a twinned monoclinic crystal whose 222 step adds the twin law (which is the
|
||||
// best-agreeing operator in that crystal) TOGETHER with a real 2-fold, so the mean of the two
|
||||
// sits between them; it is the case that sets this bound, and refusing it is confirmed
|
||||
// independently by its added operators' twin-immune zone reading acentric. 0.72 sits in that
|
||||
// gap, with ~0.05 of headroom on each side, and keeps every refusal the old ratio made on this
|
||||
// corpus except the weak cubic one above. Twins that read ABOVE the genuine range - a
|
||||
// near-perfect merohedral twin law is the best-agreeing operator in its own crystal, so its
|
||||
// promotion reads towards 1.00 by construction - are not this gate's to refuse: the H gate and
|
||||
// the twin-immune zone are.
|
||||
double min_operator_r_contrast = 0.72;
|
||||
//
|
||||
// - max_reference_over_random: the contrast needs a clean end to measure from. Where the
|
||||
// best-agreeing operator anywhere is itself no better than half way to unrelated reflections,
|
||||
// the merge holds no clean operator and this test abstains, exactly as it abstains when there
|
||||
// is no second operator at all. Over the same 149 datasets every macromolecular merge sits at
|
||||
// 0.05-0.33 and only two small-molecule ones (0.61, 0.81) trip this.
|
||||
double max_reference_over_random = 0.5;
|
||||
//
|
||||
// - max_operator_r_over_floor: the first step out of P1 confirms only the one operator, so there
|
||||
// is no other to be the best - fall back to the merge's own random-noise R floor (merge_r_floor,
|
||||
@@ -595,8 +631,9 @@ struct SearchSpaceGroupOptions {
|
||||
// only the winner and the single highest refusal.
|
||||
//
|
||||
// Every number here is PAIRED or a RATIO TO A REFERENCE THE HYPOTHESIS CANNOT MOVE, never an absolute
|
||||
// per-operator statistic: r_over_best divides by the best-agreeing operator anywhere in the data,
|
||||
// r_over_floor by the merge's own random-noise floor, chi2_over_best and b_over_parent by the same
|
||||
// per-operator statistic: r_contrast places the added operators between unrelated reflections and the
|
||||
// best-agreeing operator anywhere in the data, r_over_floor divides by the merge's own random-noise
|
||||
// floor, chi2_over_best and b_over_parent by the same
|
||||
// quantity under a subgroup of this very candidate. An absolute score would be the joint-likelihood
|
||||
// design that was already refuted; a ratio to a clean within-crystal reference is the form thread1_A
|
||||
// measured to separate genuine operators (1.06-1.15x) from false ones (2.4-4.66x).
|
||||
@@ -613,6 +650,7 @@ struct PointGroupLedgerEntry {
|
||||
double h_ratio = std::numeric_limits<double>::quiet_NaN();
|
||||
double r_added = std::numeric_limits<double>::quiet_NaN(); // added operators' mean intensity-weighted R
|
||||
double r_over_best = std::numeric_limits<double>::quiet_NaN(); // ... over the best-agreeing operator anywhere
|
||||
double r_contrast = std::numeric_limits<double>::quiet_NaN(); // ... 0 = unrelated reflections, 1 = that best operator
|
||||
double r_over_floor = std::numeric_limits<double>::quiet_NaN(); // ... over the merge's random-noise R floor
|
||||
bool adopted = false; // this is the group the search took
|
||||
bool eligible = false; // it passed every consistency test (so it COULD have been taken)
|
||||
@@ -684,14 +722,21 @@ struct SearchSpaceGroupResult {
|
||||
};
|
||||
std::vector<RowPseudoTranslation> pseudo_translations;
|
||||
// The intensity-weighted operator R of the promotion that was ADOPTED (mean over the operators it
|
||||
// adds), that R over merge_r_floor, and that R over the globally best-agreeing operator - the
|
||||
// numbers the merge-degradation gate acts on (best-operator where two or more operators are
|
||||
// confirmed, floor on the first step out of P1). Reported so a run shows the margin the gate had.
|
||||
// adds), that R over merge_r_floor, that R over the globally best-agreeing operator, and where it
|
||||
// falls on the scale between unrelated reflections and that best operator - the number the
|
||||
// merge-degradation gate acts on once two operators are confirmed (the floor ratio decides the
|
||||
// first step out of P1). Reported so a run shows the margin the gate had.
|
||||
double r_added = std::numeric_limits<double>::quiet_NaN();
|
||||
double r_over_floor = std::numeric_limits<double>::quiet_NaN();
|
||||
double r_over_best = std::numeric_limits<double>::quiet_NaN();
|
||||
double r_contrast = std::numeric_limits<double>::quiet_NaN();
|
||||
double global_best_operator_r = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
// The intensity-weighted R of UNRELATED reflections on this merge - shell-matched pairs of
|
||||
// reflections no symmetry relates. The far end of the contrast scale above: where a false
|
||||
// operator's R sits. NaN when the merge carried too few present reflections to form it.
|
||||
double random_pairing_r = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
// A HIGHER point group whose operators the intensities confirmed (Stage A) but whose promotion the
|
||||
// consistency tests refused, with the reason. Processing continues in the lower group, which is the
|
||||
// safe direction: merging a twinned crystal in the twin's holohedry averages non-equivalent
|
||||
@@ -718,9 +763,9 @@ struct SearchSpaceGroupResult {
|
||||
std::vector<PointGroupLedgerEntry> point_group_ledger;
|
||||
// The refusal bounds the ledger's own channels were judged against, copied from the options so the
|
||||
// report can print the MARGIN the adopted row had rather than only its value. The accept-side
|
||||
// headroom is thin (measured: the largest genuine adopted r_over_best on a 63-dataset corpus is
|
||||
// 1.90 against a bound of 2.00), and a margin is the only form in which that is visible per run.
|
||||
double r_over_best_bound = 0.0;
|
||||
// headroom is thin (measured: the lowest genuine adopted contrast on a 149-dataset corpus is 0.70
|
||||
// against a bound of 0.65), and a margin is the only form in which that is visible per run.
|
||||
double r_contrast_bound = 0.0;
|
||||
double r_over_floor_bound = 0.0;
|
||||
|
||||
// An axial row whose SCREW these data could not decide: the selected group and at least one of
|
||||
|
||||
@@ -600,11 +600,11 @@ ReportDocument BuildReportDocument(const std::string &output_prefix,
|
||||
search.h_ratio, search.h_ratio_bound);
|
||||
// The best-operator R gate is the one that applies once two operators are confirmed, i.e.
|
||||
// above order 2 (the first step out of P1 is judged on the noise floor instead).
|
||||
if (search.point_group_order > 2 && std::isfinite(search.r_over_best)
|
||||
&& search.r_over_best_bound > 0.0 && search.r_over_best > search.r_over_best_bound)
|
||||
line += fmt::format(";\n added-operator R {:.2f}x the best operator EXCEEDS its bound of {:.2f}x - "
|
||||
"that gate fired and was overridden", search.r_over_best,
|
||||
search.r_over_best_bound);
|
||||
if (search.point_group_order > 2 && std::isfinite(search.r_contrast)
|
||||
&& search.r_contrast_bound > 0.0 && search.r_contrast < search.r_contrast_bound)
|
||||
line += fmt::format(";\n added-operator R contrast {:.2f} is BELOW its bound of {:.2f} - "
|
||||
"that gate fired and was overridden", search.r_contrast,
|
||||
search.r_contrast_bound);
|
||||
line += ".\n The space group follows from the systematic absences.";
|
||||
if (!alts.empty())
|
||||
line += fmt::format("\n These data cannot separate it from {} - both are consistent with"
|
||||
|
||||
Reference in new issue
Block a user