diff --git a/image_analysis/structure_refinement/Refinement.cpp b/image_analysis/structure_refinement/Refinement.cpp index 96c274e52..7b11909a4 100644 --- a/image_analysis/structure_refinement/Refinement.cpp +++ b/image_analysis/structure_refinement/Refinement.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1133,8 +1134,8 @@ TrialOutcome RunTrial(TrialSlot slot, const std::vector &refinable, const s return out; } -// The trials (w, adp_w) side by side: trial i in slot i % slots, each slot's trials in turn on a thread of its -// own (on its GPU), returned in the order given. Every trial starts from pos0 and b0 and takes nothing from the +// The trials (w, adp_w) side by side, the longest to the slots first and each slot's trials in turn on a thread of +// its own (on its GPU), returned in the order given. Every trial starts from pos0 and b0 and takes nothing from the // one before it in its slot, so the outcome is the same for any number of slots. std::vector RunTrials(const std::vector &trial_slots, const std::vector> &trials, @@ -1147,13 +1148,19 @@ std::vector RunTrials(const std::vector &trial_slots, const size_t running = std::min(trial_slots.size(), trials.size()); for (size_t s = 0; s < running; s++) trial_slots[s].ctx->nthreads = std::max(1, settings.nthreads / running); + // The trials that refine B are the longest: they go to the slots first, so that two of them share a slot (or a + // card) only where the slots are fewer than they are. + std::vector order(trials.size()); + std::iota(order.begin(), order.end(), 0); + std::stable_sort(order.begin(), order.end(), + [&](size_t a, size_t b) { return (trials[a].second > 0) > (trials[b].second > 0); }); auto run_slot = [&](size_t s) { try { #ifdef JFJOCH_USE_CUDA const ScopedGpuPin pin(trial_slots[s].ctx->device >= 0 ? trial_slots[s].ctx->device : get_gpu()); #endif - for (size_t i = s; i < trials.size(); i += trial_slots.size()) - if (!trial_slots[s].ctx->Cancelled()) + for (size_t j = s; j < trials.size(); j += trial_slots.size()) + if (const size_t i = order[j]; !trial_slots[s].ctx->Cancelled()) out[i] = RunTrial(trial_slots[s], refinable, pos0, b0, fit, trials[i].first, trials[i].second, cycles, settings); } catch (...) { @@ -1416,6 +1423,10 @@ RefinementResult RefineModel(const gemmi::Structure &placed, const std::vector from_best; + bool state_is_best = true; for (int step = 0; step < settings.max_steps && state.ran && !ctx.Cancelled(); step++) { const double w = state.trial.weight, adp = state.trial.adp_weight; std::vector> trials = {{w, adp}}; @@ -1429,6 +1440,8 @@ RefinementResult RefineModel(const gemmi::Structure &placed, const std::vector> trials; for (double f : settings.final_ladder) if (w * f <= w_max) trials.emplace_back(w * f, adp); - outcomes = RunTrials(trial_slots, trials, refinable, state.pos, state.b, &state.fit, settings.macro_cycles, - settings); + outcomes.assign(trials.size(), TrialOutcome()); + std::vector> missing; + for (size_t i = 0; i < trials.size(); i++) { + for (const TrialOutcome &o : from_best) + if (o.ran && o.trial.weight == trials[i].first && o.trial.adp_weight == trials[i].second) + outcomes[i] = o; + if (!outcomes[i].ran) + missing.push_back(trials[i]); + } + std::vector ran = RunTrials(trial_slots, missing, refinable, state.pos, state.b, &state.fit, + settings.macro_cycles, settings); + for (size_t i = 0, m = 0; i < trials.size(); i++) + if (!outcomes[i].ran) + outcomes[i] = std::move(ran[m++]); k = choose(outcomes, "final"); + // The main slot's fit as it stands where its last rung ran on it, for the maps' Refit() to start from + // (the solvent grid and the likelihood's D) whether the rung ran or was taken from the step. + const size_t main_last = (trials.size() - 1) / trial_slots.size() * trial_slots.size(); + if (outcomes[main_last].ran) + ctx.Restore(outcomes[main_last].fit); if (k >= 0 && displaces(outcomes[k].trial, state.trial)) state = std::move(outcomes[k]); }