Files
Jungfraujoch/common/AzimuthalIntegrationSettings.h
leonarski_fandClaude Opus 5.5 8d1157f711 Azimuthal integration: review follow-ups for the Q binning
The low-Q headroom check and the detector-edge high Q are floored by the linear
spacing only where one was given, and by the smallest allowed Q otherwise, instead
of by a spacing log10 bins do not use. GetQSpacing_recipA says that under log10 it
means nothing about the bins. GetQBinEdges_recipA, which only the tests used, is
dropped: the tests state the edges from the documented formula. A test pins the
far-edge clamp of linear-by-spacing bins on a two-row setting. The broker docs note
that a GET carries q_bins and no q_spacing once bins are configured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK
2026-11-10 16:55:48 +01:00

92 lines
4.6 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include "JFJochMath.h"
#include <optional>
#include <cstdint>
#include <vector>
// How the Q bins are laid out between low and high Q: equal steps in Q, or equal steps in log10(Q) -
// fine bins at low Q and coarse ones at high Q, for scattering whose features are at low Q.
enum class QBinSpacing {Linear, Log10};
class AzimuthalIntegrationSettings {
constexpr static float minQ_recipA = 1e-5;
constexpr static float maxQ_recipA = 10.0;
bool solid_angle_correction = true;
bool polarization_correction = true;
// Requested upper q limit. Unset means "as far as the detector reaches": DiffractionExperiment
// resolves it from the geometry (ResolveHighQ) whenever it hands these settings out, so
// high_q_recipA below - what the bins are built from - is always a concrete number. Not clipping
// detection at an arbitrary default matters for the adaptive spot finder, which bins pixels through
// this same q range and cannot see a pixel that falls outside it.
std::optional<float> requested_high_q_recipA;
float high_q_recipA = 5.0;
float low_q_recipA = 0.1;
float bkg_estimate_high_q_recipA = 2.0f * PI / 3.0;
float bkg_estimate_low_q_recipA = 2.0f * PI / 5.0;
QBinSpacing q_bin_spacing = QBinSpacing::Linear;
// Linear binning is given either by its Q spacing or by its number of Q bins; log10 binning only
// by the number of bins. Unset number: the spacing sets the bins.
float q_spacing = 0.01;
std::optional<int32_t> requested_q_bins;
// Width of a bin in the variable the bins are equally spaced in: Q, or log10(Q).
float bin_step = 0.01;
int32_t azim_bins = 1;
// Compute azimuthal integration on the CPU instead of the FPGA during the FPGA
// acquisition workflow. Lifts the FPGA bin-count limit and adds standard-deviation output.
bool force_cpu_in_fpga_workflow = false;
int32_t q_bins= 0;
int32_t total_bins = 0;
void UpdateBinCount();
// Where q falls on the bin scale: bin i spans [i, i + 1).
[[nodiscard]] float BinCoordinate(float q) const;
public:
AzimuthalIntegrationSettings();
AzimuthalIntegrationSettings& SolidAngleCorrection(bool input);
AzimuthalIntegrationSettings& PolarizationCorrection(bool input);
AzimuthalIntegrationSettings& QRange_recipA(float low, std::optional<float> high);
// Substitute the detector's own maximum q for an unset high q. No-op if one was requested.
void ResolveHighQ(float detector_max_q_recipA);
// Linear binning with this Q spacing.
AzimuthalIntegrationSettings& QSpacing_recipA(float input);
// Linear: exactly one of spacing and bin count may be given (neither keeps the current spacing).
// Log10: the bin count is required and a spacing is refused.
AzimuthalIntegrationSettings& QBinning(QBinSpacing type, std::optional<float> spacing,
std::optional<int32_t> q_bin_count);
AzimuthalIntegrationSettings& BkgEstimateQRange_recipA(float low, float high);
AzimuthalIntegrationSettings& AzimuthalBinCount(int32_t input);
AzimuthalIntegrationSettings& ForceCPUinFPGAWorkflow(bool input);
[[nodiscard]] bool IsSolidAngleCorrection() const;
[[nodiscard]] bool IsPolarizationCorrection() const;
[[nodiscard]] float GetHighQ_recipA() const;
[[nodiscard]] std::optional<float> GetRequestedHighQ_recipA() const;
[[nodiscard]] float GetLowQ_recipA() const;
// The width of a linear bin (as given, or as the bin count makes it). Under log10 binning it is the
// last linear spacing and means nothing about the bins - use GetQBinCentres_recipA.
[[nodiscard]] float GetQSpacing_recipA() const;
[[nodiscard]] QBinSpacing GetQBinSpacing() const;
// The spacing and the bin count as they were given; one of them is unset.
[[nodiscard]] std::optional<float> GetRequestedQSpacing_recipA() const;
[[nodiscard]] std::optional<int32_t> GetRequestedQBinCount() const;
// Centre of every Q bin: the mid-point of its edges on the bin scale.
[[nodiscard]] std::vector<double> GetQBinCentres_recipA() const;
[[nodiscard]] int32_t GetBinCount() const;
[[nodiscard]] int32_t GetQBinCount() const;
[[nodiscard]] int32_t GetAzimuthalBinCount() const;
[[nodiscard]] bool IsForceCPUinFPGAWorkflow() const;
[[nodiscard]] float GetBkgEstimateLowQ_recipA() const;
[[nodiscard]] float GetBkgEstimateHighQ_recipA() const;
[[nodiscard]] uint16_t QToBin(float q) const;
[[nodiscard]] uint16_t GetBin(float q, float phi_deg) const;
};