From 0903a8eee33a47c6c6208ae4112d38bb5916fa0f Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Wed, 2 Sep 2026 14:54:54 +0200 Subject: [PATCH] maps: the map file states the group the reflections are in, and the wavelength Two things wrong with _maps.mtz beside .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) --- rugnux/ModelValidation.cpp | 13 +++++++++++-- rugnux/ModelValidation.h | 3 ++- rugnux/Rugnux.cpp | 3 ++- rugnux/rugnux_cli.cpp | 3 ++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/rugnux/ModelValidation.cpp b/rugnux/ModelValidation.cpp index 0471551c1..2f6013c4f 100644 --- a/rugnux/ModelValidation.cpp +++ b/rugnux/ModelValidation.cpp @@ -146,7 +146,8 @@ ModelValidationResult ValidateAgainstModel(const std::vector & 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 & // --- 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); diff --git a/rugnux/ModelValidation.h b/rugnux/ModelValidation.h index 0dcc61e94..079683736 100644 --- a/rugnux/ModelValidation.h +++ b/rugnux/ModelValidation.h @@ -146,7 +146,8 @@ ModelValidationResult ValidateAgainstModel(const std::vector & 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 diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index 875d30809..0a46484c4 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -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(config_.nthreads)); + static_cast(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. diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index b6a8a1e25..d83d204be 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -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(nthreads)); + static_cast(nthreads), + experiment.GetWavelength_A()); model_validation = validation; if (!validation.failure_reason.empty()) model_validation_failure = validation.failure_reason;