From ec3f2ebb800e7e86d2fd00fcab5886407e59b857 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Thu, 12 Nov 2026 07:53:25 +0100 Subject: [PATCH] rugnux: joint sweeps - only a sweep that failed is given the crystal's cell; relations measured in parallel The previous commit gave the first measured sweep's cell to every sweep after it. On a crystal of a ~600 A cell with a pseudo-centring that moved sweeps which index well de novo into another setting or centring (two of twenty lost, two gained: still 13 joined), so the cell now goes only to a sweep that found no lattice of the crystal on its own - it merged nothing, or nothing that correlates with the reference on any axes - and that sweep is processed once more given the shared-cell target's cell. A space group given as well (-S) was tried and refused by the metric check on such sweeps. The isomorphism ratios and the held-out gains of the sweeps are independent of each other and are now measured at once, one task per sweep (the held-out merge of the joined sweeps still built once). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK --- rugnux/RugnuxJointSweeps.cpp | 24 +++++++++++++++++++----- rugnux/rugnux_cli.cpp | 26 +++++++++----------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/rugnux/RugnuxJointSweeps.cpp b/rugnux/RugnuxJointSweeps.cpp index 221893b1e..c6a6253e0 100644 --- a/rugnux/RugnuxJointSweeps.cpp +++ b/rugnux/RugnuxJointSweeps.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -261,9 +262,13 @@ std::vector MeasureHeldOutGains(const std::vector out; + // ... and each sweep against it on its own, all of them at once. + std::vector> gains; for (size_t i = 0; i < ds.size(); ++i) - out.push_back(measure(*ds[i], cob_ds[i])); + gains.push_back(std::async(std::launch::async, [&measure, &ds, &cob_ds, i] { return measure(*ds[i], cob_ds[i]); })); + std::vector out; + for (auto &g : gains) + out.push_back(g.get()); return out; } @@ -434,7 +439,8 @@ JointSweepRelation RelateJointSweeps(const std::vector &sweeps laws_mark.Stop(); TimingMark iso_mark(logger, "joint sweeps: isomorphism"); - std::vector doubtful; + // Each sweep's ratio is measured on its own, all of them at once. + std::vector> isomorphism(sweeps.size()); for (size_t k = 0; k < sweeps.size(); ++k) { JointSweepRelation::Sweep &s = out.sweeps[k]; if (static_cast(k) == out.reference || !s.decision.empty()) @@ -445,8 +451,16 @@ JointSweepRelation RelateJointSweeps(const std::vector &sweeps "(best CC {:.3f})", s.cc); continue; } - const SweepIsomorphism iso = MeasureSweepIsomorphism(sweeps[k].fulls, s.cob, ref.fulls, - gemmi::Op::identity(), ref.space_group); + isomorphism[k] = std::async(std::launch::async, [&sweeps, &ref, cob = s.cob, k] { + return MeasureSweepIsomorphism(sweeps[k].fulls, cob, ref.fulls, gemmi::Op::identity(), ref.space_group); + }); + } + std::vector doubtful; + for (size_t k = 0; k < sweeps.size(); ++k) { + if (!isomorphism[k].valid()) + continue; + JointSweepRelation::Sweep &s = out.sweeps[k]; + const SweepIsomorphism iso = isomorphism[k].get(); s.iso_ratio = iso.ratio; s.iso_se = iso.se; s.iso_n = iso.n; diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index 87cc2278b..13589b719 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -4152,21 +4152,10 @@ int RugnuxCli::RunJointSweeps(const RugnuxSweepPlan &plan) { }; std::vector members; std::vector inputs; - // The sweeps are of one crystal: once one has merged with a measured accuracy (its error model resolved - // an ISa, as the reference is chosen), its cell is given to the sweeps after it as a reference cell (-C), - // which de novo indexing of one sweep can otherwise miss on a large cell - and a lattice it misses costs - // more than a whole run before it is found to merge nothing. Where -C was given, every sweep has it. - std::optional crystal_cell; - std::vector cell_given(plan.sweeps.size(), false); for (size_t k = 0; k < plan.sweeps.size(); k++) { logger.Info("Sweep {} of {}: {}, processed on its own", k + 1, plan.sweeps.size(), plan.sweeps[k].label); - cell_given[k] = crystal_cell.has_value() || fixed_reference_unit_cell.has_value(); - members.push_back(run_member(k, std::nullopt, crystal_cell, "")); + members.push_back(run_member(k, std::nullopt, std::nullopt, "")); inputs.push_back(plan.sweeps[k].input); - if (!cell_given[k] && members[k].isa > 0.0) { - crystal_cell = members[k].cell; - logger.Info("Sweep {}: its cell is given to the sweeps after it", k + 1); - } } // One cell for the crystal (SharedCellTarget): a sweep whose own cell disagrees with it - joined or not @@ -4177,12 +4166,15 @@ int RugnuxCli::RunJointSweeps(const RugnuxSweepPlan &plan) { { const JointSweepRelation &first = relation; const int target = SharedCellTarget(members, first); - // A sweep processed before any cell was given that found no lattice of this crystal - it merged - // nothing, or nothing that correlates with the reference on any axes - is processed again given the - // target sweep's cell. Its join is then decided on its intensities, as every sweep's is. - for (size_t k = 0; target >= 0 && k < members.size(); ++k) { + // A sweep that found no lattice of this crystal on its own - it merged nothing, or nothing that + // correlates with the reference on any axes - is processed again given the target sweep's cell (-C): + // de novo indexing of one sweep can miss a large cell that the crystal's other sweeps have measured. + // Its join is then decided on its intensities, as every sweep's is. Only a sweep that failed is given + // the cell: given to every sweep from the start, it moved sweeps that index well de novo into another + // setting or centring. Where -C was given, every sweep already had a cell. + for (size_t k = 0; target >= 0 && !fixed_reference_unit_cell && k < members.size(); ++k) { const std::string &decision = first.sweeps[k].decision; - if (cell_given[k] || (decision != "no merge" && decision != "no axes")) + if (decision != "no merge" && decision != "no axes") continue; logger.Info("Sweep {}: {}; processed again given sweep {}'s cell", k + 1, first.sweeps[k].why_left_out, target + 1);