Merge branch 'par-centre' into rc175
Build Packages / Create release (push) Successful in 15s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 3m35s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 8m12s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 10m35s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 15m11s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 20m28s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 20m36s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 21m55s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 22m43s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 24m29s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 18m36s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Failing after 28m11s
Build Packages / Generate python client (push) Successful in 46s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 19m29s
Build Packages / Build documentation (push) Successful in 1m28s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 18m30s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 17m38s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 18m10s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 19m53s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 15m29s
Build Packages / Unit tests (push) Successful in 1h21m1s
Build Packages / Create release (push) Successful in 15s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 3m35s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 8m12s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 10m35s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 15m11s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 20m28s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 20m36s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 21m55s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 22m43s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 24m29s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 18m36s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Failing after 28m11s
Build Packages / Generate python client (push) Successful in 46s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 19m29s
Build Packages / Build documentation (push) Successful in 1m28s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 18m30s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 17m38s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 18m10s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 19m53s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 15m29s
Build Packages / Unit tests (push) Successful in 1h21m1s
This commit is contained in:
+14
-13
@@ -483,12 +483,12 @@ void Rugnux::CollectSpeculativeProbe() {
|
||||
speculative_probe_.reset();
|
||||
}
|
||||
|
||||
std::vector<double> Rugnux::ExperimentKey(bool rotation_angles) const {
|
||||
std::vector<double> k{experiment_.GetBeamX_pxl(), experiment_.GetBeamY_pxl(),
|
||||
experiment_.GetDetectorDistance_mm(), experiment_.GetPoniRot1_rad(),
|
||||
experiment_.GetPoniRot2_rad(), experiment_.GetWavelength_A(),
|
||||
static_cast<double>(experiment_.GetMaxSpotCount())};
|
||||
if (const auto g = experiment_.GetGoniometer()) {
|
||||
std::vector<double> Rugnux::ExperimentKey(const DiffractionExperiment &e, bool rotation_angles) const {
|
||||
std::vector<double> k{e.GetBeamX_pxl(), e.GetBeamY_pxl(),
|
||||
e.GetDetectorDistance_mm(), e.GetPoniRot1_rad(),
|
||||
e.GetPoniRot2_rad(), e.GetWavelength_A(),
|
||||
static_cast<double>(e.GetMaxSpotCount())};
|
||||
if (const auto g = e.GetGoniometer()) {
|
||||
const Coord a = g->GetAxis();
|
||||
const auto helical = g->GetHelicalStep().value_or(Coord(-1, -1, -1));
|
||||
k.insert(k.end(), {1.0, a.x, a.y, a.z, helical.x, helical.y, helical.z});
|
||||
@@ -497,11 +497,11 @@ std::vector<double> Rugnux::ExperimentKey(bool rotation_angles) const {
|
||||
g->GetScreeningWedge().value_or(-1.0f)});
|
||||
} else
|
||||
k.push_back(0.0);
|
||||
if (const auto uc = experiment_.GetUnitCell())
|
||||
if (const auto uc = e.GetUnitCell())
|
||||
k.insert(k.end(), {1.0, uc->a, uc->b, uc->c, uc->alpha, uc->beta, uc->gamma});
|
||||
else
|
||||
k.push_back(0.0);
|
||||
const auto &x = experiment_.GetIndexingSettings();
|
||||
const auto &x = e.GetIndexingSettings();
|
||||
k.insert(k.end(), {x.GetTolerance(), x.GetFFT_MinUnitCell_A(), x.GetFFT_MaxUnitCell_A(),
|
||||
static_cast<double>(x.GetFFT_NumVectors()), x.GetFFT_HighResolution_A(),
|
||||
x.GetFFT_MinAngle_deg(), x.GetFFT_MaxAngle_deg(), x.GetUnitCellDistTolerance(),
|
||||
@@ -514,23 +514,24 @@ std::vector<double> Rugnux::ExperimentKey(bool rotation_angles) const {
|
||||
k.insert(k.end(), {f.signal_to_noise_threshold, static_cast<double>(f.photon_count_threshold),
|
||||
static_cast<double>(f.min_pix_per_spot.value_or(-1)), static_cast<double>(f.max_pix_per_spot),
|
||||
f.high_resolution_limit.value_or(-1.0f)});
|
||||
k.insert(k.end(), {static_cast<double>(experiment_.GetSpaceGroupOrP1().number),
|
||||
static_cast<double>(experiment_.GetImageNum())});
|
||||
k.insert(k.end(), {static_cast<double>(e.GetSpaceGroupOrP1().number),
|
||||
static_cast<double>(e.GetImageNum())});
|
||||
return k;
|
||||
}
|
||||
|
||||
std::vector<double> Rugnux::SpotFindingKey(const AzimuthalIntegrationMapping &mapping) const {
|
||||
std::vector<double> Rugnux::SpotFindingKey(const DiffractionExperiment &x,
|
||||
const AzimuthalIntegrationMapping &mapping) const {
|
||||
// Without the rotation angles: a frame's spots are found the same under any of them, and the angle
|
||||
// each spot carries is stamped again where a list is taken from the store (prefetch_spots). The
|
||||
// probes of the rotation-scale walk differ only there, so they find the spots of a frame once.
|
||||
std::vector<double> k = ExperimentKey(/*rotation_angles=*/false);
|
||||
std::vector<double> k = ExperimentKey(x, /*rotation_angles=*/false);
|
||||
const auto &f = config_.spot_finding;
|
||||
k.insert(k.end(), {static_cast<double>(f.enable), f.low_resolution_limit.value_or(-1.0f),
|
||||
f.cutoff_spot_count_low_res, f.high_res_gap_Q_recipA.value_or(-1.0f),
|
||||
f.ice_ring_width_Q_recipA, static_cast<double>(f.adaptive_threshold),
|
||||
f.false_pixels_per_frame, static_cast<double>(f.measured_ring_q_recipA.size())});
|
||||
k.insert(k.end(), f.measured_ring_q_recipA.begin(), f.measured_ring_q_recipA.end());
|
||||
k.insert(k.end(), {static_cast<double>(experiment_.IsDetectIceRings()),
|
||||
k.insert(k.end(), {static_cast<double>(x.IsDetectIceRings()),
|
||||
static_cast<double>(pixel_mask_.GetBinaryMaskChecksum()),
|
||||
static_cast<double>(mapping.GetPixelToBinChecksum()),
|
||||
static_cast<double>(mapping.GetCorrectionsChecksum())});
|
||||
|
||||
+11
-2
@@ -787,7 +787,12 @@ class Rugnux {
|
||||
// not evaluate it again. Shared with the copies the run makes of itself.
|
||||
std::shared_ptr<AzimuthalIntegrationGeometryCache> azint_geometry_ =
|
||||
std::make_shared<AzimuthalIntegrationGeometryCache>();
|
||||
[[nodiscard]] std::vector<double> SpotFindingKey(const AzimuthalIntegrationMapping &mapping) const;
|
||||
[[nodiscard]] std::vector<double> SpotFindingKey(const AzimuthalIntegrationMapping &mapping) const {
|
||||
return SpotFindingKey(experiment_, mapping);
|
||||
}
|
||||
// The same, for spots found at another experiment (a trial beam centre) against its mapping.
|
||||
[[nodiscard]] std::vector<double> SpotFindingKey(const DiffractionExperiment &x,
|
||||
const AzimuthalIntegrationMapping &mapping) const;
|
||||
void AddFirstPassMemo(const FirstPassMemo &memo);
|
||||
// The geometry walk's next indexing probe, started on a copy of the run as soon as the canonical
|
||||
// pass has post-refined - it depends on nothing that pass does afterwards - so that it runs beside
|
||||
@@ -809,7 +814,11 @@ class Rugnux {
|
||||
[[nodiscard]] std::vector<double> FirstPassInputKey() const;
|
||||
// The part of FirstPassInputKey that is the experiment and the spot-finding settings; without the
|
||||
// goniometer's angles where rotation_angles is false (SpotFindingKey).
|
||||
[[nodiscard]] std::vector<double> ExperimentKey(bool rotation_angles = true) const;
|
||||
[[nodiscard]] std::vector<double> ExperimentKey(bool rotation_angles = true) const {
|
||||
return ExperimentKey(experiment_, rotation_angles);
|
||||
}
|
||||
// The same, of another experiment beside the run's settings.
|
||||
[[nodiscard]] std::vector<double> ExperimentKey(const DiffractionExperiment &x, bool rotation_angles = true) const;
|
||||
// The file's detector distance, from before the first pass of the rotation two-pass: a pass whose
|
||||
// distance is still this one asks the post-refinement to test "the header distance is right"
|
||||
// (PostRefineSettings::distance_at_header); a pass that has walked off it does not.
|
||||
|
||||
+215
-22
@@ -88,6 +88,105 @@
|
||||
using namespace rugnux_internal;
|
||||
|
||||
namespace {
|
||||
// The spots of one first-pass frame. A frame's spots are a pure function of that frame and the
|
||||
// settings - the engine carries nothing from one image to the next - so it does not matter which
|
||||
// engine, or which thread, finds them.
|
||||
std::vector<SpotToSave> FindFirstPassSpots(JFJochReader &reader, const SpotFindingSettings &settings,
|
||||
const JFJochReaderDataset &dataset,
|
||||
MXAnalysisWithoutFPGA &analysis,
|
||||
AzimuthalIntegrationProfile &profile,
|
||||
JFJochReaderRawImage &img, int ordinal, int image_idx,
|
||||
Logger &logger) {
|
||||
std::vector<SpotToSave> spots;
|
||||
try {
|
||||
if (reader.ReadRawImage(image_idx, img)) {
|
||||
DataMessage m{};
|
||||
m.number = ordinal;
|
||||
m.original_number = image_idx;
|
||||
auto first_pass = settings;
|
||||
first_pass.indexing = false;
|
||||
first_pass.quick_integration = false;
|
||||
m.image = img.image;
|
||||
if (dataset.efficiency.size() > image_idx)
|
||||
m.image_collection_efficiency = dataset.efficiency[image_idx];
|
||||
analysis.Analyze(m, profile, first_pass);
|
||||
spots = std::move(m.spots);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
if (IsFatalResourceError(e)) throw;
|
||||
logger.Warning("First-pass spot read failed for image {}: {}", image_idx, e.what());
|
||||
}
|
||||
return spots;
|
||||
}
|
||||
|
||||
// First-pass spot finding on workers of its own, all on one card (`device`), for work that runs
|
||||
// beside the first pass. Each worker keeps its engine from one call to the next - building one
|
||||
// costs far more than a frame - and gives it back on that card.
|
||||
class SpotFindingWorkers {
|
||||
public:
|
||||
SpotFindingWorkers(JFJochReader &reader, const DiffractionExperiment &x,
|
||||
const AzimuthalIntegrationMapping &mapping, const PixelMask &mask,
|
||||
const SpotFindingSettings &settings, const JFJochReaderDataset &dataset,
|
||||
int start_image, int stride, size_t nworkers, int device)
|
||||
: reader(reader), x(x), mapping(mapping), mask(mask), settings(settings), dataset(dataset),
|
||||
start_image(start_image), stride(stride), device(device), engines(std::max<size_t>(nworkers, 1)) {}
|
||||
|
||||
~SpotFindingWorkers() {
|
||||
OnWorkers(engines.size(), [&](size_t t) { engines[t] = Engine{}; });
|
||||
}
|
||||
|
||||
// The spots of each ordinal, in the order given.
|
||||
std::vector<std::vector<SpotToSave>> Find(const std::vector<int> &ordinals) {
|
||||
std::vector<std::vector<SpotToSave>> found(ordinals.size());
|
||||
std::atomic<size_t> next{0};
|
||||
OnWorkers(std::min(engines.size(), ordinals.size()), [&](size_t t) {
|
||||
Engine &e = engines[t];
|
||||
if (!e.analysis) {
|
||||
e.indexer = std::make_unique<IndexAndRefine>(x, nullptr, /*retain_outcomes=*/false);
|
||||
e.analysis = std::make_unique<MXAnalysisWithoutFPGA>(x, mapping, mask, *e.indexer,
|
||||
/*enable_fused_adaptive_gpu=*/true);
|
||||
e.profile = std::make_unique<AzimuthalIntegrationProfile>(mapping);
|
||||
}
|
||||
for (size_t i = next.fetch_add(1); i < ordinals.size(); i = next.fetch_add(1))
|
||||
found[i] = FindFirstPassSpots(reader, settings, dataset, *e.analysis, *e.profile,
|
||||
e.raw_image, ordinals[i], start_image + ordinals[i] * stride,
|
||||
logger);
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Engine {
|
||||
// Before the analysis engine, which page-locks these bytes for its uploads.
|
||||
JFJochReaderRawImage raw_image;
|
||||
std::unique_ptr<IndexAndRefine> indexer;
|
||||
std::unique_ptr<MXAnalysisWithoutFPGA> analysis;
|
||||
std::unique_ptr<AzimuthalIntegrationProfile> profile;
|
||||
};
|
||||
|
||||
// fn(t) on n threads of its own, on the card.
|
||||
template <class Fn> void OnWorkers(size_t n, Fn fn) {
|
||||
std::vector<std::future<void>> futures;
|
||||
for (size_t t = 0; t < n; t++)
|
||||
futures.emplace_back(std::async(std::launch::async, [this, &fn, t] {
|
||||
pin_gpu(device);
|
||||
fn(t);
|
||||
}));
|
||||
for (auto &f : futures)
|
||||
f.get();
|
||||
}
|
||||
|
||||
JFJochReader &reader;
|
||||
const DiffractionExperiment &x;
|
||||
const AzimuthalIntegrationMapping &mapping;
|
||||
const PixelMask &mask;
|
||||
const SpotFindingSettings settings;
|
||||
const JFJochReaderDataset &dataset;
|
||||
const int start_image, stride, device;
|
||||
std::vector<Engine> engines;
|
||||
Logger logger{"Rugnux"};
|
||||
};
|
||||
|
||||
// Field by field, bitwise, for the spot memo's verification.
|
||||
bool SameSpots(const std::vector<SpotToSave> &a, const std::vector<SpotToSave> &b) {
|
||||
if (a.size() != b.size())
|
||||
@@ -248,27 +347,8 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) {
|
||||
// not matter which engine, or which thread, finds them.
|
||||
const auto find_spots = [&](MXAnalysisWithoutFPGA &analysis, AzimuthalIntegrationProfile &profile,
|
||||
JFJochReaderRawImage &img, int ordinal) {
|
||||
const int image_idx = start_image + ordinal * config_.stride;
|
||||
std::vector<SpotToSave> spots;
|
||||
try {
|
||||
if (reader_.ReadRawImage(image_idx, img)) {
|
||||
DataMessage m{};
|
||||
m.number = ordinal;
|
||||
m.original_number = image_idx;
|
||||
auto first_pass = config_.spot_finding;
|
||||
first_pass.indexing = false;
|
||||
first_pass.quick_integration = false;
|
||||
m.image = img.image;
|
||||
if (dataset->efficiency.size() > image_idx)
|
||||
m.image_collection_efficiency = dataset->efficiency[image_idx];
|
||||
analysis.Analyze(m, profile, first_pass);
|
||||
spots = std::move(m.spots);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
if (IsFatalResourceError(e)) throw;
|
||||
logger.Warning("First-pass spot read failed for image {}: {}", image_idx, e.what());
|
||||
}
|
||||
return spots;
|
||||
return FindFirstPassSpots(reader_, config_.spot_finding, *dataset, analysis, profile, img,
|
||||
ordinal, start_image + ordinal * config_.stride, logger);
|
||||
};
|
||||
|
||||
// Find the spots of every ordinal in the list that is not cached yet, on several workers, and
|
||||
@@ -1200,7 +1280,97 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) {
|
||||
std::vector<std::future<void>> futures; // last, so it is waited for before the rest goes
|
||||
};
|
||||
std::optional<ShortAxisSpeculation> short_spec;
|
||||
// The beam-centre check below runs a second first pass at the centre measured from the
|
||||
// background, and it reads the first one's answer only after that pass is over. So the spots at
|
||||
// the measured centre are found, and both schemes fed and indexed, here beside the file centre's
|
||||
// pass - on a copy of the experiment at that centre, with workers, a mapping and a spot list of
|
||||
// their own - and taken at the check's place only if what they read is still what the run has
|
||||
// there (the experiment with its rotation angles, SpotFindingKey at the measured centre, the spot
|
||||
// budget); otherwise that pass is run there as before. The schemes are scored there in either
|
||||
// case, on the run's own indexer.
|
||||
//
|
||||
// Only with a second GPU, whose card its spot finding takes: on one card the two passes share the
|
||||
// card and the reader, and measured, running them side by side saved nothing. There the check
|
||||
// runs its pass after the first, as it always did.
|
||||
struct CentreSpeculation {
|
||||
std::vector<double> experiment_key;
|
||||
std::vector<double> key;
|
||||
size_t spots_per_image = 0;
|
||||
std::unique_ptr<DiffractionExperiment> experiment;
|
||||
std::unique_ptr<PixelMask> mask;
|
||||
std::unique_ptr<AzimuthalIntegrationMapping> mapping;
|
||||
std::map<int, std::vector<SpotToSave>> spots;
|
||||
SchemeIndexers ris;
|
||||
std::future<void> done; // last, so it is waited for before the rest goes
|
||||
};
|
||||
std::optional<CentreSpeculation> centre_spec;
|
||||
const auto start_centre_speculation = [&] {
|
||||
if (get_gpu_count() < 2)
|
||||
return;
|
||||
JoinBeamCenterCapture();
|
||||
if (cancelled_ || !config_.beam_center_check || !background_center_)
|
||||
return;
|
||||
auto &cs = centre_spec.emplace();
|
||||
cs.experiment = std::make_unique<DiffractionExperiment>(experiment_);
|
||||
cs.experiment->BeamX_pxl(background_center_->beam_x_pxl).BeamY_pxl(background_center_->beam_y_pxl);
|
||||
cs.mask = std::make_unique<PixelMask>(pixel_mask_);
|
||||
cs.mapping = std::make_unique<AzimuthalIntegrationMapping>(*cs.experiment, *cs.mask, *azint_geometry_);
|
||||
cs.experiment_key = ExperimentKey(*cs.experiment);
|
||||
cs.key = SpotFindingKey(*cs.experiment, *cs.mapping);
|
||||
cs.spots_per_image = first_pass_spots_per_image;
|
||||
{
|
||||
// What an earlier pass already found at this centre under these settings.
|
||||
std::lock_guard lock(first_pass_spots_->m);
|
||||
for (const auto &[k, spots] : first_pass_spots_->entries)
|
||||
if (k == cs.key)
|
||||
cs.spots = spots;
|
||||
}
|
||||
// At this pass's angles, as prefetch_spots takes them.
|
||||
const DiffractionGeometry spot_geometry = cs.experiment->GetDiffractionGeometry();
|
||||
for (auto &[ordinal, spots] : cs.spots)
|
||||
StampSpotAngles(spots, spot_geometry);
|
||||
cs.done = std::async(std::launch::async, [this, &cs, &schemes, &validation, &indexer_pool, &run_indexing,
|
||||
settings = config_.spot_finding, dataset, start_image,
|
||||
nworkers = engines.size()] {
|
||||
SpotFindingWorkers workers(reader_, *cs.experiment, *cs.mapping, *cs.mask, settings, *dataset,
|
||||
start_image, config_.stride, nworkers, /*device=*/1);
|
||||
const auto find = [&](const std::vector<int> &ordinals) {
|
||||
std::vector<int> wanted;
|
||||
for (const int o : ordinals)
|
||||
if (!cs.spots.contains(o))
|
||||
wanted.push_back(o);
|
||||
auto found = workers.Find(wanted);
|
||||
for (size_t i = 0; i < wanted.size(); i++)
|
||||
cs.spots.emplace(wanted[i], std::move(found[i]));
|
||||
};
|
||||
// As feed_scheme, at the measured centre.
|
||||
const auto gonio = cs.experiment->GetGoniometer();
|
||||
for (const auto &[name, ordinals] : schemes) {
|
||||
auto ri = std::make_unique<RotationIndexer>(*cs.experiment, *indexer_pool);
|
||||
ri->MaxSpotsPerImage(cs.spots_per_image);
|
||||
ri->VerifyMemo(config_.verify_first_pass_memo);
|
||||
for (size_t i = 0; i < ordinals.size(); i++) {
|
||||
if (cancelled_ || ri->AccumulationFull())
|
||||
break;
|
||||
if (!cs.spots.contains(ordinals[i]))
|
||||
find({ordinals.begin() + i,
|
||||
ordinals.begin() + std::min(ordinals.size(), i + SPOT_PREFETCH_CHUNK)});
|
||||
std::optional<float> angle;
|
||||
if (gonio)
|
||||
angle = gonio->GetAngle_deg(static_cast<float>(ordinals[i])) + gonio->GetWedge_deg() / 2.0f;
|
||||
ri->ProcessImage(ordinals[i], cs.spots.at(ordinals[i]), angle);
|
||||
}
|
||||
cs.ris.push_back(std::move(ri));
|
||||
}
|
||||
find(validation);
|
||||
std::vector<RotationIndexer *> rp;
|
||||
for (const auto &ri : cs.ris)
|
||||
rp.push_back(ri.get());
|
||||
run_indexing(rp);
|
||||
});
|
||||
};
|
||||
FirstPass best = pick_best(*indexer_pool, *indexer, [&] {
|
||||
start_centre_speculation();
|
||||
if (cancelled_ || experiment_.GetUnitCell().has_value())
|
||||
return;
|
||||
auto &sp = short_spec.emplace();
|
||||
@@ -1322,7 +1492,30 @@ bool Rugnux::FirstPassRotationIndexing(PipelineLocals &p) {
|
||||
{
|
||||
const int majority = static_cast<int>(validation.size()) / 2;
|
||||
try_beam_center(measured_x, measured_y);
|
||||
FirstPass alt = pick_best(*indexer_pool, *indexer);
|
||||
FirstPass alt;
|
||||
if (centre_spec)
|
||||
centre_spec->done.get();
|
||||
if (centre_spec && centre_spec->experiment_key == ExperimentKey()
|
||||
&& centre_spec->key == SpotFindingKey(*spot_mapping)
|
||||
&& centre_spec->spots_per_image == first_pass_spots_per_image) {
|
||||
// What pick_best would have found and fed here: hand the spots to the run's cache and
|
||||
// its memo as prefetch_spots does, and score the schemes on the run's own indexer.
|
||||
spot_cache = centre_spec->spots;
|
||||
{
|
||||
std::lock_guard lock(first_pass_spots_->m);
|
||||
auto &entries = first_pass_spots_->entries;
|
||||
if (std::none_of(entries.begin(), entries.end(),
|
||||
[&](const auto &e) { return e.first == centre_spec->key; })) {
|
||||
constexpr size_t MAX_SPOT_KEYS = 8;
|
||||
if (entries.size() == MAX_SPOT_KEYS)
|
||||
entries.erase(entries.begin());
|
||||
entries.emplace_back(centre_spec->key, centre_spec->spots);
|
||||
}
|
||||
}
|
||||
alt = score_schemes(centre_spec->ris, *indexer);
|
||||
} else
|
||||
alt = pick_best(*indexer_pool, *indexer);
|
||||
centre_spec.reset();
|
||||
const bool header_indexes = best.result.has_value() && best.score > majority;
|
||||
bool measured_indexes = alt.result.has_value() && alt.score > majority;
|
||||
if (!header_indexes) {
|
||||
|
||||
Reference in New Issue
Block a user