maps: the map file states the group the reflections are in, and the wavelength

Two things wrong with <prefix>_maps.mtz beside <prefix>.mtz.

It took the space group from the MODEL, unconditionally. That was invisible while
a model's hand was always adopted; now that a model decides nothing until it beats
its own null, a rejected model that asserts the other enantiomorph leaves the two
files disagreeing - the reflections in the group the data were merged in, the map
coefficients labelled with the model's. An enantiomorphic pair indexes identically,
so the coefficients are the same numbers either way and only the label moves, but
the two groups have different screw translations: a reader that expands symmetry
out of the map file was doing it in the wrong group. It now takes the group the
same way AdoptModelFrame does a few lines below - the model's only where the hand
was adopted, the data's otherwise.

And it carried no wavelength at all, DWAVEL 0.00000 on both datasets, because
nothing in ValidateAgainstModel knew it; it is now passed in from the experiment
the two callers already hold.

Measured on one rotation data set: with the model accepted both files read
P 43 21 2, with an unrelated model rejected both read P 41 21 2, and both carry
the collection wavelength.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-02 14:54:54 +02:00
co-authored by Claude Opus 5
parent 63a2f6744a
commit 0903a8eee3
4 changed files with 17 additions and 5 deletions
+11 -2
View File
@@ -146,7 +146,8 @@ ModelValidationResult ValidateAgainstModel(const std::vector<MergedReflection> &
Logger &logger,
const gemmi::SpaceGroup *data_space_group,
bool probe_indexing_ambiguity,
size_t nthreads) {
size_t nthreads,
double wavelength_A) {
ModelValidationResult result;
result.model_path = model_path;
@@ -730,9 +731,17 @@ ModelValidationResult ValidateAgainstModel(const std::vector<MergedReflection> &
// --- MTZ of map coefficients so the maps can be re-opened / rebuilt in Coot etc. ---
try {
gemmi::Mtz mtz(true);
mtz.spacegroup = sg;
// The group the REFLECTIONS end up in, which is the model's only where its hand was adopted -
// AdoptModelFrame decides this the same way a few lines below. An enantiomorphic pair indexes
// identically, so the coefficients are the same numbers either way and only the label moves;
// but the two groups have different screw translations, so a reader that expands symmetry out
// of this file works in the wrong one if the label disagrees with the .mtz beside it.
mtz.spacegroup = result.adopted_model_enantiomorph
? sg
: (data_space_group != nullptr ? data_space_group : sg);
mtz.set_cell_for_all(ucell);
mtz.add_dataset("model_validation");
mtz.datasets.back().wavelength = wavelength_A;
mtz.add_column("FP", 'F', -1, -1, false);
mtz.add_column("FC", 'F', -1, -1, false);
mtz.add_column("PHIC", 'P', -1, -1, false);
+2 -1
View File
@@ -146,7 +146,8 @@ ModelValidationResult ValidateAgainstModel(const std::vector<MergedReflection> &
Logger &logger,
const gemmi::SpaceGroup *data_space_group = nullptr,
bool probe_indexing_ambiguity = true,
size_t nthreads = 1);
size_t nthreads = 1,
double wavelength_A = 0.0);
// Reindex `merged` into the frame ValidateAgainstModel reported, so the reflection files that are
// written describe the same indexing as the R-factors and the maps. Returns the space group they are
+2 -1
View File
@@ -4803,7 +4803,8 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
config_.output_prefix, logger,
data_sg ? &*data_sg : nullptr,
/*probe_indexing_ambiguity=*/config_.reference_data.empty(),
static_cast<size_t>(config_.nthreads));
static_cast<size_t>(config_.nthreads),
experiment_.GetWavelength_A());
// A model that was asked for and could not be used has to say so where anyone will see
// it. Without this the run ends successfully with no R-free, no maps and nothing in the
// report - indistinguishable from a run that was never given --model at all.
+2 -1
View File
@@ -1790,7 +1790,8 @@ static int RunRugnux(int argc, char **argv) {
output_prefix, logger,
data_sg ? &*data_sg : nullptr,
/*probe_indexing_ambiguity=*/reference_data.empty(),
static_cast<size_t>(nthreads));
static_cast<size_t>(nthreads),
experiment.GetWavelength_A());
model_validation = validation;
if (!validation.failure_reason.empty())
model_validation_failure = validation.failure_reason;