From 80ed784d1de41142bda0646acdc2f434a2bb5c80 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 19 Oct 2026 08:07:53 +0200 Subject: [PATCH] rugnux: no .ccp4 map files - the maps are given as coefficients in _maps.mtz only A model validation no longer writes _2fofc.ccp4, _fofc.ccp4 and _anom.ccp4 (nor their _mr twins); _maps.mtz, which Coot and PyMOL open as maps, is what is written. With the files goes everything that only existed for them: ModelMaps keeps the coefficients alone, the keep_map_grids switch is gone from the validation schedule and the rugnux config, and the mFo-DFc map is no longer transformed to real space at all (nothing read it). The 2mFo-DFc and anomalous maps are still made, for the mean atom density and the anomalous-site read-out. The anomalous difference map is now in no output file: _maps.mtz carries no anomalous columns. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PXc1zD1yxHqaR1DGdCTHL4 --- docs/CPU_DATA_ANALYSIS_DECISIONS.md | 6 +++--- docs/RUGNUX_ADVANCED.md | 10 +++++----- docs/RUGNUX_MODEL_AND_REFERENCE.md | 4 ++-- docs/RUGNUX_REPORT.md | 2 +- docs/RUGNUX_TUTORIAL.md | 6 +++--- .../structure_refinement/ModelValidation.cpp | 19 ++++-------------- .../structure_refinement/ModelValidation.h | 7 +------ rugnux/Rugnux.h | 6 +----- rugnux/RugnuxOutputFiles.cpp | 20 +------------------ rugnux/RugnuxOutputFiles.h | 3 +-- rugnux/RugnuxScaleMerge.cpp | 16 ++++----------- rugnux/rugnux_cli.cpp | 5 +---- tests/ModelValidationTest.cpp | 3 +-- 13 files changed, 28 insertions(+), 79 deletions(-) diff --git a/docs/CPU_DATA_ANALYSIS_DECISIONS.md b/docs/CPU_DATA_ANALYSIS_DECISIONS.md index 3a904b742..64e4479bb 100644 --- a/docs/CPU_DATA_ANALYSIS_DECISIONS.md +++ b/docs/CPU_DATA_ANALYSIS_DECISIONS.md @@ -156,7 +156,7 @@ with R-free the same sum restricted to the free set. Every parameter $F_\mathrm{ ### 14.4 Electron-density maps -Two maps are formed with the model phases $\varphi_\mathrm{model}$, both $\sigma_A$ weighted: a $2mF_o-DF_c$ map and an $mF_o-DF_c$ difference map, each inverse-Fourier-transformed to a real-space CCP4 map (`_2fofc.ccp4`, `_fofc.ccp4`). A map-coefficient MTZ (`_maps.mtz`: `FP`, `FC`, `PHIC`, `FWT`/`PHWT`, `DELFWT`/`PHDELWT`, `FOM`, `FREE`) is written alongside so the maps can be reopened or rebuilt in Coot / PyMOL. +Two sets of map coefficients are formed with the model phases $\varphi_\mathrm{model}$, both $\sigma_A$ weighted: $2mF_o-DF_c$ and $mF_o-DF_c$. They are written as a map-coefficient MTZ (`_maps.mtz`: `FP`, `FC`, `PHIC`, `FWT`/`PHWT`, `DELFWT`/`PHDELWT`, `FOM`, `FREE`), which Coot / PyMOL open directly as maps. The $2mF_o-DF_c$ coefficients are also inverse-Fourier-transformed to a real-space map internally, for the mean density at the atom centres. $\sigma_A$ is estimated by maximum likelihood **per resolution shell**, on the **free reflections only** — on the working set the model has been fitted to the data, so $\sigma_A$ would come out too high and the weighting would understate exactly the model error the map is meant to reveal. The shells are cut by equal reflection count, and it is the *number of shells* that is chosen from the size of the free set (about 50 free reflections to a shell, at most 20 shells), so no shell is thin by construction; a shell that still ends up with fewer than 10 free reflections takes the estimate made over the whole free set instead. Acentric and centric reflections enter with their own likelihoods (Rice and Woolfson respectively), and the epsilon factor is divided out before normalising both amplitudes to $\langle|E|^2\rangle = 1$ within the shell. The figure of merit is then $m = I_1(X)/I_0(X)$ with $X = 2\sigma_A|E_o||E_c|/(1-\sigma_A^2)$ for an acentric reflection and $m = \tanh(X/2)$ for a centric one, and $D = \sigma_A\sqrt{\Sigma_o/\Sigma_c}$ carries $F_c$ onto the observed amplitudes' scale. The $2F_o-F_c$ combination becomes $2mF_o - DF_c$ for acentric reflections and $mF_o$ for centric ones — a centric reflection's phase is either exactly right or 180° wrong, never in between — while the difference coefficient is $mF_o - DF_c$ throughout. @@ -253,13 +253,13 @@ $ \big(|F(+)| - |F(-)|\big)\, e^{i(\varphi_\mathrm{model} - \pi/2)}, $ -over the acentric reflections that have both hands (a centric reflection has no anomalous difference, only noise). Turning the model phase back by 90° is what makes the anomalous scattering, which is 90° out of phase with the normal scattering, add up in the real part: the map's peaks then sit on the anomalous scatterers. It is written as `_anom.ccp4`. Its hand is the one §14.6 settled: with the mates the wrong way round every peak becomes a trough, so a map of clean peaks is itself a check that the frame is right. +over the acentric reflections that have both hands (a centric reflection has no anomalous difference, only noise). Turning the model phase back by 90° is what makes the anomalous scattering, which is 90° out of phase with the normal scattering, add up in the real part: the map's peaks then sit on the anomalous scatterers. It is read here and not written out. Its hand is the one §14.6 settled: with the mates the wrong way round every peak becomes a trough, so a map of clean peaks is itself a check that the frame is right. Rather than searching the map for blobs and leaving a list of coordinates, the map is read **at the model's own atom centres** (hydrogens excluded — they scatter no anomalous signal), and the ten highest, in units of the map's r.m.s., are reported in the log and as `ANOMALOUS_SITE_01`…`ANOMALOUS_SITE_10` in the results report. Each site is therefore named — the atom, residue and chain it belongs to — which is what says *what* carries the signal, not just where it is. The reading is cubic, not linear: the map is sampled every $d_\mathrm{min}/3$, and a peak that sharp read by trilinear interpolation comes out up to a quarter low — unevenly enough to reorder the sites. This reading is the one ANODE reports. The same reading averaged over the model's **anomalous scatterers** — every atom from phosphorus ($Z = 15$) up: the S of Met and Cys, metals, Cl, I — is reported as `ANOMALOUS_SCATTERER_MEAN_SIGMA` (with their count, `ANOMALOUS_SCATTERERS`): one number for how much anomalous signal the merge carries, which does not depend on which ten atoms happen to come out on top. Below phosphorus (C, N, O, Na, Mg) $f''$ is a fraction of sulfur's at any wavelength these data are taken at, and counting those atoms would only dilute the mean with noise. Because the map is built on the model's phases and the data's own indexing, it is also the only test of whether the two agree about the **hand** (§14.6). A model and a dataset in opposite hands turn every anomalous peak into a trough, so a map whose deepest hole at an atom is both deeper than $5\sigma$ and deeper than its highest peak says so, and the run reports it as a warning naming that atom. It is not repaired by reindexing: that would make the two agree by construction and destroy the evidence for which of the model and the data is in the wrong hand. Note that R-free cannot see this at all — a mirrored model gives R-free to four decimal places unchanged, and an exactly inverted anomalous map. -The list is always ten entries long, so it is their height that carries the information: on a sulfur-SAD dataset the sulfurs fill the top of the list and are followed by a clear drop to the couple of sigma that is the map's noise, while a dataset with no anomalous signal has no such separation and lists ten unrelated atoms at noise level. A scatterer the **model does not contain** — a bound ion, a soaked heavy atom — is by construction invisible in the list, and is what the map file is for. +The list is always ten entries long, so it is their height that carries the information: on a sulfur-SAD dataset the sulfurs fill the top of the list and are followed by a clear drop to the couple of sigma that is the map's noise, while a dataset with no anomalous signal has no such separation and lists ten unrelated atoms at noise level. A scatterer the **model does not contain** — a bound ion, a soaked heavy atom — is by construction invisible in the list, and takes the anomalous difference map itself, which rugnux does not write out. ### 14.8 Rigid-body placement of the model diff --git a/docs/RUGNUX_ADVANCED.md b/docs/RUGNUX_ADVANCED.md index 4116ec3bf..6c8d56d0b 100644 --- a/docs/RUGNUX_ADVANCED.md +++ b/docs/RUGNUX_ADVANCED.md @@ -153,11 +153,11 @@ Which decisions a model can influence, and which it cannot, is set out decision Given an atomic model of the same structure, `--model model.pdb` scales the model structure factors to the merged amplitudes — fitting a flat bulk-solvent contribution and an overall anisotropic *B* — and reports **R-work / R-free** and the mean 2mFo−DFc density at the atom centres. -It also writes the σA-weighted maps `_2fofc.ccp4` (2mFo−DFc) and -`_fofc.ccp4` (mFo−DFc), and the map-coefficient MTZ `_maps.mtz`, next to the -merged reflections — and, where the merge kept the Bijvoet split (a rotation merge always does), -`_anom.ccp4`, the anomalous difference map whose strongest sites the report names -(`ANOMALOUS_SITE_01`…`10`), and the map's mean height at the model's anomalous scatterers - every +It also writes the map-coefficient MTZ `_maps.mtz` next to the merged reflections: the +σA-weighted 2mFo−DFc and mFo−DFc coefficients, which Coot and PyMOL open directly as +maps. Where the merge kept the Bijvoet split (a rotation merge always does), an anomalous +difference map is made too and read, not written: the report names its strongest sites +(`ANOMALOUS_SITE_01`…`10`) and the map's mean height at the model's anomalous scatterers - every atom from phosphorus up - as one number for the anomalous signal (`ANOMALOUS_SCATTERER_MEAN_SIGMA`). The structure itself is not refined; the model is re-fractionalized into the data cell and then placed as **one rigid body**, so a deposited model from a crystal that is not diff --git a/docs/RUGNUX_MODEL_AND_REFERENCE.md b/docs/RUGNUX_MODEL_AND_REFERENCE.md index 1cc62e8c2..65b32d536 100644 --- a/docs/RUGNUX_MODEL_AND_REFERENCE.md +++ b/docs/RUGNUX_MODEL_AND_REFERENCE.md @@ -154,7 +154,7 @@ these letters. is a result, not a warning ([Molecular replacement](RUGNUX_REPORT.md#molecular-replacement)). 8. **Validation and files.** R-factors, CC(model, data), maps and the placed model are computed for the placement that was kept, against the merge it was chosen on, and written: - `_2fofc.ccp4`, `_fofc.ccp4`, `_anom.ccp4`, `_maps.mtz`, `_model.cif`/`.pdb` — or the + `_maps.mtz` (map coefficients), `_model.cif`/`.pdb` — or the `_mr_*` equivalents where the choice was `_mr`. A rejected model gets its maps too: that is the case where looking at it in the density is most useful. 9. **The reflection files** — `.mtz`, `.cif`, `.hkl`, `_unmerged.mtz` — are written last, @@ -293,7 +293,7 @@ the data, a weak shell says nothing about them, and nothing acts on it. | File | What it is | Use it for | |---|---|---| | `.mtz` / `.cif` / `.hkl` | The answer: the data's space group, with only the class-(b) labels from the model | Refinement, deposition, everything by default | -| `_model.cif` / `.pdb`, `_2fofc.ccp4`, `_fofc.ccp4`, `_anom.ccp4`, `_maps.mtz` | The model as placed, in the frame of `.mtz`, and its maps | Starting refinement (REFMAC5, phenix.refine), PanDDA, Coot | +| `_model.cif` / `.pdb`, `_maps.mtz` | The model as placed, in the frame of `.mtz`, and its map coefficients (Coot/PyMOL open them as maps) | Starting refinement (REFMAC5, phenix.refine), PanDDA, Coot | | `_twin_.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 | | `_mr.mtz` / `.cif`, `_mr_model.pdb` / `.cif`, `_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 | diff --git a/docs/RUGNUX_REPORT.md b/docs/RUGNUX_REPORT.md index 5fc36b0ec..bae385ed0 100644 --- a/docs/RUGNUX_REPORT.md +++ b/docs/RUGNUX_REPORT.md @@ -919,7 +919,7 @@ coordinates, the B-factors and the occupancies are the model's own, and only the placement are fitted — so work and free estimate the same quantity (they differ by a median 0.001 over our corpus) and the split buys no cross-validation while costing a factor of √20 in precision. -None of this touches the maps: the map coefficients, the `.ccp4` files and `R_WORK` / `R_FREE` are +None of this touches the maps: the map coefficients and `R_WORK` / `R_FREE` are computed from the scale described above and are unchanged by these keys. | key | meaning | diff --git a/docs/RUGNUX_TUTORIAL.md b/docs/RUGNUX_TUTORIAL.md index d74631dcf..ecc3e3fdc 100644 --- a/docs/RUGNUX_TUTORIAL.md +++ b/docs/RUGNUX_TUTORIAL.md @@ -275,9 +275,9 @@ Absorption is corrected only by the empirical surface a rotation run fits and ke merged files and with `--no-merge` too; `--no-export-unmerged` skips it. See [The unmerged export](RUGNUX_INTEGRATION.md#the-unmerged-export). `--export-unmerged-partials` writes `_unmerged_partials.mtz`, one row per image, instead of summing. -- With `--model`, the validation outputs land beside the files above: the σA-weighted - maps `_2fofc.ccp4` and `_fofc.ccp4`, the map-coefficient MTZ `_maps.mtz`, - the anomalous difference map `_anom.ccp4` (where the merge kept the Bijvoet split), and +- With `--model`, the validation outputs land beside the files above: the map-coefficient MTZ + `_maps.mtz` (σA-weighted 2mFo−DFc and mFo−DFc; Coot and PyMOL open it + directly as maps), and the model **as placed** against the data as `_model.cif` **and** `_model.pdb` — the coordinates that go with the maps, in the cell and space group of `.mtz`. Mind the names: `.cif` is merged *reflections*, `_model.cif` is *coordinates*. The PDB is diff --git a/image_analysis/structure_refinement/ModelValidation.cpp b/image_analysis/structure_refinement/ModelValidation.cpp index 25c96b7fb..0ad7cd61a 100644 --- a/image_analysis/structure_refinement/ModelValidation.cpp +++ b/image_analysis/structure_refinement/ModelValidation.cpp @@ -1697,9 +1697,8 @@ ModelValidationResult Validate(const std::vector &merged, result.mean_fom = sigma_a.mean_fom; result.sigma_a_shells = sigma_a.shells; - gemmi::AsuData> map2fofc, mapfofc; + gemmi::AsuData> map2fofc; map2fofc.unit_cell_ = ucell; map2fofc.spacegroup_ = sg; - mapfofc.unit_cell_ = ucell; mapfofc.spacegroup_ = sg; std::vector fwt(terms.size()), delfwt(terms.size()); for (size_t i = 0; i < terms.size(); i++) { const double m = sigma_a.weight[i].m, D = sigma_a.weight[i].d; @@ -1710,7 +1709,6 @@ ModelValidationResult Validate(const std::vector &merged, delfwt[i] = static_cast(m * terms[i].fo - D * terms[i].fc); const std::complex ph = std::polar(1.0f, static_cast(terms[i].phi)); map2fofc.v.push_back({terms[i].hkl, fwt[i] * ph}); - mapfofc.v.push_back({terms[i].hkl, delfwt[i] * ph}); } // A validation started before the one it follows had decided makes no maps until that one has, @@ -1728,13 +1726,9 @@ ModelValidationResult Validate(const std::vector &merged, if (!maps_superseded) { maps = std::make_shared(); // --- make the maps and score the 2mFo-DFc map at atom centres (a real map peaks there) --- - // The three maps - this one, the difference map and the anomalous map - share nothing but what - // they read, so the other two are made on threads of their own beside this one. Nothing here - // reads the difference map, so it is made only where it is kept. - std::future fofc_map = std::async(std::launch::async, [&] { - if (schedule.keep_map_grids) - maps->map_fofc = map_from_coefficients(mapfofc, sf_gpu); - }); + // This map and the anomalous map share nothing but what they read, so the anomalous one is made + // on a thread of its own beside this one. Nothing here reads the difference map: its coefficients + // go to the file, the map is not made. // --- anomalous difference map, where the merge kept the Bijvoet split --- // Coefficients F(+) - F(-) carried on the model phase turned back by 90 degrees. Its peaks sit on @@ -1749,8 +1743,6 @@ ModelValidationResult Validate(const std::vector &merged, if (!mapanom.v.empty()) { gemmi::Grid grid = map_from_coefficients(mapanom, sf_gpu); read_anomalous_map(grid, map_rms(grid), st, result); - if (schedule.keep_map_grids) - maps->map_anom = std::move(grid); } }); @@ -1764,9 +1756,6 @@ ModelValidationResult Validate(const std::vector &merged, for (gemmi::Atom &a : r.atoms) { s += grid2fofc.interpolate_value(a.pos, MAP_INTERPOLATION_ORDER); ++n; } result.mean_atom_density_sigma = (n > 0 && rms2 > 0) ? (s / n) / rms2 : 0; } - if (schedule.keep_map_grids) - maps->map_2fofc = std::move(grid2fofc); - fofc_map.get(); anomalous_map.get(); } diff --git a/image_analysis/structure_refinement/ModelValidation.h b/image_analysis/structure_refinement/ModelValidation.h index d059d31d5..b9dba2fe3 100644 --- a/image_analysis/structure_refinement/ModelValidation.h +++ b/image_analysis/structure_refinement/ModelValidation.h @@ -22,12 +22,9 @@ class Logger; namespace gemmi { struct Structure; } -// The maps of a validation: their coefficients, as _maps.mtz holds them, and the maps themselves, -// as the .ccp4 files hold them, where the caller asked for them (ModelValidationSchedule::keep_map_grids). -// The anomalous map is there only where the merge kept the Bijvoet split. +// The maps of a validation: their coefficients, as _maps.mtz holds them. struct ModelMaps { MtzTable coefficients; - std::optional> map_2fofc, map_fofc, map_anom; }; // Result of validating merged intensities against an atomic model. @@ -294,8 +291,6 @@ struct ModelValidationSchedule { // GPU memory another validation running beside this one holds there. It limits how many engines // this one takes and never what runs on the GPU, so no number depends on whether the other runs. size_t gpu_beside_bytes = 0; - // Keep the maps themselves (ModelMaps), not only their coefficients: rugnux writes them as .ccp4. - bool keep_map_grids = false; }; // What a model validation needs of its card, part by part: the bytes of one engine of each, how many diff --git a/rugnux/Rugnux.h b/rugnux/Rugnux.h index 80c2979f5..cf08a2cee 100644 --- a/rugnux/Rugnux.h +++ b/rugnux/Rugnux.h @@ -106,10 +106,6 @@ struct ProcessConfig { // scaling. bool write_merged = true; - // Keep the model validation's maps themselves (ModelMaps), not only their coefficients: rugnux writes - // them as .ccp4. The broker never asks for them. - bool keep_map_grids = false; - SpotFindingSettings spot_finding; // FullAnalysis spot finding (and Calibration by spots) // Calibration: how the powder rings are measured, and the calibrant's rings as q = 2*pi/d (1/A). @@ -380,7 +376,7 @@ struct RugnuxOutputs { std::optional merged; // .mtz/.cif/.hkl std::optional twin; // the twin hypothesis' merge std::optional model_prior; // the model's choice, _mr.mtz/.cif - std::shared_ptr maps; // _maps.mtz, _2fofc/_fofc/_anom.ccp4 + std::shared_ptr maps; // _maps.mtz std::shared_ptr model_prior_maps; // the same at _mr std::optional placed_model; // _model.cif/.pdb std::optional model_prior_placed_model; // _mr_model.cif/.pdb diff --git a/rugnux/RugnuxOutputFiles.cpp b/rugnux/RugnuxOutputFiles.cpp index 1570f47e0..ea118d3fd 100644 --- a/rugnux/RugnuxOutputFiles.cpp +++ b/rugnux/RugnuxOutputFiles.cpp @@ -6,21 +6,12 @@ #include #include -#include - #include "../writer/FileWriter.h" #include "../writer/WriteModel.h" #include "../writer/WriteRugnuxFiles.h" namespace { -void WriteCcp4(const gemmi::Grid &map, const std::string &path) { - gemmi::Ccp4 ccp4; - ccp4.grid = map; - ccp4.update_ccp4_header(2); - ccp4.write_ccp4_map(path); -} - // The files of one output pass at `prefix`, all but its _process.h5. void WriteOutputs(const RugnuxOutputs &o, const std::string &prefix, const OutputProvenance &provenance, size_t nthreads, Logger &logger) { @@ -132,16 +123,7 @@ void WriteMergedOutput(const MergedOutput &merged, const std::string &prefix, co void WriteModelMaps(const ModelMaps &maps, const std::string &prefix, Logger &logger) { WriteMtz(maps.coefficients, prefix + "_maps.mtz"); - if (maps.map_2fofc) - WriteCcp4(*maps.map_2fofc, prefix + "_2fofc.ccp4"); - if (maps.map_fofc) - WriteCcp4(*maps.map_fofc, prefix + "_fofc.ccp4"); - if (maps.map_anom) - WriteCcp4(*maps.map_anom, prefix + "_anom.ccp4"); - logger.Info("Model validation: wrote {}_maps.mtz{}", prefix, - maps.map_2fofc ? fmt::format(", {0}_2fofc.ccp4, {0}_fofc.ccp4{1}", prefix, - maps.map_anom ? ", " + prefix + "_anom.ccp4" : std::string()) - : std::string()); + logger.Info("Model validation: wrote {}_maps.mtz", prefix); } void WritePerImagePlot(const std::string &prefix, const PerImagePlot &plot) { diff --git a/rugnux/RugnuxOutputFiles.h b/rugnux/RugnuxOutputFiles.h index 86f425576..5821bd21c 100644 --- a/rugnux/RugnuxOutputFiles.h +++ b/rugnux/RugnuxOutputFiles.h @@ -39,8 +39,7 @@ void WriteRugnuxOutputs(const ProcessResult &result, const std::string &prefix, void WriteMergedOutput(const MergedOutput &merged, const std::string &prefix, const OutputProvenance &provenance, size_t nthreads, bool hkl); -// A model validation's maps at `prefix`: _maps.mtz, and _2fofc.ccp4, _fofc.ccp4 and _anom.ccp4 for the maps -// it kept (ModelValidationSchedule::keep_map_grids). +// A model validation's map coefficients at `prefix`: _maps.mtz. void WriteModelMaps(const ModelMaps &maps, const std::string &prefix, Logger &logger); // _plot.txt (PerImagePlotText). diff --git a/rugnux/RugnuxScaleMerge.cpp b/rugnux/RugnuxScaleMerge.cpp index 6bc59cf37..ad9bc12f5 100644 --- a/rugnux/RugnuxScaleMerge.cpp +++ b/rugnux/RugnuxScaleMerge.cpp @@ -3925,14 +3925,11 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) { // measurements, so the statistics computed above are unaffected; what changes is that the file, // the R-factors and the maps then all describe one indexing instead of two. // - // A validation's maps go to the outputs as it returns: at or _mr, each map the one - // the last validation there made - the anomalous map only where that one's merge kept the Bijvoet split. + // A validation's maps go to the outputs as it returns: at or _mr, the ones the last + // validation there made. const auto take_maps = [](std::shared_ptr &at, ModelValidationResult &v) { - if (!v.maps) - return; - if (at && at->map_anom && !v.maps->map_anom) - v.maps->map_anom = std::move(at->map_anom); - at = std::move(v.maps); + if (v.maps) + at = std::move(v.maps); }; // The twin-law merge made ready to be written: on the axes the answer is written on (every @@ -4148,7 +4145,6 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) { const size_t merge_on_card = rsm && rsm->OnGPU() ? rsm->DeviceBytes() : 0; ModelValidationSchedule first_schedule; first_schedule.gpu_resident_bytes = merge_on_card; - first_schedule.keep_map_grids = config_.keep_map_grids; first_schedule.on_forecast = [&](const ModelFrameForecast &f) { if (ahead.run.valid() || !config_.reference_data.empty() || !data_sg.has_value() || !f.may_run_beside || f.enantiomorph_candidate @@ -4180,7 +4176,6 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) { } ModelValidationSchedule schedule; schedule.maps_gate = gate; - schedule.keep_map_grids = config_.keep_map_grids; schedule.fit_test = false; // the verdict is the first validation's (KeepModelVerdict) schedule.gpu_resident_bytes = merge_on_card; schedule.gpu_beside_bytes = beside; @@ -4290,7 +4285,6 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) { ModelValidationSchedule kept_verdict; kept_verdict.fit_test = false; kept_verdict.gpu_resident_bytes = merge_on_card; - kept_verdict.keep_map_grids = config_.keep_map_grids; remade = ValidateAgainstModel(sm.merged, *result.consensus_cell, *config_.model, logger, sg_now ? &*sg_now : nullptr, @@ -4336,7 +4330,6 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) { }; ModelValidationSchedule twin_schedule; twin_schedule.gpu_resident_bytes = merge_on_card; - twin_schedule.keep_map_grids = config_.keep_map_grids; twin_schedule.maps_superseded = [&](const ModelValidationResult &v) { return !maps_from(v); }; phase("Validating against model (twin hypothesis)"); auto twin_validation = ValidateAgainstModel( @@ -4464,7 +4457,6 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) { ModelValidationSchedule placed_elsewhere; placed_elsewhere.fit_test = false; placed_elsewhere.gpu_resident_bytes = merge_on_card; - placed_elsewhere.keep_map_grids = config_.keep_map_grids; phase("Validating against model (molecular replacement)"); ModelValidationResult v = ValidateAgainstModel( own ? own->merged : sm.merged, cell, *config_.model, logger, &data_sg, diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index c6a9bcd89..03522cc7d 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -2332,15 +2332,13 @@ static int RunRugnux(int argc, char **argv, bool no_gpu) { std::vector report_shell_d_min; for (const auto &sh : merged_statistics.shells) report_shell_d_min.push_back(sh.d_min); - ModelValidationSchedule schedule; - schedule.keep_map_grids = true; auto validation = ValidateAgainstModel(merged_reflections, *experiment.GetUnitCell(), *model, logger, data_sg ? &*data_sg : nullptr, /*probe_indexing_ambiguity=*/reference_data.empty(), static_cast(nthreads), - experiment.GetWavelength_A(), report_shell_d_min, schedule); + experiment.GetWavelength_A(), report_shell_d_min); if (validation.maps) WriteModelMaps(*validation.maps, output_prefix, logger); validation.maps.reset(); @@ -3174,7 +3172,6 @@ static int RunRugnux(int argc, char **argv, bool no_gpu) { std::optional sink; if (!output_prefix.empty() && (run_scaling ? write_process_h5_flag : true)) config.image_sink = &sink.emplace(output_prefix); - config.keep_map_grids = true; config.finalist_ledger = finalist_ledger_flag; config.live = broker_feed || simulate_live; config.fast_live = fast_live && config.live; diff --git a/tests/ModelValidationTest.cpp b/tests/ModelValidationTest.cpp index 1697896d9..58b8a4ba9 100644 --- a/tests/ModelValidationTest.cpp +++ b/tests/ModelValidationTest.cpp @@ -574,10 +574,9 @@ TEST_CASE("ModelValidation_FindsTheDatasDescriptionOfTheLattice", "[ModelValidat const auto moved = ValidateAgainstModel(obs, data_cell, ReadModel(other_setting), logger, data_sg, /*probe_indexing_ambiguity=*/false, 1, 1.0); REQUIRE(moved.ok); - // The maps come back in memory: their coefficients always, the maps themselves only when asked for. + // The map coefficients come back in memory. REQUIRE(moved.maps); CHECK(moved.maps->coefficients.columns.size() == 12); - CHECK_FALSE(moved.maps->map_2fofc); logger.Info("Change of basis test: took {} out of {}, R-work {:.4f}", moved.change_of_basis_op.triplet(), moved.setting_as_read, moved.r_work); CHECK_FALSE(moved.change_of_basis_op == gemmi::Op::identity());