diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index 7ec824b9d..cbbc4b2eb 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -1577,11 +1577,21 @@ ProcessResult Rugnux::RunAllPasses(RugnuxObserver *observer) { // genuine de-novo demotion to a smaller primitive (the pseudo-symmetry case the second pass re-indexes // de novo to catch) has pass2 <= pass1 and is left untouched. if (!cancelled_ && prepass_result_ && pass1.consensus_cell && pass2.consensus_cell) { - const double v1 = gemmi::UnitCell(*pass1.consensus_cell).volume; - const double v2 = gemmi::UnitCell(*pass2.consensus_cell).volume; + // PRIMITIVE volumes, like the two guards in RunPipeline: a centred conventional cell is an + // exact integer multiple of its primitive one (C/I 2x, R 3x, F 4x), so two settings of the + // SAME lattice differ by that factor and comparing the conventional cells reads a mere + // change of setting as a supercell - which then forces pass 1's setting and, with it, its + // lower symmetry. Measured: every dataset this guard fired on was at an exact centring + // multiplicity, one of them an F-centred lattice held down to P1 at 3.99x. + const auto primitive_volume = [](const UnitCell &cell, std::optional centering) { + return std::abs(CrystalLattice(cell).ToPrimitive(centering.value_or('P')).CalcVolume()); + }; + const double v1 = primitive_volume(*pass1.consensus_cell, pass1.consensus_centering); + const double v2 = primitive_volume(*pass2.consensus_cell, pass2.consensus_centering); if (v1 > 0.0 && v2 > 1.5 * v1) { - logger.Info("Two-pass: second-pass cell volume {:.0f} A^3 is {:.2f}x pass-1 ({:.0f} A^3) - a " - "spurious supercell; re-running the second pass with pass-1's result forced", + logger.Info("Two-pass: second-pass primitive cell volume {:.0f} A^3 is {:.2f}x pass-1's " + "({:.0f} A^3) - a spurious supercell; re-running the second pass with " + "pass-1's result forced", v2, v2 / v1, v1); force_rotation_result_ = *prepass_result_; // Pass 1's result carries pass 1's goniometer, and the per-image path takes its angles @@ -1596,8 +1606,8 @@ ProcessResult Rugnux::RunAllPasses(RugnuxObserver *observer) { pass2.pass_count = 3; pass2.pass_decision = fmt::format( "post-refined geometry adopted, re-run with pass-1's lattice forced: the de-novo " - "cell volume {:.0f} A^3 was {:.2f}x pass-1's ({:.0f} A^3), a spurious supercell", - v2, v2 / v1, v1); + "primitive cell volume {:.0f} A^3 was {:.2f}x pass-1's ({:.0f} A^3), a spurious " + "supercell", v2, v2 / v1, v1); force_rotation_result_.reset(); } } @@ -3162,6 +3172,9 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b .crystal_system = rot->search_result.system }; result.rotation_lattice_found = true; + // The consensus cell below is this lattice's conventional cell, so this is the centring + // it is written in - the pair the volume comparisons in Run() need. + result.consensus_centering = rot->search_result.centering; } result.consensus_cell = indexer->GetConsensusUnitCell(); end_msg.unit_cell = result.consensus_cell; @@ -3293,6 +3306,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b } } result.consensus_cell = cand.conventional.GetUnitCell(); + result.consensus_centering = cand.centering; end_msg.unit_cell = result.consensus_cell; end_msg.rotation_lattice = end_msg.rotation_lattice->Multiply(reindex); end_msg.rotation_lattice_type = LatticeMessage{ .centering = cand.centering, @@ -4025,6 +4039,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b } const size_t dropped = sm.merged.size() - merged_p.size(); result.consensus_cell = cand->conventional.GetUnitCell(); + result.consensus_centering = cand->centering; end_msg.unit_cell = result.consensus_cell; end_msg.rotation_lattice = end_msg.rotation_lattice->Multiply(reduce); end_msg.rotation_lattice_type = LatticeMessage{ .centering = cand->centering, @@ -4089,6 +4104,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b reindex_hkl(r, *commit_reindex); } result.consensus_cell = commit_cell; + result.consensus_centering = commit_lattice_type->centering; end_msg.unit_cell = commit_cell; // Keep the lattice metadata (hence the master-file UB matrix and indexed-lattice // vectors) in the same conventional setting as the cell, reflections and space group. diff --git a/rugnux/Rugnux.h b/rugnux/Rugnux.h index 7c0019dc3..5e6865b55 100644 --- a/rugnux/Rugnux.h +++ b/rugnux/Rugnux.h @@ -218,6 +218,11 @@ struct ProcessResult { std::array used_detector_tilt_deg{}; double used_direct_beam_x_pxl = 0.0, used_direct_beam_y_pxl = 0.0; std::optional consensus_cell; + // The centring `consensus_cell` is written in; empty when nothing named one (the stills path + // averages per-image cells and has no lattice search behind them). A conventional cell says + // nothing about size on its own - a centred one is an exact integer multiple of its primitive + // cell - so two cells can only be compared for volume once this has brought both to primitive. + std::optional consensus_centering; bool rotation_lattice_found = false; MeanProcessingTime mean_processing_time{}; std::optional written_master_path;