rugnux: write I(+)/I(-) and F(+)/F(-) by default (rotation)
A rotation merge now keeps each acentric reflection's Bijvoet split - the inverse-variance I(+)/I(-) from the same scaled fulls - even when the merge is Friedel-averaged (the default, no -A). IMEAN stays the primary intensity and is bit-identical to before; the split is purely additive and scaled non-anomalously, so a weak anomalous signal is preserved in the output without having to reprocess with -A. French-Wilson now also fills F(+)/F(-) from the two hands (one pass, shared Wilson prior). Writers: the default (Friedel-merged) MTZ and mmCIF now carry IMEAN plus I(+)/I(-) and F/F(+)/F(-) whenever a reflection has an anomalous split (the stills path, which computes none, keeps the previous columns). A missing mate or a centric is emitted as the CCP4 missing-value flag (NaN). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "scale_merge/HKLKey.h"
|
||||
#include "scale_merge/TwinningAnalysis.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
@@ -188,8 +189,16 @@ void WriteMmcifReflections(const std::vector<MergedReflection> &reflections,
|
||||
out << "_refln.index_l\n";
|
||||
out << "_refln.intensity_meas\n";
|
||||
out << "_refln.intensity_sigma\n";
|
||||
out << "_refln.pdbx_I_plus\n";
|
||||
out << "_refln.pdbx_I_plus_sigma\n";
|
||||
out << "_refln.pdbx_I_minus\n";
|
||||
out << "_refln.pdbx_I_minus_sigma\n";
|
||||
out << "_refln.F_meas_au\n";
|
||||
out << "_refln.F_meas_sigma_au\n";
|
||||
out << "_refln.pdbx_F_plus\n";
|
||||
out << "_refln.pdbx_F_plus_sigma\n";
|
||||
out << "_refln.pdbx_F_minus\n";
|
||||
out << "_refln.pdbx_F_minus_sigma\n";
|
||||
out << "_refln.status_free\n";
|
||||
out << "_refln.status\n";
|
||||
|
||||
@@ -199,8 +208,16 @@ void WriteMmcifReflections(const std::vector<MergedReflection> &reflections,
|
||||
<< std::setw(5) << r.l << " "
|
||||
<< std::setw(14) << Fmt(r.I, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.sigma, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.I_plus, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.sigma_plus, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.I_minus, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.sigma_minus, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.F, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.sigmaF, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.F_plus, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.sigmaF_plus, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.F_minus, 4) << " "
|
||||
<< std::setw(14) << Fmt(r.sigmaF_minus, 4) << " "
|
||||
<< (r.rfree_flag ? 1 : 0) << " "
|
||||
<< "o" // 'o' = observed
|
||||
<< "\n";
|
||||
@@ -235,25 +252,56 @@ void WriteMtzReflections(const std::vector<MergedReflection> &reflections,
|
||||
// reflection with the standard CCP4 anomalous layout (IMEAN + I(+)/I(-), and the same split for
|
||||
// the French-Wilson amplitude), which aimless / ctruncate / mtz2sca / ANODE read directly.
|
||||
if (experiment.GetScalingSettings().GetMergeFriedel()) {
|
||||
// Friedel-merged output: IMEAN is the primary intensity. I(+)/I(-) are written by default too
|
||||
// when the merge kept a Bijvoet split (rotation always does; the split is scaled non-anomalously),
|
||||
// so a weak anomalous signal is preserved without reprocessing. A reflection with only one mate,
|
||||
// or a centric, gets a missing value (NaN) for the absent hand. When no reflection has an
|
||||
// anomalous split (e.g. the stills path, which does not compute one) the columns are omitted.
|
||||
const bool has_anom = std::any_of(reflections.begin(), reflections.end(),
|
||||
[](const MergedReflection& r){ return std::isfinite(r.I_plus) || std::isfinite(r.I_minus); });
|
||||
mtz.add_column("H", 'H', dataset_id, -1, false);
|
||||
mtz.add_column("K", 'H', dataset_id, -1, false);
|
||||
mtz.add_column("L", 'H', dataset_id, -1, false);
|
||||
mtz.add_column("IMEAN", 'J', dataset_id, -1, false);
|
||||
mtz.add_column("SIGIMEAN", 'Q', dataset_id, -1, false);
|
||||
if (has_anom) {
|
||||
mtz.add_column("I(+)", 'K', dataset_id, -1, false);
|
||||
mtz.add_column("SIGI(+)", 'M', dataset_id, -1, false);
|
||||
mtz.add_column("I(-)", 'K', dataset_id, -1, false);
|
||||
mtz.add_column("SIGI(-)", 'M', dataset_id, -1, false);
|
||||
}
|
||||
mtz.add_column("F", 'F', dataset_id, -1, false); // French-Wilson amplitude
|
||||
mtz.add_column("SIGF", 'Q', dataset_id, -1, false);
|
||||
if (has_anom) {
|
||||
mtz.add_column("F(+)", 'G', dataset_id, -1, false);
|
||||
mtz.add_column("SIGF(+)", 'L', dataset_id, -1, false);
|
||||
mtz.add_column("F(-)", 'G', dataset_id, -1, false);
|
||||
mtz.add_column("SIGF(-)", 'L', dataset_id, -1, false);
|
||||
}
|
||||
mtz.add_column("FreeR_flag", 'I', dataset_id, -1, false);
|
||||
|
||||
mtz.nreflections = static_cast<int>(reflections.size());
|
||||
mtz.data.reserve(reflections.size() * 8);
|
||||
mtz.data.reserve(reflections.size() * (has_anom ? 16 : 8));
|
||||
for (const auto& r : reflections) {
|
||||
mtz.data.push_back(static_cast<float>(r.h));
|
||||
mtz.data.push_back(static_cast<float>(r.k));
|
||||
mtz.data.push_back(static_cast<float>(r.l));
|
||||
mtz.data.push_back(r.I);
|
||||
mtz.data.push_back(r.sigma);
|
||||
if (has_anom) {
|
||||
mtz.data.push_back(r.I_plus);
|
||||
mtz.data.push_back(r.sigma_plus);
|
||||
mtz.data.push_back(r.I_minus);
|
||||
mtz.data.push_back(r.sigma_minus);
|
||||
}
|
||||
mtz.data.push_back(r.F);
|
||||
mtz.data.push_back(r.sigmaF);
|
||||
if (has_anom) {
|
||||
mtz.data.push_back(r.F_plus);
|
||||
mtz.data.push_back(r.sigmaF_plus);
|
||||
mtz.data.push_back(r.F_minus);
|
||||
mtz.data.push_back(r.sigmaF_minus);
|
||||
}
|
||||
mtz.data.push_back(r.rfree_flag ? 1.0f : 0.0f);
|
||||
}
|
||||
mtz.write_to_file(filename);
|
||||
|
||||
Reference in New Issue
Block a user