merged MTZ: FreeR_flag in the convention its own column name carries

The column was written with 1 for the test set and 0 for the working set - the
phenix/CNS numbering - under FreeR_flag, which is CCP4's column name. CCP4's own
freerflag writes 0..19 with 0 as the test bin, and REFMAC5's default FREE 0
reads it that way, so REFMAC5 stopped on every file we wrote: "more than half of
reflections are in free R set", then "Cannot switch free R flag", exit 1.
phenix auto-detects either numbering with equal confidence (measured on both,
score 3 each), and rugnux's own reference-MTZ reader already takes flag 0 as the
test set, so 0 = test is the numbering that works everywhere and the one the
rest of the code assumes.

This changes every merged .mtz we write: a script that reads FreeR_flag == 1 as
the test set has to be inverted. The mmCIF's _refln.status_free is a separate
item with its own convention and is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
This commit is contained in:
2026-09-02 09:18:22 +02:00
co-authored by Claude Opus 5
parent 762c093b10
commit 47ec939c39
2 changed files with 12 additions and 4 deletions
+11 -4
View File
@@ -74,7 +74,14 @@ struct MergedOutRow {
int h = 0, k = 0, l = 0;
float Imean = NAN, sImean = NAN, Ip = NAN, sIp = NAN, Im = NAN, sIm = NAN;
float Fmean = NAN, sFmean = NAN, Fp = NAN, sFp = NAN, Fm = NAN, sFm = NAN;
int rfree = 0;
// The value written into FreeR_flag: 0 for the TEST set, 1 for the working set. That is the CCP4
// convention the column NAME belongs to - CCP4's own freerflag writes 0..19 with 0 as the ~5%
// test bin, and REFMAC's default FREE 0 reads it that way, so the opposite (phenix/CNS) numbering
// under this name stops REFMAC dead: "more than half of reflections are in free R set", then
// "Cannot switch free R flag", exit 1. phenix auto-detects either numbering with equal confidence
// (measured on both), and rugnux's own reference-MTZ reader already takes 0 as the test set, so
// this is the numbering that works everywhere and the one the rest of the code assumes.
int rfree = 1;
};
std::vector<MergedOutRow> BuildMergedRows(const std::vector<MergedReflection> &reflections,
@@ -95,7 +102,7 @@ std::vector<MergedOutRow> BuildMergedRows(const std::vector<MergedReflection> &r
for (const auto& r : reflections)
out_rows.push_back({r.h, r.k, r.l, r.I, r.sigma, r.I_plus, r.sigma_plus, r.I_minus,
r.sigma_minus, r.F, r.sigmaF, r.F_plus, r.sigmaF_plus, r.F_minus,
r.sigmaF_minus, r.rfree_flag ? 1 : 0});
r.sigmaF_minus, r.rfree_flag ? 0 : 1});
} else {
// Anomalous: group the two mates by their (shared) Friedel-merged ASU representative, then form
// IMEAN / F as their inverse-variance Friedel mean. A single generator gives both the group key
@@ -105,14 +112,14 @@ std::vector<MergedOutRow> BuildMergedRows(const std::vector<MergedReflection> &r
int h = 0, k = 0, l = 0;
float Ip = NAN, sIp = NAN, Im = NAN, sIm = NAN;
float Fp = NAN, sFp = NAN, Fm = NAN, sFm = NAN;
int rfree = 0;
int rfree = 1;
};
std::map<std::tuple<int, int, int>, AnomRow> rows;
for (const auto& r : reflections) {
const HKLKey key = key_gen(r);
AnomRow& row = rows[{key.h, key.k, key.l}];
row.h = key.h; row.k = key.k; row.l = key.l;
row.rfree = r.rfree_flag ? 1 : 0;
row.rfree = r.rfree_flag ? 0 : 1;
if (key.plus) { row.Ip = r.I; row.sIp = r.sigma; row.Fp = r.F; row.sFp = r.sigmaF; }
else { row.Im = r.I; row.sIm = r.sigma; row.Fm = r.F; row.sFm = r.sigmaF; }
}