Wire BraggIntegrationEngine into the pipeline; deterministic prediction; integration_model API
Replace the free functions BraggIntegrate2D/ProfileIntegrate2D with the BraggIntegrationEngine (CPU/GPU) as the live integrator. - IndexAndRefine no longer holds the integrator: ProcessImage takes a per-worker BraggIntegrateFn callback (ProcessImage is called concurrently by the shared IndexAndRefine, so the stateful engine must not be a member). - WithoutFPGA/jfjoch_process: owns a GPU engine when a GPU is present, else CPU, and passes the GPU-resident preprocessed buffer so integration runs on-device. - AfterFPGA: forces CPU and integrates straight off the assembled CompressedImage via a templated per-pixel sampler - only the reflection-disk pixels are read, no whole-image copy (the FPGA host runs up to 36 GB/s). Sampler maps type min/max to INT32_MIN/INT32_MAX on read; special/saturation only, no +/-1 band. - Remove BraggIntegrate2D/ProfileIntegrate2D and their test; keep IntegratorMode. Prediction: buffer up to 20000 candidates but return the 10000 closest to the Ewald sphere (deterministic partial_sort on |dist_ewald|, hkl tiebreak) instead of the GPU atomic-fill order. Serialized output stays <=10000, so the frame transport headroom and its CBOR guard are unchanged. integration_model exposed via OpenAPI (bragg_integration_settings schema, /config/bragg_integration PUT/GET, added to jfjoch_settings and jfjoch_statistics) and the frontend (BraggIntegrationSettings dropdown). Regenerated C++/TS clients and redoc. Validated old-vs-new on all 18 /data/rotation_test crystals: indexing rate and space group bit-identical; ISa/CC identical on 16/18 (one improved, EcwtAL500 ISa 0.0->6.7); new CompressedImage-vs-buffer and GPU-vs-CPU parity tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,28 +5,54 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include "../../common/CompressedImage.h"
|
||||
#include "../../common/JFJochException.h"
|
||||
|
||||
using namespace bragg_engine;
|
||||
|
||||
namespace {
|
||||
|
||||
// The preprocessed buffer stores masked/bad pixels as INT32_MIN and saturated as INT32_MAX.
|
||||
// The engine reads pixels in the INT32_MIN(masked)/INT32_MAX(saturated) convention.
|
||||
inline bool valid(int32_t v) { return v != INT32_MIN && v != INT32_MAX; }
|
||||
|
||||
// Identity sampler over the preprocessed int32 buffer (already in that convention).
|
||||
struct BufferSampler {
|
||||
const int32_t *p;
|
||||
int32_t operator[](size_t i) const { return p[i]; }
|
||||
};
|
||||
|
||||
// Sampler over a raw detector image of pixel type T: masked pixels carry the type minimum, saturated
|
||||
// the type maximum (the FPGA image has no lossy-codec +/-1 band). Only the pixels actually read - the
|
||||
// reflection disks - are converted, so there is no whole-image pass.
|
||||
template <class T>
|
||||
struct ImageSampler {
|
||||
const T *p;
|
||||
int64_t special_value;
|
||||
int64_t saturation;
|
||||
int32_t operator[](size_t i) const {
|
||||
const int64_t v = p[i];
|
||||
if (v == special_value) return INT32_MIN;
|
||||
if (v == saturation) return INT32_MAX;
|
||||
return static_cast<int32_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
BraggIntegrationEngineCPU::BraggIntegrationEngineCPU(const DiffractionExperiment &experiment)
|
||||
: BraggIntegrationEngine(experiment) {}
|
||||
|
||||
std::vector<Reflection> BraggIntegrationEngineCPU::Run(const ImagePreprocessorBuffer &image,
|
||||
const std::vector<Reflection> &predicted,
|
||||
size_t npredicted, int64_t image_number) {
|
||||
template <class Sampler>
|
||||
std::vector<Reflection> BraggIntegrationEngineCPU::RunImpl(const Sampler &img,
|
||||
const std::vector<Reflection> &predicted,
|
||||
size_t npredicted, int64_t image_number) {
|
||||
std::vector<BraggFitResult> results(npredicted);
|
||||
if (image.size() != npixel || npredicted == 0)
|
||||
if (npredicted == 0)
|
||||
return Finalize(predicted, npredicted, results, image_number);
|
||||
|
||||
const int32_t *img = image.data();
|
||||
const int W = static_cast<int>(xpixel), H = static_cast<int>(ypixel);
|
||||
const bool do_clip = apply_bkg_clip && mode != IntegratorMode::BoxSum;
|
||||
|
||||
@@ -294,3 +320,43 @@ std::vector<Reflection> BraggIntegrationEngineCPU::Run(const ImagePreprocessorBu
|
||||
|
||||
return Finalize(predicted, npredicted, results, image_number);
|
||||
}
|
||||
|
||||
std::vector<Reflection> BraggIntegrationEngineCPU::Run(const ImagePreprocessorBuffer &image,
|
||||
const std::vector<Reflection> &predicted,
|
||||
size_t npredicted, int64_t image_number) {
|
||||
if (image.size() != npixel)
|
||||
return Finalize(predicted, npredicted, std::vector<BraggFitResult>(npredicted), image_number);
|
||||
return RunImpl(BufferSampler{image.data()}, predicted, npredicted, image_number);
|
||||
}
|
||||
|
||||
std::vector<Reflection> BraggIntegrationEngineCPU::Run(const CompressedImage &image,
|
||||
const std::vector<Reflection> &predicted,
|
||||
size_t npredicted, int64_t image_number) {
|
||||
if (image.GetWidth() * image.GetHeight() != npixel)
|
||||
return Finalize(predicted, npredicted, std::vector<BraggFitResult>(npredicted), image_number);
|
||||
|
||||
std::vector<uint8_t> scratch;
|
||||
const auto *ptr = image.GetUncompressedPtr(scratch);
|
||||
switch (image.GetMode()) {
|
||||
case CompressedImageMode::Int8:
|
||||
return RunImpl(ImageSampler<int8_t>{reinterpret_cast<const int8_t *>(ptr), INT8_MIN, INT8_MAX},
|
||||
predicted, npredicted, image_number);
|
||||
case CompressedImageMode::Int16:
|
||||
return RunImpl(ImageSampler<int16_t>{reinterpret_cast<const int16_t *>(ptr), INT16_MIN, INT16_MAX},
|
||||
predicted, npredicted, image_number);
|
||||
case CompressedImageMode::Int32:
|
||||
return RunImpl(ImageSampler<int32_t>{reinterpret_cast<const int32_t *>(ptr), INT32_MIN, INT32_MAX},
|
||||
predicted, npredicted, image_number);
|
||||
case CompressedImageMode::Uint8:
|
||||
return RunImpl(ImageSampler<uint8_t>{reinterpret_cast<const uint8_t *>(ptr), UINT8_MAX, UINT8_MAX},
|
||||
predicted, npredicted, image_number);
|
||||
case CompressedImageMode::Uint16:
|
||||
return RunImpl(ImageSampler<uint16_t>{reinterpret_cast<const uint16_t *>(ptr), UINT16_MAX, UINT16_MAX},
|
||||
predicted, npredicted, image_number);
|
||||
case CompressedImageMode::Uint32:
|
||||
return RunImpl(ImageSampler<uint32_t>{reinterpret_cast<const uint32_t *>(ptr), UINT32_MAX, UINT32_MAX},
|
||||
predicted, npredicted, image_number);
|
||||
default:
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Image mode not supported");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user