Space-group search: record what the E^2 shell count does on a small merge

The shell count is order.size() / 100 in integer arithmetic, so a merge of
fewer than two hundred reflections gets a single shell and E^2 becomes I
divided by one global mean.

That is harmless for the thing E^2 was introduced for - the operator
correlation is a Pearson coefficient and is invariant to a common scale,
so it lands on exactly the value raw I would have given. It is not
harmless for the overlap cap, which then rejects reflections by their
intensity against the whole merge rather than against their own
resolution shell, and so cuts the low-resolution end and spares the high.

No behaviour change; a merge that small has already failed for other
reasons. This is here so the next reader does not have to rediscover that
the normalisation quietly switches off at the bottom of the range.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CHMmeM1d489zvNFT7ZMN2P
This commit is contained in:
2026-08-26 10:29:58 +02:00
co-authored by Claude Opus 5
parent 00c27584cb
commit d987bd6178
@@ -289,6 +289,11 @@ SearchSpaceGroupResult SearchSpaceGroup(
order.push_back(i);
std::sort(order.begin(), order.end(),
[&](size_t a, size_t b) { return merged[a].d > merged[b].d; }); // low res -> high res
// A hundred reflections a shell, at most twenty-five shells - and integer division, so
// below two hundred there is a single shell and E^2 is I over one global mean. That
// leaves the operator correlation exactly where it was, Pearson being invariant to a
// common scale, but not the E^2 overlap cap below: it then cuts on I against the whole
// merge instead of against each reflection's own shell.
const int bins = std::clamp(static_cast<int>(order.size() / 100), 1, 25);
const size_t per = (order.size() + bins - 1) / std::max(1, bins);
for (size_t b = 0; b * per < order.size(); ++b) {