diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e154e1dd..13aeb112 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,7 +3,7 @@ ### 1.0.0-rc.164 This is an UNSTABLE release. It includes many experimental features, as well as many AI generated fixes. We recommend using rc.152 for production use. -* rugnux: `--export-unmerged ` writes the integrated observations as an unmerged MTZ - readable by aimless, pointless, careless and `iotbx.merging_statistics` - in `--mode mx` and `--mode scale` alike, with each rotation reflection's partials summed into one full; `--export-unmerged-partials ` writes one row per image instead. +* rugnux: `--export-unmerged` writes the integrated observations as `_unmerged.mtz`, an unmerged MTZ readable by aimless, pointless, careless and `iotbx.merging_statistics`, in `--mode mx` and `--mode scale` alike; each rotation reflection's partials are summed into one full, and `--export-unmerged-partials` writes one row per image instead. Lattice-centring absences are not written; screw and glide absences are. ### 1.0.0-rc.163 This is an UNSTABLE release. It includes many experimental features, as well as many AI generated fixes. We recommend using rc.152 for production use. diff --git a/image_analysis/WriteReflections.cpp b/image_analysis/WriteReflections.cpp index 5a402a21..2ebfc046 100644 --- a/image_analysis/WriteReflections.cpp +++ b/image_analysis/WriteReflections.cpp @@ -5,6 +5,7 @@ #include "scale_merge/Merge.h" #include "scale_merge/HKLKey.h" #include "scale_merge/TwinningAnalysis.h" +#include "bragg_integration/SystematicAbsence.h" #include "../common/ParallelFor.h" #include @@ -692,7 +693,18 @@ void WriteUnmergedMtzReflections(const std::vector &outcomes // it, and every program this file is for wants to handle it its own way. gemmi::UnmergedHklMover hkl_mover(mtz.spacegroup); std::set batch_numbers; + + // Lattice-centring absences are integrated on purpose - prediction runs in P so the space-group + // search can confirm or disprove the centring - but they are not written here. POINTLESS reads + // the interstitial nodes, takes the lattice for primitive and demotes the space group, and they + // roughly halve AIMLESS's . Screw and glide absences ARE written: they are the evidence + // the space group was chosen on, so a program reading this file can check that choice, and + // removing them turns its test into an assumption. XDS and DIALS draw the line in the same place. + const char centering = mtz.spacegroup ? mtz.spacegroup->hm[0] : 'P'; + const auto add_row = [&](const Reflection &r) { + if (systematic_absence(r.h, r.k, r.l, centering)) + return; std::array hkl{r.h, r.k, r.l}; const int isym = hkl_mover.move_to_asu(hkl); // A summed full's image_number is its rocking-curve centroid, so this is the batch the diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index 815f0332..211949d5 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -2991,17 +2991,19 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b // rather than the merged ones: the partiality and the per-image scale are left for the reading // program, which fits a scale model of its own. if (full && !cancelled_ && write_output && !geometry_prepass && result.consensus_cell) { - if (!config_.unmerged_export_path.empty()) { + if (config_.export_unmerged) { if (observer) observer->OnPhase("Writing unmerged reflections"); + const std::string path = config_.output_prefix + "_unmerged.mtz"; WriteUnmergedMtzReflections(indexer->GetIntegrationOutcome(), *result.consensus_cell, - experiment_, true, config_.unmerged_export_path); - logger.Info("Unmerged observations written to {}", config_.unmerged_export_path); + experiment_, true, path); + logger.Info("Unmerged observations written to {}", path); } - if (!config_.unmerged_partials_export_path.empty()) { + if (config_.export_unmerged_partials) { if (observer) observer->OnPhase("Writing unmerged reflections"); + const std::string path = config_.output_prefix + "_unmerged_partials.mtz"; WriteUnmergedMtzReflections(indexer->GetIntegrationOutcome(), *result.consensus_cell, - experiment_, false, config_.unmerged_partials_export_path); - logger.Info("Unmerged partials written to {}", config_.unmerged_partials_export_path); + experiment_, false, path); + logger.Info("Unmerged partials written to {}", path); } } diff --git a/rugnux/Rugnux.h b/rugnux/Rugnux.h index 3c54800c..409d590d 100644 --- a/rugnux/Rugnux.h +++ b/rugnux/Rugnux.h @@ -119,14 +119,14 @@ struct ProcessConfig { // Diagnostic: if set, the -P rot3d combine writes the unmerged fulls here (for comparison vs XDS). std::string observation_dump_path; - // If set, the integrated observations are written here as an unmerged MTZ (--export-unmerged), - // for aimless / pointless / careless and anything else that scales unmerged data itself - each - // rocking event summed into one full. - std::string unmerged_export_path; + // Write the integrated observations as _unmerged.mtz (--export-unmerged), for aimless / + // pointless / careless and anything else that scales unmerged data itself - each rocking event + // summed into one full. + bool export_unmerged = false; - // The same file with each partial written as its own row instead, for the reader to sum - // (--export-unmerged-partials). Independent of the above; a run can write both. - std::string unmerged_partials_export_path; + // The same as _unmerged_partials.mtz, with each partial written as its own row for the + // reader to sum (--export-unmerged-partials). Independent of the above; a run can write both. + bool export_unmerged_partials = false; // If set, after merging, validate the merged intensities against this atomic model (PDB): // compute R-free with an optimized bulk solvent and write 2Fo-Fc / Fo-Fc maps (--model). diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index 95f87899..7c685358 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -162,8 +162,8 @@ void print_usage() { std::cout << " -z, --reference-mtz Reference MTZ file" << std::endl; std::cout << " --reference-column