Build Packages / Build documentation (push) Successful in 46s
Build Packages / Create release (push) Successful in 8s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 5m25s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 9m46s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 13m25s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 16m41s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 23m46s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 25m37s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 24m31s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 28m9s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 20m47s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 26m21s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 27m36s
Build Packages / Generate python client (push) Successful in 31s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 29m27s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 31m46s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 25m22s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 30m36s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 31m49s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 23m57s
Build Packages / Unit tests (push) Successful in 1h47m52s
Build Packages / Validation Battery (push) Successful in 44m55s
* Rugnux: Rotation processing recovers most of the speed lost since 1.0.0-rc.180; live processing in jfjoch_broker finishes sooner. * Rugnux: More reliable rotation processing and indexing, including small molecules and supercells. * Rugnux: More robust serial data and model refinement; molecular replacement handles multi-chain models. * jfjoch_broker (experimental): Live serial processing keeps the detector geometry fixed for the whole run. * jfjoch_broker: Pixel sum without an FPGA, a mask bit for inactive pixels, and several stability fixes. * jfjoch_broker: Rotation indexing and quick integration moved to rugnux live mode; their API settings are deprecated and ignored. * jfjoch_broker, Rugnux: Azimuthal integration can use logarithmic (log10) Q bins or a given number of bins, set from the API, the web interface and the Rugnux command line. * jfjoch_viewer: Build and display fixes; the README is rewritten. Reviewed-on: #95 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
164 lines
5.8 KiB
C++
164 lines
5.8 KiB
C++
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "MXAnalysisAfterFPGA.h"
|
|
|
|
#include <span>
|
|
|
|
#include "spot_finding/DetModuleSpotFinder_cpu.h"
|
|
#include "../common/CUDAWrapper.h"
|
|
#include "../common/JFJochException.h"
|
|
#include "spot_finding/SpotUtils.h"
|
|
|
|
double stddev(const std::vector<float> &v) {
|
|
if (v.size() <= 1)
|
|
return 0.0;
|
|
|
|
double mean = 0.0f;
|
|
|
|
for (const auto &i: v)
|
|
mean += i;
|
|
mean /= v.size();
|
|
|
|
double stddev = 0.0f;
|
|
for (const auto &i: v)
|
|
stddev += (i - mean) * (i - mean);
|
|
|
|
return sqrt(stddev / (v.size() - 1));
|
|
}
|
|
|
|
|
|
MXAnalysisAfterFPGA::MXAnalysisAfterFPGA(const DiffractionExperiment &in_experiment,
|
|
const AzimuthalIntegrationMapping &in_integration,
|
|
IndexAndRefine &indexer)
|
|
: experiment(in_experiment),
|
|
integration(in_integration),
|
|
indexer(indexer) {
|
|
if (experiment.IsSpotFindingEnabled())
|
|
find_spots = true;
|
|
|
|
if (experiment.GetAzimuthalIntegrationSettings().IsForceCPUinFPGAWorkflow())
|
|
cpu_azint = std::make_unique<AzIntEngineCPU>(integration);
|
|
}
|
|
|
|
void MXAnalysisAfterFPGA::RunAzimuthalIntegration(const void *image, AzimuthalIntegrationProfile &profile) {
|
|
if (!cpu_azint)
|
|
return;
|
|
|
|
const size_t npixel = experiment.GetPixelsNum();
|
|
|
|
auto run = [&](auto tag) {
|
|
using pixel_t = decltype(tag);
|
|
cpu_azint->RunAzint(std::span<const pixel_t>(static_cast<const pixel_t *>(image), npixel), profile);
|
|
};
|
|
|
|
switch (experiment.GetByteDepthImage()) {
|
|
case 1:
|
|
experiment.IsPixelSigned() ? run(int8_t{}) : run(uint8_t{});
|
|
break;
|
|
case 2:
|
|
experiment.IsPixelSigned() ? run(int16_t{}) : run(uint16_t{});
|
|
break;
|
|
case 4:
|
|
experiment.IsPixelSigned() ? run(int32_t{}) : run(uint32_t{});
|
|
break;
|
|
default:
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pixel depth unsupported");
|
|
}
|
|
}
|
|
|
|
void MXAnalysisAfterFPGA::ReadFromFPGA(const DeviceOutput *output, const SpotFindingSettings &settings, size_t module_number) {
|
|
if (state == State::Disabled || !find_spots || !settings.enable) {
|
|
state = State::Disabled;
|
|
} else {
|
|
const auto t0 = std::chrono::steady_clock::now();
|
|
StrongPixelSet strong_pixel_set;
|
|
strong_pixel_set.ReadFPGAOutput(experiment, *output);
|
|
strong_pixel_set.FindSpots(experiment, settings, spots, module_number);
|
|
const auto t1 = std::chrono::steady_clock::now();
|
|
spot_finding_time_total += (t1 - t0);
|
|
spot_finding_timing_active = true;
|
|
|
|
state = State::Enabled;
|
|
}
|
|
}
|
|
|
|
void MXAnalysisAfterFPGA::ReadFromCPU(DeviceOutput *output, const SpotFindingSettings &settings, size_t module_number) {
|
|
std::unique_lock ul(read_from_cpu_mutex);
|
|
|
|
if (state == State::Disabled || !find_spots || !settings.enable) {
|
|
state = State::Disabled;
|
|
} else {
|
|
const auto t0 = std::chrono::steady_clock::now();
|
|
|
|
state = State::Enabled;
|
|
|
|
std::vector<float> d_map(RAW_MODULE_SIZE);
|
|
|
|
experiment.CalcSpotFinderResolutionMap(d_map.data(), module_number);
|
|
|
|
arr_mean.resize(RAW_MODULE_SIZE);
|
|
arr_sttdev.resize(RAW_MODULE_SIZE);
|
|
arr_valid_count.resize(RAW_MODULE_SIZE);
|
|
arr_strong_pixel.resize(RAW_MODULE_SIZE);
|
|
|
|
if (experiment.GetByteDepthImage() == 2)
|
|
FindSpots(*output,
|
|
settings,
|
|
d_map.data(),
|
|
arr_mean.data(),
|
|
arr_sttdev.data(),
|
|
arr_valid_count.data(),
|
|
arr_strong_pixel.data());
|
|
else if (experiment.GetByteDepthImage() == 4)
|
|
FindSpots<int32_t>(*output,
|
|
settings,
|
|
d_map.data(),
|
|
arr_mean.data(),
|
|
arr_sttdev.data(),
|
|
arr_valid_count.data(),
|
|
arr_strong_pixel.data());
|
|
else if (experiment.GetByteDepthImage() == 1)
|
|
FindSpots<int8_t>(*output,
|
|
settings,
|
|
d_map.data(),
|
|
arr_mean.data(),
|
|
arr_sttdev.data(),
|
|
arr_valid_count.data(),
|
|
arr_strong_pixel.data());
|
|
|
|
StrongPixelSet strong_pixel_set;
|
|
strong_pixel_set.ReadFPGAOutput(experiment, *output);
|
|
strong_pixel_set.FindSpots(experiment, settings, spots, module_number);
|
|
|
|
const auto t1 = std::chrono::steady_clock::now();
|
|
spot_finding_time_total += (t1 - t0);
|
|
spot_finding_timing_active = true;
|
|
}
|
|
}
|
|
|
|
void MXAnalysisAfterFPGA::Process(DataMessage &message, const SpotFindingSettings& spot_finding_settings) {
|
|
if (find_spots && (state == State::Enabled)) {
|
|
const auto t0 = std::chrono::steady_clock::now();
|
|
SpotAnalyze(experiment, spot_finding_settings, spots, message);
|
|
const auto t1 = std::chrono::steady_clock::now();
|
|
spot_finding_time_total += (t1 - t0);
|
|
|
|
// Indexing only: integration is rugnux's (live or offline), not the FPGA receiver's.
|
|
if (spot_finding_settings.indexing)
|
|
indexer.IndexFrameOnly(message, spot_finding_settings);
|
|
}
|
|
|
|
if (spot_finding_timing_active) {
|
|
// total spot-finding time for the whole image
|
|
message.spot_finding_time_s = spot_finding_time_total.count() / 1e6;
|
|
|
|
// report/store ms here
|
|
spot_finding_time_total = std::chrono::duration<double, std::micro>{0.0};
|
|
spot_finding_timing_active = false;
|
|
}
|
|
|
|
spots.clear();
|
|
state = State::Idle;
|
|
}
|