Refinement: write the anomalous difference map's coefficients to the refined maps

The refinement made the anomalous difference map on the refined model's
phases (for the blob finder and the solvent audit) but did not write it:
<prefix>_refined_maps.mtz carried FP..FREE only, while the validation's
<prefix>_maps.mtz (and _mr_maps.mtz) carried ANOM/PHANOM too.

Where the merge kept the Bijvoet pairs, the refined maps now carry ANOM
and PHANOM as well, the same coefficients the refinement reads (F(+) -
F(-) on the model phase turned back by 90 degrees), missing (NaN) on
reflections without a pair - the layout of the validation's file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqXYdL4qBGYRiSXSGXfCLu
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-11-05 15:55:15 +01:00
1 parent b7d3ddb2b6
commit 5c7cd2510f
3 files changed
+19 -4

No files matched your search

+2 -1
View File
@@ -356,7 +356,8 @@ The refined model's map coefficients, `<prefix>_refined_maps.mtz`, are $2mF_o -
$\sigma_A$ of §14.4, so the maps carry the weights the target was minimised with; $m$ is
$I_1(X)/I_0(X)$, $X = 2F_oD|F_\mathrm{model}|/\Sigma$, for an acentric reflection and $\tanh(X/2)$ for a
centric one. Where the merge kept the Bijvoet pairs, the anomalous difference map is made on the refined
model's phases too, and read by the blob finder below.
model's phases too, written in the same file as `ANOM`/`PHANOM` (as in `<prefix>_maps.mtz`; missing where a
reflection has no Bijvoet pair), and read by the blob finder below.
### 15.9 Unexplained density: blobs
+1 -1
View File
@@ -440,7 +440,7 @@ the data, a weak shell says nothing about them, and nothing acts on it.
|---|---|---|
| `<prefix>.mtz` / `.cif` / `.hkl` | The answer: the data's space group, with only the class-(b) labels from the model | Refinement, deposition, everything by default |
| `<prefix>_model.cif` / `.pdb`, `<prefix>_maps.mtz` | The model as placed, in the frame of `<prefix>.mtz`, and its map coefficients (Coot/PyMOL open them as maps) | Starting refinement (REFMAC5, phenix.refine), PanDDA, Coot |
| `<prefix>_refined_model.cif`/`.pdb`, `<prefix>_refined_maps.mtz`, `<prefix>_blobs.json` | The placed model refined by rugnux, its map coefficients, and the unexplained blobs of its difference map | A first look at the refined density and at what the model leaves unexplained; continuing refinement |
| `<prefix>_refined_model.cif`/`.pdb`, `<prefix>_refined_maps.mtz`, `<prefix>_blobs.json` | The placed model refined by rugnux, its map coefficients (with the anomalous difference map's, `ANOM`/`PHANOM`, where the merge kept the Bijvoet pairs, as in `<prefix>_maps.mtz`), and the unexplained blobs of its difference map | A first look at the refined density and at what the model leaves unexplained; continuing refinement |
| `<prefix>_twin_<SG>.mtz` / `.cif` | The same data merged in a subgroup with a twin law — written from the data, model or not | Twin refinement, where you suspect the higher group absorbed a twin law |
| `<prefix>_mr.mtz` / `.cif`, `<prefix>_mr_model.pdb` / `.cif`, `<prefix>_mr_*` maps | The merge in the space group the model chose (`MODEL_CHOICE_SPACE_GROUP=`), and the model placed there | Refinement *if* you accept the model's choice; compare R-free against the answer's |
@@ -8,6 +8,7 @@
#include <chrono>
#include <set>
#include <future>
#include <limits>
#include <map>
#include <optional>
#include <cmath>
@@ -1252,12 +1253,25 @@ void MakeMaps(Context &ctx, const std::vector<MergedReflection> &merged, double
gemmi::AsuData<std::complex<float>> anom;
anom.unit_cell_ = ctx.cell;
anom.spacegroup_ = ctx.sg;
// Its coefficients in the maps file too (ANOM/PHANOM, as model validation writes them), missing (NaN) where
// the reflection has no Bijvoet pair.
const float nan = std::numeric_limits<float>::quiet_NaN();
MtzTable::Column anom_col{"ANOM", 'F', 1, {}}, phanom_col{"PHANOM", 'P', 1, {}};
for (size_t m = 0; m < ctx.refl.Size(); m++) {
const auto it = danom.find(ctx.refl.hkl[m]);
if (it != danom.end())
anom.v.push_back({ctx.refl.hkl[m], it->second * std::polar(1.0f, static_cast<float>(std::arg(fmodel[m]) - PI / 2))});
if (it == danom.end()) {
anom_col.values.push_back(nan);
phanom_col.values.push_back(nan);
continue;
}
const std::complex<float> c = it->second * std::polar(1.0f, static_cast<float>(std::arg(fmodel[m]) - PI / 2));
anom.v.push_back({ctx.refl.hkl[m], c});
anom_col.values.push_back(std::abs(c));
phanom_col.values.push_back(MtzPhase(std::arg(c) * 180.0 / PI));
}
if (!anom.v.empty()) {
mtz.columns.push_back(std::move(anom_col));
mtz.columns.push_back(std::move(phanom_col));
result.anomalous_map = std::make_shared<gemmi::Grid<float>>(
MapFromFPhi(gemmi::get_f_phi_on_grid<float>(anom, gemmi::get_size_for_hkl(anom, {{0, 0, 0}}, 3.0), true)));
result.anomalous_map_rms = gemmi::calculate_data_statistics(result.anomalous_map->data).rms;