Carry the twin-immune zone verdict as a value, not as report text

The report decided whether to name a twin law by substring-matching
ProcessResult::twin_zone_decision, a field every zone read overwrites.
A run reads the zone up to four times, so the match saw the last
comparison made rather than the refusal that stood, and TWIN_LAW and the
_pdbx_reflns_twin mmCIF loop could appear on a crystal whose refusal was
overturned - or be missing on one whose refusal stood.

TwinZoneVerdict already returns the verdict; it now records it on the
result, and the decision point reads that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nW6FNRP1bBJJ8pfHiByAT
This commit is contained in:
leonarski_fandClaude Opus 5 committed 2026-09-20 18:58:09 +02:00
1 parent e61454a27b
commit a6c21bd84a
2 files changed
+12 -1

No files matched your search

+6 -1
View File
@@ -1785,6 +1785,11 @@ namespace {
z.tncs_normalised ? ", normalised per pseudo-translation class" : "",
verdict < 0 ? "acentric, the added operators are a twin law or a pseudo-symmetry"
: verdict > 0 ? "centric, the added operators are real" : "undecided");
// The verdict as a VALUE, for the report to act on. Every caller acts on a decisive read -
// the refusal stands or it is overturned - so recording it here records the decision, where
// matching on the text records only the last comparison the run happened to make.
if (verdict != 0)
result.twin_zone_refusal_stood = verdict < 0;
return verdict;
}
@@ -6924,7 +6929,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
// refused group adds, at the fraction its own disagreement implies (a lower bound).
if (twin_sg && result.space_group_search
&& result.space_group_search->refused_point_group_representative
&& result.twin_zone_decision.find("acentric, the added") != std::string::npos) {
&& result.twin_zone_refusal_stood) {
const auto &sgs = *result.space_group_search;
const gemmi::GroupOps hi = sgs.refused_point_group_representative->operations();
const gemmi::GroupOps lo = twin_sg->operations();
+6
View File
@@ -364,6 +364,12 @@ struct ProcessResult {
// The twin-immune zone read for a promotion a twin gate refused (the H ratio or the added-operator
// R), and what was decided on it. Empty when no twin gate fired.
std::string twin_zone_decision;
// What that zone DECIDED, carried as a value beside the text: true where the refusal STOOD (the
// added operators read acentric and are recorded as a twin law), false where the zone overturned
// the refusal or decided nothing. A run reads the zone up to four times and each read rewrites
// the text, so the decision cannot be recovered from the text - matching on it named the twin law
// of whichever comparison happened to be made last rather than of the refusal that stood.
bool twin_zone_refusal_stood = false;
// Per lattice twin law outside the adopted group: the operator's own H, the fraction it implies
// and its correlation, on the final merge. Report-only.
std::vector<std::string> twin_law_evidence;