Report: FITTED_RESOLUTION is the fit on the standard CC1/2; the weighted fit the cut uses is FITTED_RESOLUTION_INTERNAL
The number to quote is read off the CC1/2 the shell table prints (rotation: the plain fit) and judged against those shells; the cut stays on the internal weighted fit, reported as FITTED_RESOLUTION_INTERNAL. FITTED_RESOLUTION_PLAIN is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HVeDQqrcrEZy6XBUCMZvyT
This commit is contained in:
1 parent
e3d7341ea0
commit
29108f4421
5 files changed
+51
-39
No files matched your search
@@ -66,7 +66,8 @@ each half is averaged with 1/sigma^2 weights, and CC1/2 is the plain Pearson cor
|
||||
half-set means, every reflection counted alike. The same holds for `REFRES_CC_HALF=`, the CC1/2 and CC\*
|
||||
columns of every shell table and the mmCIF. The automatic resolution cut is fitted to a different,
|
||||
internal form of CC1/2, in which a reflection measured only on frames the crystal barely diffracted on
|
||||
counts less (see `FITTED_RESOLUTION`); on a sweep without a weak stretch the two agree.
|
||||
counts less (`FITTED_RESOLUTION_INTERNAL`); `FITTED_RESOLUTION` is read off the standard one. On a sweep
|
||||
without a weak stretch the two agree.
|
||||
|
||||
**`R_MERGE=`** and **`R_PIM=`** are the other two merging R-factors over the same observations as
|
||||
`R_MEAS=`: R_merge without the multiplicity correction, and the precision-indicating R_pim (Weiss,
|
||||
@@ -246,8 +247,9 @@ The keys most scripts read, by section; the rest of this page explains the ones
|
||||
| `MOSAICITY_DEG` | Rotation only ([below](#mosaicity)) |
|
||||
| `MERGE` | Section 3: `PERFORMED` or `NOT_PERFORMED` (`--no-merge`) |
|
||||
| `INCLUDE_RESOLUTION_RANGE` | `dmax dmin` of the written reflections (Å), as XDS's keyword |
|
||||
| `FITTED_RESOLUTION` | Where the CC1/2 fall-off crosses the `--resolution-cc-target`; absent where the curve is not a fall-off (a `RESOLUTION_FIT` warning says why). It is read off the automatic cut's own fit, which weights each reflection by the scale of the frames it was measured on so that a weak stretch of the sweep does not drag it in; on such a sweep the unweighted `CC_HALF` of the shell table can cross the target earlier |
|
||||
| `FITTED_RESOLUTION_PLAIN`, `RESOLUTION_CUT_PLAIN` | Diagnostic, rotation only: the same automatic fit made on the unweighted CC1/2 of the shell table - its crossing, and the limit the cut would have written from it. Nothing is cut at them; they show what the weighting of the fit is worth on this run |
|
||||
| `FITTED_RESOLUTION` | Where the fall-off of the standard CC1/2 (the `CC_HALF` of the shell table) crosses the `--resolution-cc-target` - the number to quote; absent where the curve is not a fall-off (a `RESOLUTION_FIT` warning says why) |
|
||||
| `FITTED_RESOLUTION_INTERNAL` | Rotation only, internal: the crossing of the automatic cut's own fit, which weights each reflection by the scale of the frames it was measured on so that a weak stretch of the sweep does not drag it in. The written range (`INCLUDE_RESOLUTION_RANGE`) is cut from this one; on a sweep without a weak stretch it equals `FITTED_RESOLUTION` |
|
||||
| `RESOLUTION_CUT_PLAIN` | Diagnostic, rotation only: the limit the cut would have written from the fit on the standard CC1/2. Nothing is cut at it |
|
||||
| `UNIQUE_REFLECTIONS`, `TOTAL_OBSERVATIONS`, `COMPLETENESS`, `MULTIPLICITY` | Over the written range; completeness in percent |
|
||||
| `I_OVER_SIGMA`, `R_MEAS`, `CC_HALF`, `WILSON_B` | The overall merging statistics; the shell table beside them is in the comment lines. `R_MEAS` (and `R_MERGE`, `R_PIM`) rotation only, `R_SPLIT` stills only |
|
||||
| `SIGANO`, `CC_ANOM`, `FRIEDELS_LAW` | The anomalous signal, and whether Friedel pairs were merged (`FALSE` with `-A`) |
|
||||
|
||||
@@ -50,7 +50,7 @@ std::vector<double> CCHalfFrameFactors(const std::vector<double> &frame_scale,
|
||||
const std::vector<int64_t> &n_obs);
|
||||
|
||||
// weighted: each reflection's pair enters with its cc_weight, the internal form the cut is decided on;
|
||||
// false fits the plain (reported) CC1/2 instead - a diagnostic only (FITTED_RESOLUTION_PLAIN).
|
||||
// false fits the plain (reported) CC1/2 instead - the quoted FITTED_RESOLUTION; not applied as the cut.
|
||||
ResolutionCutoffResult ComputeCCHalfLogisticCutoff(const std::vector<MergedReflection> &merged,
|
||||
double cc_target, Logger &logger, bool weighted = true);
|
||||
|
||||
|
||||
+33
-25
@@ -408,16 +408,19 @@ namespace {
|
||||
Add(s, KeyReal((prefix + "CC_HALF").c_str(), po.cc_half, "{:.4f}"));
|
||||
}
|
||||
|
||||
// The three helpers below judge the automatic cut's fit against the shells, so they read the curve the
|
||||
// fit was made on - the internal weighted CC1/2 (cc_half_internal) - not the plain one the shell table
|
||||
// prints. On a sweep without a weak stretch the two are the same number.
|
||||
// The three helpers below judge the quoted fit against the shells, so they read the curve the fit was
|
||||
// made on: `plain` the standard CC1/2 the shell table prints (cc_half), else the internal weighted one
|
||||
// (cc_half_internal). On a sweep without a weak stretch the two are the same number.
|
||||
double ShellCC(const MergeStatisticsShell &sh, bool plain) {
|
||||
return plain ? sh.cc_half : sh.cc_half_internal;
|
||||
}
|
||||
|
||||
// The finest shell whose CC1/2 still reaches the cut's own target: where the table says the signal
|
||||
// is, for the completeness quoted beside the written range.
|
||||
double FinestSupportedShell(const MergeStatistics &ms, double target) {
|
||||
double FinestSupportedShell(const MergeStatistics &ms, double target, bool plain) {
|
||||
double finest = 0.0;
|
||||
for (const auto &sh : ms.shells)
|
||||
if (std::isfinite(sh.cc_half_internal) && sh.cc_half_internal >= target && (finest == 0.0 || sh.d_min < finest))
|
||||
if (std::isfinite(ShellCC(sh, plain)) && ShellCC(sh, plain) >= target && (finest == 0.0 || sh.d_min < finest))
|
||||
finest = sh.d_min;
|
||||
return finest; // 0.0 = no shell reaches the target at all
|
||||
}
|
||||
@@ -429,12 +432,12 @@ namespace {
|
||||
// shell ABOVE the target instead withheld the number on 68 of 100 battery datasets whose fit sat
|
||||
// 0.01-0.02 A inside the first sub-target shell. 0.0 when no shell reaches the target; the finest
|
||||
// shell when none falls below it.
|
||||
double CrossingShellLimit(const MergeStatistics &ms, double target) {
|
||||
double CrossingShellLimit(const MergeStatistics &ms, double target, bool plain) {
|
||||
double finest = 0.0;
|
||||
for (const auto &sh : ms.shells) {
|
||||
if (!std::isfinite(sh.cc_half_internal))
|
||||
if (!std::isfinite(ShellCC(sh, plain)))
|
||||
continue;
|
||||
if (sh.cc_half_internal < target)
|
||||
if (ShellCC(sh, plain) < target)
|
||||
return finest == 0.0 ? 0.0 : sh.d_min;
|
||||
finest = sh.d_min;
|
||||
}
|
||||
@@ -447,12 +450,12 @@ namespace {
|
||||
// resolution can be read off it. Returns that shell's d_min, 0.0 when the curve never climbs
|
||||
// back. A wobble that stays ABOVE the target is not this - a fall-off with a noisy shell in it
|
||||
// is still a fall-off.
|
||||
double CCHalfClimbsBackAboveTarget(const MergeStatistics &ms, double target) {
|
||||
double CCHalfClimbsBackAboveTarget(const MergeStatistics &ms, double target, bool plain) {
|
||||
bool below = false;
|
||||
for (const auto &sh : ms.shells) {
|
||||
if (!std::isfinite(sh.cc_half_internal))
|
||||
if (!std::isfinite(ShellCC(sh, plain)))
|
||||
continue;
|
||||
if (sh.cc_half_internal < target)
|
||||
if (ShellCC(sh, plain) < target)
|
||||
below = true;
|
||||
else if (below)
|
||||
return sh.d_min;
|
||||
@@ -1513,9 +1516,14 @@ ReportDocument BuildReportDocument(const std::string &output_prefix,
|
||||
// be quotable when the curve it was read off never behaved like a fall-off. A fit finer
|
||||
// than the finest shell that still reaches the target asserts a resolution the shell
|
||||
// table on this very page refutes.
|
||||
const double finest_supported = FinestSupportedShell(ms, cc_target);
|
||||
const double crossing_limit = CrossingShellLimit(ms, cc_target);
|
||||
const double climbs_back = CCHalfClimbsBackAboveTarget(ms, cc_target);
|
||||
// The number to quote is the fit on the standard CC1/2 the shell table prints (rotation: the
|
||||
// plain fit); the cut applied is made on the internal weighted fit, reported as
|
||||
// FITTED_RESOLUTION_INTERNAL where the two exist. Stills have only the one, already unweighted.
|
||||
const bool plain = result.resolution_fit_plain_A.has_value();
|
||||
const std::optional<double> quoted_fit = plain ? result.resolution_fit_plain_A : result.resolution_fit_A;
|
||||
const double finest_supported = FinestSupportedShell(ms, cc_target, plain);
|
||||
const double crossing_limit = CrossingShellLimit(ms, cc_target, plain);
|
||||
const double climbs_back = CCHalfClimbsBackAboveTarget(ms, cc_target, plain);
|
||||
std::string fit_suppressed;
|
||||
// Only where there are shells to judge the fit against; with no table there is no
|
||||
// evidence either way, and suppressing on no evidence is its own false claim.
|
||||
@@ -1525,26 +1533,26 @@ ReportDocument BuildReportDocument(const std::string &output_prefix,
|
||||
"CC1/2 falls below the cut's target of {:.2f} and then climbs back above it"
|
||||
" at {:.2f} A, so this curve is not a fall-off and no one resolution can be"
|
||||
" read off it", cc_target, climbs_back);
|
||||
else if (result.resolution_fit_A) {
|
||||
else if (quoted_fit) {
|
||||
if (crossing_limit == 0.0)
|
||||
fit_suppressed = fmt::format(
|
||||
"CC1/2 never reaches the cut's target of {:.2f} in any shell, so there is"
|
||||
" no fall-off to fit", cc_target);
|
||||
else if (*result.resolution_fit_A < crossing_limit - 1e-6)
|
||||
else if (*quoted_fit < crossing_limit - 1e-6)
|
||||
fit_suppressed = fmt::format(
|
||||
"the fit reads {:.2f} A, finer than the shell where CC1/2 falls below"
|
||||
" {:.2f} ({:.2f} A), so the curve it was read off is not a fall-off",
|
||||
*result.resolution_fit_A, cc_target, crossing_limit);
|
||||
*quoted_fit, cc_target, crossing_limit);
|
||||
else
|
||||
fitted_resolution = *result.resolution_fit_A;
|
||||
fitted_resolution = *quoted_fit;
|
||||
}
|
||||
}
|
||||
if (fitted_resolution)
|
||||
Add(s, KeyReal("FITTED_RESOLUTION", *fitted_resolution, "{:.2f}"));
|
||||
// Diagnostic: the same automatic fit on the plain CC1/2 (rotation), its crossing and the limit
|
||||
// it would have written. Not applied; printed as fitted, without the suppression above.
|
||||
if (result.resolution_fit_plain_A)
|
||||
Add(s, KeyReal("FITTED_RESOLUTION_PLAIN", *result.resolution_fit_plain_A, "{:.2f}"));
|
||||
// Internal: the fit the applied cut was made on (the weighted CC1/2), printed as fitted, without
|
||||
// the suppression above; and the limit the plain fit would have written.
|
||||
if (plain && result.resolution_fit_A)
|
||||
Add(s, KeyReal("FITTED_RESOLUTION_INTERNAL", *result.resolution_fit_A, "{:.2f}"));
|
||||
if (result.resolution_cut_plain_A)
|
||||
Add(s, KeyReal("RESOLUTION_CUT_PLAIN", *result.resolution_cut_plain_A, "{:.2f}"));
|
||||
|
||||
@@ -1688,9 +1696,9 @@ ReportDocument BuildReportDocument(const std::string &output_prefix,
|
||||
" FITTED_RESOLUTION\n is where the CC1/2 fall-off crosses {:.2f}, and is the number"
|
||||
" to quote. The data are kept\n one shell past it on purpose: an included shell can"
|
||||
" still be downweighted by refinement,\n while a truncated one cannot be put back."
|
||||
" The fit weights each reflection by the scale of the\n frames it was measured on,"
|
||||
" so a weak stretch of the sweep does not drag it in; the CC1/2 in\n the shell table"
|
||||
" is the standard unweighted one, and can read lower where there is such a stretch.",
|
||||
" It is read off the standard CC1/2 of the shell table; the\n cut itself weights"
|
||||
" each reflection by the scale of the frames it was measured on, so a weak\n stretch"
|
||||
" of the sweep does not drag it in (FITTED_RESOLUTION_INTERNAL).",
|
||||
cc_target)));
|
||||
if (completeness_narrower && o.possible_unique_reflections > 0)
|
||||
Add(s, Prose(fmt::format(
|
||||
|
||||
@@ -396,7 +396,7 @@ TEST_CASE("ResolutionCutoff_ScaledUpNoiseDoesNotHideTheFallOff") {
|
||||
CHECK(*rc.d_fit == Catch::Approx(2.0).margin(0.15));
|
||||
|
||||
// Counted as equals, the same reflections hide it: the curve reads noise from the first bin on.
|
||||
// The plain fit (the FITTED_RESOLUTION_PLAIN diagnostic) is that, with the weights left in place.
|
||||
// The plain fit (the quoted FITTED_RESOLUTION) is that, with the weights left in place.
|
||||
const auto plain = ComputeCCHalfLogisticCutoff(merged, 0.30, logger, /*weighted=*/false);
|
||||
for (auto &m : merged) m.cc_weight = 1.0f;
|
||||
const auto unweighted = ComputeCCHalfLogisticCutoff(merged, 0.30, logger);
|
||||
|
||||
@@ -792,18 +792,20 @@ TEST_CASE("ResultReport_FittedResolutionSuppressed", "[Diagnostics]") {
|
||||
!= std::string::npos);
|
||||
result.resolution_fit_A = 0.95;
|
||||
|
||||
// The fit is judged against the curve it was made on, the internal weighted CC1/2, not the plain one
|
||||
// the table prints: a weak stretch can pull the plain shell CC1/2 under the target where the
|
||||
// weighted one, and the fit, stay above it.
|
||||
// Without a plain fit (stills) the one fit is quoted, judged against the internal CC1/2 it was made
|
||||
// on: a weak stretch can pull the plain shell CC1/2 under the target where the weighted one stays.
|
||||
ProcessResult weak = result;
|
||||
weak.merge_statistics.shells.front().cc_half = 0.25;
|
||||
CHECK(RenderResultReport("p", "in.h5", x, weak).find("\nFITTED_RESOLUTION= 0.95\n") != std::string::npos);
|
||||
// The fit on the plain CC1/2 is a diagnostic beside it, printed as fitted.
|
||||
weak.resolution_fit_plain_A = 1.10;
|
||||
weak.resolution_cut_plain_A = 1.02;
|
||||
const auto weak_text = RenderResultReport("p", "in.h5", x, weak);
|
||||
CHECK(weak_text.find("\nFITTED_RESOLUTION_PLAIN= 1.10\n") != std::string::npos);
|
||||
CHECK(weak_text.find("\nRESOLUTION_CUT_PLAIN= 1.02\n") != std::string::npos);
|
||||
// With a plain fit (rotation) that is the number quoted, and the weighted fit the cut was made on is
|
||||
// reported as internal, printed as fitted.
|
||||
ProcessResult two = result;
|
||||
two.resolution_fit_plain_A = 1.10;
|
||||
two.resolution_cut_plain_A = 1.02;
|
||||
const auto two_text = RenderResultReport("p", "in.h5", x, two);
|
||||
CHECK(two_text.find("\nFITTED_RESOLUTION= 1.10\n") != std::string::npos);
|
||||
CHECK(two_text.find("\nFITTED_RESOLUTION_INTERNAL= 0.95\n") != std::string::npos);
|
||||
CHECK(two_text.find("\nRESOLUTION_CUT_PLAIN= 1.02\n") != std::string::npos);
|
||||
|
||||
// CC1/2 that drops through the target and climbs back above it is not a fall-off either, and the
|
||||
// finest shell reaching the target is then noise correlating with noise - so a fit the test above
|
||||
|
||||
Reference in new issue
Block a user