Stills merge: R_split and a frame-split CC1/2, per shell and overall
Serial crystallography quotes R_split and a CC1/2 whose half-sets keep each crystal whole (CrystFEL's partialator), and rugnux's stills report had neither, so its numbers could not be set beside CrystFEL's. MergeOnTheFly now also sums every frame's observations into the frame's own half (the existing per-image hash preference), and MergeStats reports from those two means R_split = (1/sqrt 2) sum|I1-I2| / (1/2 sum(I1+I2)) and their CC1/2. They are printed as a "Serial statistics" table under the shell table (also in the --report-resolution table) and as R_SPLIT / CC_HALF_SPLIT and their REFRES_ keys. The balanced per-reflection split behind CC_HALF is unchanged, and so is everything decided on it (resolution cut, space-group search, pass gates). Rotation output is unchanged: RotationScaleMerge does not fill the new fields and the table is not printed. On a 4000-frame jet subset over 30-1.5 A: CC1/2 0.830, R_split 31.8%, against 0.834 / 31.3% computed by hand from two separate even/odd runs and 0.842 / 30.5% from CrystFEL on the same frames. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2a45eeb831
commit
6a1d859d49
6 files changed
+142
No files matched your search
@@ -863,3 +863,54 @@ TEST_CASE("SublatticeRelabel_SignalPicksTheAxisThatWasDoubled") {
|
||||
CHECK((ScatteringVector(outcomes[0].latt, r) - ScatteringVector(before.latt, b)).Length() < 1e-5f);
|
||||
}
|
||||
}
|
||||
|
||||
// R_split's half-sets keep every frame whole: each frame measures the same reflections at its own
|
||||
// intensity, so every reflection's two half-set means are the same two numbers. R_split is then
|
||||
// (1/sqrt 2) |I_1 - I_2| / ((I_1 + I_2) / 2) in every shell, and the frame-split CC1/2 is undefined
|
||||
// (the half-set means do not vary) - the per-reflection balanced split, which the processing decides on,
|
||||
// is left as it was.
|
||||
TEST_CASE("MergeOnTheFly_RsplitKeepsEveryFrameWhole") {
|
||||
DiffractionExperiment x;
|
||||
x.SetSpaceGroup(TestSpaceGroup());
|
||||
|
||||
std::vector<IntegrationOutcome> outcomes(64);
|
||||
for (size_t f = 0; f < outcomes.size(); ++f) {
|
||||
for (int h = 1; h <= 4; ++h)
|
||||
for (int k = 1; k <= 4; ++k) {
|
||||
Reflection r{};
|
||||
r.h = h;
|
||||
r.k = k;
|
||||
r.l = 1;
|
||||
r.d = 20.0f / std::sqrt(static_cast<float>(h * h + k * k + 1));
|
||||
r.I = 100.0f + static_cast<float>(f);
|
||||
r.sigma = 10.0f;
|
||||
r.prescaling_corr = 1.0f;
|
||||
r.partiality = 1.0f;
|
||||
r.image_scale_corr = 1.0f;
|
||||
outcomes[f].reflections.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
MergeOnTheFly merge(x);
|
||||
merge.AddImages(outcomes);
|
||||
const auto merged = merge.ExportReflections();
|
||||
REQUIRE(merged.size() == 16);
|
||||
|
||||
const float a = merged[0].I_split[0], b = merged[0].I_split[1];
|
||||
REQUIRE(std::isfinite(a));
|
||||
REQUIRE(std::isfinite(b));
|
||||
CHECK(a != b);
|
||||
for (const auto &m: merged) {
|
||||
CHECK(m.I_split[0] == a);
|
||||
CHECK(m.I_split[1] == b);
|
||||
}
|
||||
|
||||
const auto stats = merge.MergeStats(merged, outcomes);
|
||||
const double expected = std::fabs(a - b) / (0.5 * (a + b)) / std::sqrt(2.0);
|
||||
CHECK(stats.overall.n_split_pairs == 16);
|
||||
CHECK(stats.overall.r_split == Catch::Approx(expected).epsilon(1e-5));
|
||||
for (const auto &sh: stats.shells)
|
||||
if (sh.n_split_pairs > 0)
|
||||
CHECK(sh.r_split == Catch::Approx(expected).epsilon(1e-5));
|
||||
CHECK_FALSE(SerialShellTable(stats).empty());
|
||||
}
|
||||
Reference in new issue
Block a user