Files
Jungfraujoch/common/DiffractionExperiment.cpp
T
leonarski_fandClaude Opus 5 07607d3d47 Keep the online reflection cap where the transport can carry it
Raising the per-image reflection limit to 65536 for offline reprocessing also
raised the image-buffer headroom derived from it, and that headroom divides a
FIXED total buffer - so every slot grew from compressed+4 MB to compressed+16.7
MB and the receiver's slot count, i.e. how much of a burst it can absorb, fell by
about three. Online never needed the raised limit: measured on three serial
stills datasets the worst frame predicts 1380 reflections, 14% of even the old
cap.

So split them, the same way the geometry refinement's stopping rule is split:
online keeps the transport-sized 10000, offline gets the full 65536, and the
buffer headroom derives from the online one. Both still come from BraggPrediction
so the cap, the prediction and the headroom cannot drift apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 22:49:16 +02:00

1754 lines
59 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <algorithm>
#include <cmath>
#include <utility>
#include "NetworkAddressConvert.h"
#include "JFJochCompressor.h" // For ZSTD_USE_JFJOCH_RLE
#include "DiffractionExperiment.h"
#include "../image_analysis/bragg_prediction/BraggPrediction.h"
#include "CUDAWrapper.h"
#include "JFJochException.h"
#include "RawToConvertedGeometry.h"
#include "../include/spdlog/fmt/fmt.h"
#include "GitInfo.h"
using namespace std::literals::chrono_literals;
#define check_max(param, val, max) if ((val) > (max)) throw JFJochException(JFJochExceptionCategory::InputParameterAboveMax, param)
#define check_min(param, val, min) if ((val) < (min)) throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin, param)
#define check_finite(param, val) if (!std::isfinite(val)) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, param)
DiffractionExperiment::DiffractionExperiment() : DiffractionExperiment(DetJF4M()) {}
DiffractionExperiment::DiffractionExperiment(const DetectorSetup& det_setup)
: detector(det_setup) {
ndatastreams = 1;
series_id = 0;
mode = DetectorMode::Standard;
image_format_settings.Conv();
summation = 1;
cpu_summation = false;
}
// setter functions
DiffractionExperiment &DiffractionExperiment::Detector(const DetectorSetup &input) {
detector = input;
// Conversion is always default mode after switching detectors
mode = DetectorMode::Standard;
auto settings = detector.GetDefaultSettings();
if (settings)
detector_settings = *settings;
return *this;
}
DiffractionExperiment &DiffractionExperiment::Mode(DetectorMode input) {
// Handle allowed mode settings
switch (GetDetectorType()) {
case DetectorType::JUNGFRAU:
if (input == DetectorMode::DarkMask)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Dark mask is not supported for PSI JUNGFRAU detector");
break;
case DetectorType::DECTRIS:
if ((input == DetectorMode::PedestalG0) || (input == DetectorMode::PedestalG1) || (input == DetectorMode::PedestalG2))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pedestal data collection is not supported for DECTRIS detector");
break;
case DetectorType::EIGER:
if ((input == DetectorMode::PedestalG0) || (input == DetectorMode::PedestalG1) || (input == DetectorMode::PedestalG2) || (input == DetectorMode::DarkMask))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pedestal or dark mask data collection is not supported for PSI EIGER detector");
break;
}
mode = input;
return *this;
}
DiffractionExperiment &DiffractionExperiment::DataStreams(int64_t input) {
check_max("Number of data streams", input, 16);
check_min("Number of data streams", input, 1);
ndatastreams = input;
return *this;
}
DiffractionExperiment &DiffractionExperiment::ImagesPerTrigger(int64_t input) {
dataset.ImagesPerTrigger(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::NumTriggers(int64_t input) {
dataset.NumTriggers(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::FrameTime(std::chrono::nanoseconds in_frame_time,
std::chrono::nanoseconds in_count_time) {
DetectorSettings tmp = detector_settings;
if (in_count_time.count() == 0)
tmp.FrameTime(in_frame_time);
else
tmp.FrameTime(in_frame_time, in_count_time);
ImportDetectorSettings(tmp);
return *this;
}
DiffractionExperiment &DiffractionExperiment::PedestalG0Frames(int64_t input) {
detector_settings.PedestalG0Frames(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::PedestalG1Frames(int64_t input) {
detector_settings.PedestalG1Frames(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::PedestalG2Frames(int64_t input) {
detector_settings.PedestalG2Frames(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::IncidentEnergy_keV(float input) {
dataset.PhotonEnergy_keV(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::BeamX_pxl(float input) {
dataset.BeamX_pxl(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::BeamY_pxl(float input) {
dataset.BeamY_pxl(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::DetectorDistance_mm(float input) {
dataset.DetectorDistance_mm(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::FilePrefix(std::string input) {
dataset.FilePrefix(std::move(input));
return *this;
}
DiffractionExperiment &DiffractionExperiment::FilePrefixTrusted(std::string input) {
dataset.FilePrefixTrusted(std::move(input));
return *this;
}
DiffractionExperiment &DiffractionExperiment::UseInternalPacketGenerator(bool input) {
detector_settings.InternalGeneratorEnable(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::MaskModuleEdges(bool input) {
image_format_settings.MaskModuleEdges(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::Compression(CompressionAlgorithm input) {
dataset.Compression(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::MaskChipEdges(bool input) {
image_format_settings.MaskChipEdges(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::QRangeForAzimInt_recipA(float low, std::optional<float> high) {
az_integration_settings.QRange_recipA(low, high);
return *this;
}
DiffractionExperiment& DiffractionExperiment::BkgEstimateQRange_recipA(float low, float high) {
az_integration_settings.BkgEstimateQRange_recipA(low, high);
return *this;
}
DiffractionExperiment& DiffractionExperiment::QSpacingForAzimInt_recipA(float input) {
az_integration_settings.QSpacing_recipA(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::SetUnitCell(const std::optional<UnitCell> &cell) {
dataset.SetUnitCell(cell);
return *this;
}
DiffractionExperiment &DiffractionExperiment::ZMQPreviewPeriod(const std::optional<std::chrono::microseconds> &input) {
if (input.has_value()) {
check_min("Preview image generation period [ms]", input.value().count(), 0);
}
zmq_preview_period = input;
return *this;
}
DiffractionExperiment &DiffractionExperiment::SpaceGroupNumber(std::optional<int64_t> input) {
dataset.SpaceGroupNumber(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::StorageCells(int64_t input) {
detector_settings.StorageCells(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::StorageCellStart(int64_t input) {
detector_settings.StorageCellStart(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::SampleName(const std::string &input) {
dataset.SampleName(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::OverwriteExistingFiles(bool input) {
file_writer.OverwriteExistingFiles(input);
return *this;
}
// getter functions
int64_t DiffractionExperiment::GetNumTriggers() const {
if (GetDetectorMode() != DetectorMode::Standard)
// For pedestal and dark mask modes
return 1;
else if (IsPulsedSource())
// For pulsed source summation happens over multiple triggers
return dataset.GetNumTriggers() * GetSummation();
else
return dataset.GetNumTriggers();
}
DetectorMode DiffractionExperiment::GetDetectorMode() const {
return mode;
}
std::chrono::nanoseconds DiffractionExperiment::GetFrameTime() const {
switch (GetDetectorMode()) {
case DetectorMode::DarkMask:
return GetDarkMaskSettings().GetFrameTime();
case DetectorMode::PedestalG1:
case DetectorMode::PedestalG2:
return detector_settings.GetFrameTimePedestalG1G2();
default:
if ((GetDetectorType() != DetectorType::JUNGFRAU) && dataset.GetImageTime().has_value())
return dataset.GetImageTime().value();
return detector_settings.GetFrameTime();
}
}
std::chrono::nanoseconds DiffractionExperiment::GetDetectorPeriod() const {
// Without storage cells - this is just frame time
if (GetStorageCellNumber() == 1)
return GetFrameTime();
// With storage cells
// Do 100 Hz repetition for pedestal G1/G2
// Do 2 kHz repetition for conversion/raw/pedestal G0
switch (GetDetectorMode()) {
case DetectorMode::PedestalG1:
case DetectorMode::PedestalG2:
return detector_settings.GetFrameTimePedestalG1G2();
default:
return MIN_FRAME_TIME_JUNGFRAU_FULL_SPEED * GetStorageCellNumber();
}
}
std::chrono::nanoseconds DiffractionExperiment::GetImageTime() const {
switch (GetDetectorMode()) {
case DetectorMode::PedestalG1:
case DetectorMode::PedestalG2:
return detector_settings.GetFrameTimePedestalG1G2();
default:
return GetFrameTime() * GetSummation();
}
}
int64_t DiffractionExperiment::GetImageNum() const {
if (GetDetectorMode() == DetectorMode::Standard)
return GetFrameNum() / GetSummation();
return 0;
}
int64_t DiffractionExperiment::GetFrameNum() const {
return GetFrameNumPerTrigger() * GetNumTriggers();
}
int64_t DiffractionExperiment::GetFrameNumPerTrigger() const {
switch (GetDetectorMode()) {
case DetectorMode::DarkMask:
return GetDarkMaskSettings().GetNumberOfFrames();
case DetectorMode::PedestalG0:
return GetPedestalG0Frames() * GetStorageCellNumber();
case DetectorMode::PedestalG1:
return GetPedestalG1Frames() * GetStorageCellNumber();
case DetectorMode::PedestalG2:
return GetPedestalG2Frames()* GetStorageCellNumber();
default:
if (GetStorageCellNumber() > 1)
return GetStorageCellNumber();
else if (IsPulsedSource())
return 1;
else
return dataset.GetImageNumPerTrigger() * GetSummation();
}
}
std::chrono::nanoseconds DiffractionExperiment::GetFrameCountTime() const {
if ((GetDetectorType() != DetectorType::JUNGFRAU)
&& dataset.GetImageTime().has_value()
&& !detector_settings.GetCountTime().has_value())
return std::chrono::duration_cast<std::chrono::nanoseconds>(dataset.GetImageTime().value())
- detector.GetReadOutTime();
if (detector_settings.GetCountTime().has_value())
return detector_settings.GetCountTime().value();
return std::chrono::duration_cast<std::chrono::nanoseconds>(detector_settings.GetFrameTime())
- detector.GetReadOutTime();
}
bool DiffractionExperiment::GetFrameCountTimeAuto() const {
return !detector_settings.GetCountTime().has_value();
}
std::chrono::nanoseconds DiffractionExperiment::GetImageCountTime() const {
return GetFrameCountTime() * GetSummation();
}
int64_t DiffractionExperiment::GetPedestalG0Frames() const {
if (GetDetectorType() != DetectorType::JUNGFRAU)
return 0;
return detector_settings.GetPedestalG0Frames();
}
int64_t DiffractionExperiment::GetPedestalG1Frames() const {
if (GetDetectorType() != DetectorType::JUNGFRAU)
return 0;
return detector_settings.GetPedestalG1Frames();
}
int64_t DiffractionExperiment::GetPedestalG2Frames() const {
if (GetDetectorType() != DetectorType::JUNGFRAU)
return 0;
return detector_settings.GetPedestalG2Frames();
}
float DiffractionExperiment::GetIncidentEnergy_keV() const {
return dataset.GetPhotonEnergy_keV();
}
float DiffractionExperiment::GetWavelength_A() const {
if (instrument.IsElectronSource()) {
const double hc = WVL_1A_IN_KEV;
const double me_c2 = 511; // in keV
return static_cast<float>(hc / sqrt(dataset.GetPhotonEnergy_keV() * (dataset.GetPhotonEnergy_keV() + 2 * me_c2)));
}
return WVL_1A_IN_KEV / dataset.GetPhotonEnergy_keV();
}
float DiffractionExperiment::GetBeamX_pxl() const {
return dataset.GetBeamX_pxl();
}
float DiffractionExperiment::GetBeamY_pxl() const {
return dataset.GetBeamY_pxl();
}
float DiffractionExperiment::GetDetectorDistance_mm() const {
return dataset.GetDetectorDistance_mm();
}
Coord DiffractionExperiment::GetScatteringVector() const {
return dataset.GetScatteringVector();
}
std::string DiffractionExperiment::GetFilePrefix() const {
return dataset.GetFilePrefix();
}
CompressionAlgorithm DiffractionExperiment::GetCompressionAlgorithm() const {
return dataset.GetCompressionAlgorithm();
}
int64_t DiffractionExperiment::GetByteDepthImage() const {
if (detector.GetBitDepthImage())
return detector.GetBitDepthImage().value() / 8;
if (IsCPUSummation())
return 4;
auto bit_depth_image = image_format_settings.GetBitDepthImage();
if (!bit_depth_image.has_value()) {
if (GetBitDepthReadout() == 32)
return 4;
if (GetBitDepthReadout() == 8)
return 1;
return (GetSummation() > 2) ? 4 : 2;
}
return bit_depth_image.value() / 8;
}
bool DiffractionExperiment::IsPixelSigned() const {
auto pixel_signed = image_format_settings.IsPixelSigned();
if (!pixel_signed.has_value())
return IsJungfrauConvPhotonCnt() && (GetDetectorType() == DetectorType::JUNGFRAU);
else
return pixel_signed.value();
}
int64_t DiffractionExperiment::GetDataStreamsNum() const {
return std::min<int64_t>(ndatastreams, detector.GetModulesNum());
}
int64_t DiffractionExperiment::GetModulesNum(uint16_t data_stream) const {
if (data_stream >= GetDataStreamsNum())
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Non existing data stream");
return (detector.GetModulesNum() + (GetDataStreamsNum() - 1) - data_stream) / GetDataStreamsNum();
}
int64_t DiffractionExperiment::GetModulesNum() const {
return detector.GetModulesNum();
}
int64_t DiffractionExperiment::GetFirstModuleOfDataStream(uint16_t data_stream) const {
if (data_stream >= GetDataStreamsNum())
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Non exisiting data stream");
int64_t val = 0;
for (int i = 0; i < data_stream; i++) val += GetModulesNum(i);
return val;
}
int64_t DiffractionExperiment::GetMaxCompressedSize() const {
return MaxCompressedSize(GetCompressionAlgorithm(), GetPixelsNum(), GetByteDepthImage());
}
int64_t DiffractionExperiment::GetPixelsNum() const {
return GetXPixelsNum() * GetYPixelsNum();
}
int64_t DiffractionExperiment::GetXPixelsNum() const {
return detector.GetGeometry().GetWidth(IsGeometryTransformed());
}
int64_t DiffractionExperiment::GetYPixelsNum() const {
return detector.GetGeometry().GetHeight(IsGeometryTransformed());
}
int64_t DiffractionExperiment::GetPixelsNumConv() const {
return GetXPixelsNumConv() * GetYPixelsNumConv();
}
int64_t DiffractionExperiment::GetXPixelsNumConv() const {
return detector.GetGeometry().GetWidth(true);
}
int64_t DiffractionExperiment::GetYPixelsNumConv() const {
return detector.GetGeometry().GetHeight(true);
}
int64_t DiffractionExperiment::GetPixel0OfModuleConv(uint16_t module_number) const {
return detector.GetGeometry().GetPixel0(module_number, true);
}
int64_t DiffractionExperiment::GetOverflow() const {
switch (GetByteDepthImage()) {
case 4:
return IsPixelSigned() ? static_cast<int64_t>(INT32_MAX) : static_cast<int64_t>(UINT32_MAX);
case 2:
return IsPixelSigned() ? INT16_MAX : UINT16_MAX;
case 1:
return IsPixelSigned() ? INT8_MAX : UINT8_MAX;
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pixel depth unsupported");
}
}
int64_t DiffractionExperiment::GetSaturationLimit() const {
auto sat = detector.GetSaturationLimit();
auto overflow = GetOverflow();
if (!sat)
return overflow;
return std::min(sat.value(), overflow);
}
int64_t DiffractionExperiment::GetUnderflow() const {
if (!IsPixelSigned())
return -1;
switch (GetByteDepthImage()) {
case 1:
return INT8_MIN;
case 2:
return INT16_MIN;
case 4:
return INT32_MIN;
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pixel depth unsupported");
}
}
std::optional<std::chrono::microseconds> DiffractionExperiment::GetZMQPreviewPeriod() const {
if (IsPedestalRun())
return {};
else
return zmq_preview_period;
}
int64_t DiffractionExperiment::GetDefaultPlotBinning() const {
// If images are equal longer than 100 ms, don't bin images
// If data collection is shorter than 5s, don't bin
if ((GetImageTime() >= 100ms) || (GetImageNum() * GetImageTime() < 5s))
return 1;
return 500ms / GetImageTime(); // 1 bin == 500 ms
}
bool DiffractionExperiment::IsUsingInternalPacketGen() const {
return detector_settings.IsInternalGeneratorEnable();
}
uint32_t DiffractionExperiment::GetSrcIPv4Address(uint32_t data_stream, uint32_t half_module) const {
uint32_t host = half_module + detector.GetUDPInterfaceCount() * GetFirstModuleOfDataStream(data_stream);
return detector.GetSrcIPv4Addr(host);
}
bool DiffractionExperiment::GetMaskModuleEdges() const {
return image_format_settings.IsMaskModuleEdges();
}
bool DiffractionExperiment::GetMaskChipEdges() const {
return image_format_settings.IsMaskChipEdges();
}
std::optional<UnitCell> DiffractionExperiment::GetUnitCell() const {
return dataset.GetUnitCell();
}
std::string DiffractionExperiment::GetUnitCellString() const {
auto uc = dataset.GetUnitCell();
if (uc.has_value()) {
return fmt::format("{:.1f}, {:.1f}, {:.1f}, {:.1f}, {:.1f} {:.1f}",
uc.value().a,
uc.value().b,
uc.value().c,
uc.value().alpha,
uc.value().beta,
uc.value().gamma);
} else
return "-";
}
std::optional<int64_t> DiffractionExperiment::GetSpaceGroupNumber() const {
return dataset.GetSpaceGroupNumber();
}
int64_t DiffractionExperiment::GetStorageCellNumber() const {
auto storage_cells = detector_settings.GetStorageCells();
switch (GetDetectorMode()) {
case DetectorMode::PedestalG1:
if (IsFixedGainG1())
return storage_cells;
case DetectorMode::PedestalG2:
if (storage_cells > 1)
return 2;
else
return 1;
default:
return storage_cells;
}
}
int64_t DiffractionExperiment::GetStorageCellStart() const {
return detector_settings.GetStorageCellStart();
}
float DiffractionExperiment::GetLowQForAzimInt_recipA() const {
return az_integration_settings.GetLowQ_recipA();
}
float DiffractionExperiment::GetHighQForAzimInt_recipA() const {
return GetAzimuthalIntegrationSettings().GetHighQ_recipA();
}
float DiffractionExperiment::GetQSpacingForAzimInt_recipA() const {
return az_integration_settings.GetQSpacing_recipA();
}
DiffractionExperiment &DiffractionExperiment::MaxSpotCount(int64_t input) {
dataset.MaxSpotCount(input);
return *this;
}
int64_t DiffractionExperiment::GetMaxSpotCount() const {
if (!IsSpotFindingEnabled())
return 0;
return dataset.GetMaxSpotCount();
}
std::string DiffractionExperiment::GetSampleName() const {
return dataset.GetSampleName();
}
void DiffractionExperiment::CheckDataProcessingSettings(const SpotFindingSettings &settings) {
check_finite("Signal to noise threshold", settings.signal_to_noise_threshold);
check_min("Signal to noise threshold", settings.signal_to_noise_threshold, 1);
check_min("Photon count threshold", settings.photon_count_threshold, 0);
check_min("Minimum pixels per spot", settings.min_pix_per_spot.value_or(2), 1);
check_min("Maximum pixels per spot", settings.max_pix_per_spot, settings.min_pix_per_spot.value_or(2) + 1);
check_finite("Spot finding low resolution limit", settings.low_resolution_limit);
// An unset high-resolution limit means "as far as the detector reaches", so there is nothing to check.
// A value that is present still has to be finite - NaN fails every comparison below, so testing it
// inside the branch would let it through unchecked.
if (settings.high_resolution_limit.has_value())
check_finite("Spot finding high resolution limit", *settings.high_resolution_limit);
if (settings.high_resolution_limit.value_or(0.0f) > 0) {
check_min("Spot finding high resolution limit", *settings.high_resolution_limit, 0.5);
check_max("Spot finding high resolution limit", *settings.high_resolution_limit, 50.0);
if (settings.low_resolution_limit > 0) {
check_min("Spot finding low resolution limit", settings.low_resolution_limit,
*settings.high_resolution_limit);
}
} else if (settings.low_resolution_limit > 0) {
check_min("Spot finding low resolution limit", settings.low_resolution_limit, 1.0);
check_max("Spot finding low resolution limit", settings.low_resolution_limit, 50.0);
}
check_min("Ice ring width in Q-space (A^-1)", settings.ice_ring_width_Q_recipA, 0.0);
check_max("Ice ring width in Q-space (A^-1)", settings.ice_ring_width_Q_recipA, 1.0);
check_finite("Ice ring width in Q-space (A^-1)", settings.ice_ring_width_Q_recipA);
if (settings.high_res_gap_Q_recipA.has_value()) {
check_min("High resolution gap (A^-1)", settings.high_res_gap_Q_recipA.value(), 0.1);
check_max("High resolution gap (A^-1)", settings.high_res_gap_Q_recipA.value(), 5.0);
check_finite("High resolution gap (A^-1)", settings.high_res_gap_Q_recipA.value());
}
}
SpotFindingSettings DiffractionExperiment::DefaultDataProcessingSettings() {
return {};
}
void DiffractionExperiment::FillMessage(StartMessage &message) const {
message.images_per_file = GetImagesPerFile();
message.beam_center_x = GetBeamX_pxl();
message.beam_center_y = GetBeamY_pxl();
message.detector_distance = GetDetectorDistance_mm() * 1e-3f;
message.incident_wavelength = GetWavelength_A();
// NXmx incident_wavelength_spread is the FWHM of the wavelength distribution,
// in wavelength units. We store bandwidth as a relative FWHM (dlambda/lambda),
// so convert to absolute: dlambda = (dlambda/lambda) * lambda.
if (const auto bw = GetBandwidthFWHM())
message.incident_wavelength_spread = bw.value() * GetWavelength_A();
message.incident_energy = GetIncidentEnergy_keV() * 1e3f;
message.image_size_x = GetXPixelsNum();
message.image_size_y = GetYPixelsNum();
message.saturation_value = GetSaturationLimit() - 1;
message.error_value = GetUnderflow();
message.frame_time = std::chrono::duration<float>(GetImageTime()).count();
message.count_time = std::chrono::duration<float>(GetImageCountTime()).count();
message.number_of_images = GetImageNum();
message.pixel_size_x = GetPixelSize_mm() * 1e-3f;
message.pixel_size_y = GetPixelSize_mm() * 1e-3f;
message.sensor_material = detector.GetSensorMaterial();
message.sensor_thickness = detector.GetSensorThickness_um() * 1e-6f;
message.bit_depth_image = GetByteDepthImage() * 8;
message.bit_depth_readout = GetBitDepthReadout();
message.indexing_algorithm = GetIndexingAlgorithm();
message.images_per_trigger = dataset.GetImageNumPerTrigger();
if (GetDetectorType() == DetectorType::JUNGFRAU) {
message.storage_cell_number = GetStorageCellNumber();
message.storage_cell_delay_ns = GetStorageCellDelay().count();
}
message.file_prefix = GetFilePrefix();
message.pixel_signed = IsPixelSigned();
message.sample_name = GetSampleName();
message.max_spot_count = GetMaxSpotCount();
message.max_extra_lattices = indexing.GetMaxExtraLattices();
message.pixel_mask_enabled = IsApplyPixelMask();
message.detector_description = GetDetectorDescription();
message.space_group_number = GetSpaceGroupNumber();
message.unit_cell = GetUnitCell();
message.total_flux = GetTotalFlux();
message.attenuator_transmission = GetAttenuatorTransmission();
message.detector_translation[0] = 0.0f;
message.detector_translation[1] = 0.0f;
message.detector_translation[2] = GetDetectorDistance_mm() * 1e-3f;
message.source_name = GetSourceName();
message.source_type = GetSourceType();
message.instrument_name = GetInstrumentName();
message.summation = GetSummation();
message.user_data = GetHeaderAppendix();
message.countrate_correction_enabled = false;
message.flatfield_enabled = false;
message.goniometer = dataset.GetGoniometer();
message.grid_scan = dataset.GetGridScan();
message.run_number = GetRunNumber();
message.run_name = GetRunName();
message.gain_file_names = detector.GetGainFileNames();
message.rois = roi_mask.ExportMetadata();
message.data_reduction_factor_serialmx = GetLossyCompressionSerialMX();
message.experiment_group = dataset.GetExperimentGroup();
message.jfjoch_release = jfjoch_version();
message.detector_serial_number = detector.GetSerialNumber();
message.write_master_file = dataset.IsWriteNXmxHDF5Master();
message.overwrite = file_writer.IsOverwriteExistingFiles();
message.file_format = file_writer.GetFileFormat();
message.ring_current_mA = dataset.GetRingCurrent_mA();
message.sample_temperature_K = dataset.GetSampleTemperature_K();
message.fluorescence_spectrum = dataset.GetFluorescenceSpectrum();
message.poni_rot1 = dataset.GetPoniRot1_rad();
message.poni_rot2 = dataset.GetPoniRot2_rad();
message.poni_rot3 = dataset.GetPoniRot3_rad();
message.detect_ice_rings = dataset.IsDetectIceRings();
message.channels = {"default"};
message.smargon_position = dataset.GetSmargonPosition();
switch (GetDetectorType()) {
case DetectorType::JUNGFRAU:
message.jungfrau_conversion_enabled = IsJungfrauConvPhotonCnt();
if (IsJungfrauConvPhotonCnt())
message.jungfrau_conversion_factor = GetPhotonEnergyForConversion_keV() * 1000;
break;
case DetectorType::EIGER:
case DetectorType::DECTRIS:
message.threshold_energy["default"] = GetEigerThreshold_keV() * 1000.0f; // threshold in CBOR is in eV
break;
}
message.geometry_transformation_enabled = IsGeometryTransformed();
if (GetSummation() == 1)
message.summation_mode = "none";
else {
if (IsCPUSummation())
message.summation_mode = "cpu";
else
message.summation_mode = "fpga";
}
}
float DiffractionExperiment::GetPixelSize_mm() const {
return detector.GetPixelSize_mm();
}
std::string DiffractionExperiment::GetSourceName() const {
return instrument.GetSourceName();
}
std::string DiffractionExperiment::GetSourceType() const {
return instrument.GetSourceType();
}
std::string DiffractionExperiment::GetInstrumentName() const {
return instrument.GetInstrumentName();
}
int64_t DiffractionExperiment::GetModuleFastDirectionStep(uint16_t module_number) const {
return detector.GetGeometry().GetFastDirectionStep(module_number);
}
int64_t DiffractionExperiment::GetModuleSlowDirectionStep(uint16_t module_number) const {
return detector.GetGeometry().GetSlowDirectionStep(module_number);
}
std::vector<std::string> DiffractionExperiment::GetDetectorModuleHostname() const {
return detector.GetDetectorModuleHostname();
}
std::string DiffractionExperiment::GetDetectorDescription() const {
return detector.GetDescription();
}
DiffractionExperiment &DiffractionExperiment::ApplySolidAngleCorr(bool input) {
az_integration_settings.SolidAngleCorrection(input);
return *this;
}
bool DiffractionExperiment::GetApplySolidAngleCorr() const {
return az_integration_settings.IsSolidAngleCorrection();
}
bool DiffractionExperiment::GetSaveCalibration() const {
auto val = dataset.IsSaveCalibration();
if (val.has_value())
return val.value();
// By default calibration is saved if more than 4 images
// to limit cases were size of the file is determined by the calibration
return (GetImageNum() > 4) && (GetDetectorType() == DetectorType::JUNGFRAU);
}
DiffractionExperiment &DiffractionExperiment::StorageCellDelay(std::chrono::nanoseconds input) {
detector_settings.StorageCellDelay(input);
return *this;
}
std::chrono::nanoseconds DiffractionExperiment::GetStorageCellDelay() const {
return detector_settings.GetStorageCellDelay();
}
DiffractionExperiment &DiffractionExperiment::Summation(int64_t input) {
check_min("Summation factor", input, 1);
summation = input;
return *this;
}
int64_t DiffractionExperiment::GetSummation() const {
if (GetAutoSummation())
return summation;
return 1;
}
int64_t DiffractionExperiment::GetFPGASummation() const {
if (IsCPUSummation())
return 1;
return GetSummation();
}
int64_t DiffractionExperiment::GetByteDepthFPGA() const {
if (IsCPUSummation())
return 2;
return GetByteDepthImage();
}
int64_t DiffractionExperiment::GetUDPInterfaceCount() const {
return detector.GetUDPInterfaceCount();
}
std::vector<DetectorModuleConfig>
DiffractionExperiment::GetDetectorModuleConfig(const std::vector<AcquisitionDeviceNetConfig> &net_config) const{
std::vector<DetectorModuleConfig> ret;
for (int d = 0; d < GetDataStreamsNum(); d++) {
for (int m = 0; m < GetModulesNum(d); m++) {
DetectorModuleConfig mod_cfg;
mod_cfg.data_stream = d;
mod_cfg.udp_dest_port_1 = 1024 + m;
mod_cfg.udp_dest_port_2 = 1024 + m;
if (detector.GetUDPInterfaceCount() == 2) {
mod_cfg.ipv4_src_addr_1 = IPv4AddressToStr(GetSrcIPv4Address(d, 2 * m));
mod_cfg.ipv4_src_addr_2 = IPv4AddressToStr(GetSrcIPv4Address(d, 2 * m + 1));
} else {
mod_cfg.ipv4_src_addr_1 = IPv4AddressToStr(GetSrcIPv4Address(d, m));
mod_cfg.ipv4_src_addr_2 = IPv4AddressToStr(GetSrcIPv4Address(d, m)); // not used, settings just in case
}
mod_cfg.ipv4_dest_addr_1 = net_config[d].ipv4_addr;
mod_cfg.ipv4_dest_addr_2 = net_config[d].ipv4_addr;
mod_cfg.mac_addr_dest_1 = net_config[d].mac_addr;
mod_cfg.mac_addr_dest_2 = net_config[d].mac_addr;
mod_cfg.module_id_in_data_stream = m;
ret.emplace_back(std::move(mod_cfg));
}
}
return ret;
}
float DiffractionExperiment::GetLowQForBkgEstimate_recipA() const {
return az_integration_settings.GetBkgEstimateLowQ_recipA();
}
float DiffractionExperiment::GetHighQForBkgEstimate_recipA() const {
return az_integration_settings.GetBkgEstimateHighQ_recipA();
}
DiffractionExperiment &DiffractionExperiment::AttenuatorTransmission(const std::optional<float> &input) {
dataset.AttenuatorTransmission(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::TotalFlux(const std::optional<float> &input) {
dataset.TotalFlux(input);
return *this;
}
std::optional<float> DiffractionExperiment::GetAttenuatorTransmission() const {
return dataset.GetAttenuatorTransmission();
}
std::optional<float> DiffractionExperiment::GetTotalFlux() const {
return dataset.GetTotalFlux();
}
DiffractionExperiment &DiffractionExperiment::Goniometer(const std::optional<GoniometerAxis> &input) {
dataset.Goniometer(input);
return *this;
}
std::optional<GoniometerAxis> DiffractionExperiment::GetGoniometer() const {
return dataset.GetGoniometer();
}
std::optional<GridScanSettings> DiffractionExperiment::GetGridScan() const {
return dataset.GetGridScan();
}
DiffractionExperiment &DiffractionExperiment::UsingGainHG0(bool input) {
detector_settings.UseGainHG0(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::FixedGainG1(bool input) {
detector_settings.FixGainG1(input);
return *this;
}
bool DiffractionExperiment::IsFixedGainG1() const {
return detector_settings.IsFixGainG1();
}
bool DiffractionExperiment::IsUsingGainHG0() const {
return detector_settings.IsUseGainHG0();
}
DiffractionExperiment &DiffractionExperiment::HeaderAppendix(const nlohmann::json &input) {
dataset.HeaderAppendix(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::ImageAppendix(const nlohmann::json &input) {
dataset.ImageAppendix(input);
return *this;
}
const nlohmann::json& DiffractionExperiment::GetHeaderAppendix() const {
return dataset.GetHeaderAppendix();
}
const nlohmann::json& DiffractionExperiment::GetImageAppendix() const {
return dataset.GetImageAppendix();
}
uint64_t DiffractionExperiment::GetRunNumber() const {
if (dataset.GetRunNumber())
return dataset.GetRunNumber().value();
return series_id;
}
std::string DiffractionExperiment::GetRunName() const {
auto run_name = dataset.GetRunName();
if (run_name)
return run_name.value();
else
return std::to_string(series_id) + ":" + dataset.GetFilePrefix();
}
DiffractionExperiment &DiffractionExperiment::IncrementRunNumber() {
series_id++;
return *this;
}
Coord DiffractionExperiment::GetModuleFastDirection(uint16_t module_number) const {
return detector.GetGeometry().GetFastDirection(module_number);
}
Coord DiffractionExperiment::GetModuleSlowDirection(uint16_t module_number) const {
return detector.GetGeometry().GetSlowDirection(module_number);
}
DiffractionExperiment &DiffractionExperiment::JungfrauConvPhotonCnt(bool input) {
image_format_settings.JungfrauConversion(input);
return *this;
}
bool DiffractionExperiment::IsJungfrauConvPhotonCnt() const {
if (!IsPedestalRun() && (GetDetectorType() == DetectorType::JUNGFRAU))
return image_format_settings.IsJungfrauConversion();
else
return false;
}
DiffractionExperiment &DiffractionExperiment::DetectorDelay(std::chrono::nanoseconds input) {
detector_settings.DetectorDelay(input);
return *this;
}
std::chrono::nanoseconds DiffractionExperiment::GetDetectorDelay() const {
return detector_settings.GetDetectorDelay();
}
const DetectorSetup &DiffractionExperiment::GetDetectorSetup() const {
return detector;
}
DetectorSetup &DiffractionExperiment::Detector() {
return detector;
}
DiffractionExperiment &DiffractionExperiment::PulsedSource(bool input) {
instrument.PulsedSource(input);
return *this;
}
bool DiffractionExperiment::IsPulsedSource() const {
return instrument.IsPulsedSource();
}
bool DiffractionExperiment::IsSpotFindingEnabled() const {
return dataset.IsSpotFindingEnabled() && !IsPedestalRun();
}
float DiffractionExperiment::GetPhotonEnergyForConversion_keV() const {
auto val = GetJungfrauConversionFactor_keV();
if (val.has_value())
return val.value();
else
return GetIncidentEnergy_keV();
}
DiffractionExperiment &DiffractionExperiment::InternalPacketGeneratorImages(int64_t input) {
detector_settings.InternalGeneratorImages(input);
return *this;
}
int64_t DiffractionExperiment::GetInternalPacketGeneratorImages() const {
return detector_settings.GetInternalGeneratorImages();
}
DiffractionExperiment &DiffractionExperiment::ImportDatasetSettings(const DatasetSettings &input) {
auto tmp = dataset;
dataset = input;
auto image_time = input.GetImageTime();
if (image_time) {
switch (GetDetectorType()) {
case DetectorType::EIGER:
case DetectorType::DECTRIS:
check_min("Image time [ns]", image_time.value().count(), detector.GetMinFrameTime().count());
summation = 1;
break;
case DetectorType::JUNGFRAU:
if (image_time->count() % GetFrameTime().count() != 0) {
dataset = tmp;
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Image time must be multiple of frame time");
}
if (GetFrameTime().count() == 0) {
dataset = tmp;
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Frame time cannot be zero");
}
if (image_time < GetFrameTime()) {
dataset = tmp;
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Image time cannot be less than base frame time");
}
this->Summation(image_time.value() / GetFrameTime());
break;
}
} else
summation = 1;
if (dataset.GridScan())
dataset.GridScan()->ImageNum(GetImageNum());
if (GetFrameNum() >= MAX_FRAMES) {
dataset = tmp;
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Frame number (summation * images_per_trigger * ntrigger) cannot exceed "
+ std::to_string(MAX_FRAMES));
}
if (GetImageNum() > MAX_IMAGE_NUMBER) {
dataset = tmp;
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Number of images cannot exceed " + std::to_string(MAX_IMAGE_NUMBER));
}
return *this;
}
DatasetSettings DiffractionExperiment::GetDatasetSettings() const {
return dataset;
}
ROIMap &DiffractionExperiment::ROI() {
return roi_mask;
}
const ROIMap &DiffractionExperiment::ROI() const {
return roi_mask;
}
std::vector<uint16_t> DiffractionExperiment::ExportROIMap() const {
return roi_mask.GetROIMap(GetDiffractionGeometry(), GetXPixelsNumConv(), GetYPixelsNumConv());
}
DiffractionExperiment &DiffractionExperiment::ImagesPerFile(int64_t input) {
dataset.ImagesPerFile(input);
return *this;
}
int64_t DiffractionExperiment::GetImagesPerFile() const {
auto tmp = dataset.GetImagesPerFile();
if (tmp == 0
|| file_writer.GetFileFormat() == FileWriterFormat::NXmxIntegrated)
return GetImageNum();
else
return tmp;
}
int64_t DiffractionExperiment::GetImageBufferLocationSize() const {
// A buffer slot holds the compressed image plus the per-image CBOR metadata (spot list, reflection
// list, azimuthal profile, ...). The reflection list dominates and is bounded by
// BraggPrediction::kOnlineMaxReflections at ~170 B each - the ONLINE limit, not the offline one:
// the slot size divides a fixed total buffer, so sizing every slot for the offline limit would cut
// the slot count, and the receiver's ability to absorb a burst, by about three. Derived from that
// constant
// rather than restate it - the two used to be written down separately and a raised cap would have
// silently outgrown the slot. Half again on top covers the capped spot list and the profile. A
// frame whose metadata still does not fit is dropped individually by the receiver rather than
// aborting the collection.
constexpr int64_t kReflectionBytes = 170;
const int64_t metadata = BraggPrediction::kOnlineMaxReflections * kReflectionBytes * 3 / 2;
return GetMaxCompressedSize() + metadata;
}
float DiffractionExperiment::GetLossyCompressionSerialMX() const {
return dataset.GetLossyCompressionSerialMX();
}
DiffractionExperiment &DiffractionExperiment::LossyCompressionSerialMX(float input) {
dataset.LossyCompressionSerialMX(input);
return *this;
}
std::optional<int64_t> DiffractionExperiment::GetLossyCompressionPoisson() const {
return dataset.GetLossyCompressionPoisson();
}
DiffractionExperiment &DiffractionExperiment::LossyCompressionPoisson(const std::optional<int64_t> &input) {
dataset.LossyCompressionPoisson(input);
return *this;
}
std::string DiffractionExperiment::GetExperimentGroup() const {
return dataset.GetExperimentGroup();
}
std::optional<int64_t> DiffractionExperiment::GetPixelValueLowThreshold() const {
return dataset.GetPixelValueLowThreshold();
}
DiffractionExperiment &DiffractionExperiment::PixelValueLowThreshold(const std::optional<int64_t> &input) {
dataset.PixelValueLowThreshold(input);
return *this;
}
std::optional<int64_t> DiffractionExperiment::GetPixelValueHighThreshold() const {
return dataset.GetPixelValueHighThreshold();
}
DiffractionExperiment &DiffractionExperiment::PixelValueHighThreshold(const std::optional<int64_t> &input) {
dataset.PixelValueHighThreshold(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::ImportInstrumentMetadata(const InstrumentMetadata &input) {
instrument = input;
return *this;
}
InstrumentMetadata DiffractionExperiment::GetInstrumentMetadata() const {
return instrument;
}
DiffractionExperiment &DiffractionExperiment::ImportFileWriterSettings(const FileWriterSettings &input) {
file_writer = input;
return *this;
}
FileWriterSettings DiffractionExperiment::GetFileWriterSettings() const {
return file_writer;
}
bool DiffractionExperiment::IsGeometryTransformed() const {
// For DECTRIS detectors always operate in transformed geometry
return (GetDetectorType() == DetectorType::DECTRIS) || image_format_settings.IsGeometryTransformed();
}
DiffractionExperiment &DiffractionExperiment::GeometryTransformation(bool input) {
image_format_settings.GeometryTransformed(input);
return *this;
}
int64_t DiffractionExperiment::GetImageFillValue() const {
switch (GetByteDepthImage()) {
case 1:
if (IsPixelSigned())
return INT8_MIN;
return UINT8_MAX;
case 2:
if (IsPixelSigned())
return INT16_MIN;
return UINT16_MAX;
case 4:
if (IsPixelSigned())
return INT32_MIN;
return UINT32_MAX;
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pixel depth unsupported");
}
}
int64_t DiffractionExperiment::GetBitDepthReadout() const {
if (GetDetectorType() == DetectorType::EIGER)
return GetEigerBitDepth();
auto det_value = detector.GetBitDepthReadout();
if (det_value)
return det_value.value();
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Bit depth readout not configured");
}
bool DiffractionExperiment::IsPedestalRun() const {
switch (GetDetectorMode()) {
case DetectorMode::PedestalG0:
case DetectorMode::PedestalG1:
case DetectorMode::PedestalG2:
return true;
default:
return false;
}
}
bool DiffractionExperiment::GetAutoSummation() const {
if (IsPedestalRun() || (GetStorageCellNumber() > 1))
return false; // for pedestal or more than 1 storage cell summation doesn't make sense and should be always turned off
else
return image_format_settings.IsAutoSummation();
}
DiffractionExperiment &DiffractionExperiment::AutoSummation(bool input) {
image_format_settings.AutoSummation(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::BitDepthImage(const std::optional<int64_t> &input) {
image_format_settings.BitDepthImage(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::PixelSigned(const std::optional<bool> &input) {
image_format_settings.PixelSigned(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::JungfrauConversionFactor_keV(const std::optional<float> &input) {
image_format_settings.JungfrauConvFactor_keV(input);
return *this;
}
std::optional<float> DiffractionExperiment::GetJungfrauConversionFactor_keV() const {
return image_format_settings.GetJungfrauConvFactor_keV();
}
DiffractionExperiment & DiffractionExperiment::Conversion() {
image_format_settings.Conv();
return *this;
}
DiffractionExperiment & DiffractionExperiment::Raw() {
image_format_settings.Raw();
return *this;
}
DiffractionExperiment &DiffractionExperiment::ImportDetectorSettings(const DetectorSettings &input) {
check_min("Frame time [ns]", input.GetFrameTime().count(), detector.GetMinFrameTime().count());
if (GetDetectorType() == DetectorType::JUNGFRAU) {
if (!input.GetCountTime().has_value()) {
// implicit count time
check_max("Frame time [ns]", input.GetFrameTime().count(),
(MAX_COUNT_TIME_JUNGFRAU + detector.GetReadOutTime()).count());
} else {
// explicit count time
check_max("Count time [ns]", input.GetCountTime().value().count(),
MAX_COUNT_TIME_JUNGFRAU.count());
}
if ((input.GetTiming() == DetectorTiming::Burst) ||
(input.GetTiming() == DetectorTiming::Gated))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Burst and gated timing modes not supported in JUNGFRAU");
}
detector_settings = input;
return *this;
}
DetectorSettings DiffractionExperiment::GetDetectorSettings() const {
return detector_settings;
}
DiffractionExperiment &DiffractionExperiment::ImportImageFormatSettings(const ImageFormatSettings &input) {
image_format_settings = input;
return *this;
}
ImageFormatSettings DiffractionExperiment::GetImageFormatSettings() const {
return image_format_settings;
}
DiffractionExperiment &DiffractionExperiment::ImportAzimuthalIntegrationSettings(const AzimuthalIntegrationSettings &input) {
az_integration_settings = input;
return *this;
}
AzimuthalIntegrationSettings DiffractionExperiment::GetAzimuthalIntegrationSettings() const {
// An unset high q means "as far as the detector reaches", so resolve it here, where the geometry is
// known. Everyone reads the settings through this getter, so nobody sees an unresolved q range.
AzimuthalIntegrationSettings ret = az_integration_settings;
ret.ResolveHighQ(GetDetectorMaxQ_recipA());
return ret;
}
float DiffractionExperiment::GetDetectorMaxResolution_A() const {
const float q = GetDetectorMaxQ_recipA();
return q > 0.0f ? 2.0f * static_cast<float>(PI) / q : 0.0f;
}
float DiffractionExperiment::GetDetectorMaxQ_recipA() const {
const DiffractionGeometry geom = GetDiffractionGeometry();
const auto width = static_cast<float>(GetXPixelsNumConv());
const auto height = static_cast<float>(GetYPixelsNumConv());
// The largest scattering angle sits at one of the detector corners, wherever the beam centre is.
float q = 0.0f;
for (const float x: {0.0f, width})
for (const float y: {0.0f, height})
q = std::max(q, geom.PxlToQ(x, y));
return q;
}
DiffractionExperiment &DiffractionExperiment::PolarizationFactor(const std::optional<float> &input) {
dataset.PolarizationFactor(input);
return *this;
}
std::optional<float> DiffractionExperiment::GetPolarizationFactor() const {
return dataset.GetPolarizationFactor();
}
DiffractionExperiment &DiffractionExperiment::BandwidthFWHM(const std::optional<float> &input) {
dataset.BandwidthFWHM(input);
return *this;
}
std::optional<float> DiffractionExperiment::GetBandwidthFWHM() const {
return dataset.GetBandwidthFWHM();
}
DiffractionExperiment &DiffractionExperiment::SaveCalibration(const std::optional<bool> &input) {
dataset.SaveCalibration(input);
return *this;
}
float DiffractionExperiment::GetPedestalG0RMSLimit() const {
return image_format_settings.GetPedestalG0RMSLimit();
}
uint32_t DiffractionExperiment::GetPedestalMinImageCount() const {
return detector_settings.GetPedestalMinImageCount();
}
float DiffractionExperiment::GetEigerThreshold_keV() const {
float thr;
if (GetDetectorMode() == DetectorMode::DarkMask)
thr = GetDarkMaskSettings().GetThreshold_keV();
else {
auto val = detector_settings.GetEigerThreshold_keV();
if (val)
thr = val.value();
else
thr = GetIncidentEnergy_keV() / 2.0f;
}
if (thr < detector.GetMinThreshold_keV())
thr = detector.GetMinThreshold_keV();
return thr;
}
DetectorTiming DiffractionExperiment::GetDetectorTiming() const {
// For calibration use Auto timing mode
switch (GetDetectorMode()) {
case DetectorMode::DarkMask:
return DetectorTiming::Auto;
default:
return detector_settings.GetTiming();
}
}
bool DiffractionExperiment::IsDetectorModuleSync() const {
return detector.IsModuleSync();
}
int64_t DiffractionExperiment::GetEigerBitDepth() const {
auto tmp = detector_settings.GetEigerBitDepth();
if (tmp.has_value())
return tmp.value();
if (GetFrameTime() >= std::chrono::microseconds(2622) )
return 32;
if (GetFrameTime() < std::chrono::microseconds(500))
return 8;
return 16;
}
DiffractionExperiment &DiffractionExperiment::EigerBitDepth(const std::optional<int64_t> &input) {
detector_settings.EigerBitDepth(input);
return *this;
}
DetectorType DiffractionExperiment::GetDetectorType() const {
return detector.GetDetectorType();
}
bool DiffractionExperiment::IsMaskPixelsWithoutG0() const {
if (GetDetectorType() == DetectorType::JUNGFRAU)
return image_format_settings.IsMaskPixelsWithoutG0();
return false;
}
bool DiffractionExperiment::IsApplyPixelMask() const {
return image_format_settings.IsApplyPixelMask() && !IsPedestalRun();
}
DiffractionExperiment &DiffractionExperiment::CPUSummation(bool input) {
cpu_summation = input;
return *this;
}
DiffractionExperiment &DiffractionExperiment::ApplyPixelMask(bool input) {
image_format_settings.ApplyPixelMask(input);
return *this;
}
bool DiffractionExperiment::IsCPUSummation() const {
if (summation == 1)
return false;
if (summation >= MAX_FPGA_SUMMATION)
return true;
return cpu_summation;
}
DiffractionExperiment & DiffractionExperiment::ElectronSource(bool input) {
instrument.ElectronSource(input);
return *this;
}
bool DiffractionExperiment::IsElectronSource() const {
return instrument.IsElectronSource();
}
DiffractionExperiment &DiffractionExperiment::SetFileWriterFormat(FileWriterFormat input) {
file_writer.FileFormat(input);
return *this;
}
FileWriterFormat DiffractionExperiment::GetFileWriterFormat() const {
return file_writer.GetFileFormat();
}
DiffractionGeometry DiffractionExperiment::GetDiffractionGeometry() const {
DiffractionGeometry g;
g.Wavelength_A(GetWavelength_A())
.PixelSize_mm(GetPixelSize_mm())
.BeamX_pxl(dataset.GetBeamX_pxl())
.BeamY_pxl(dataset.GetBeamY_pxl())
.DetectorDistance_mm(dataset.GetDetectorDistance_mm())
.PoniRot1_rad(dataset.GetPoniRot1_rad())
.PoniRot2_rad(dataset.GetPoniRot2_rad())
.PoniRot3_rad(dataset.GetPoniRot3_rad())
.Rotation(dataset.GetGoniometer());
return g;
}
void DiffractionExperiment::CalcAzIntCorrRawCoord(float *output, size_t module_number) const {
if (module_number >= GetModulesNum())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Wrong module number");
auto geom = GetDiffractionGeometry();
for (int i = 0; i < RAW_MODULE_SIZE; i++) {
auto [x,y] = RawToConvertedCoordinate(*this, module_number, i);
if (GetApplySolidAngleCorr())
output[i] /= geom.CalcAzIntSolidAngleCorr(static_cast<float>(x), static_cast<float>(y));
auto p = GetPolarizationFactor();
if (p.has_value())
output[i] /= geom.CalcAzIntPolarizationCorr(static_cast<float>(x), static_cast<float>(y), p.value());
}
}
void DiffractionExperiment::CalcSpotFinderResolutionMap(float *data, size_t module_number) const {
if (module_number >= GetModulesNum())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Wrong module number");
auto geom = GetDiffractionGeometry();
for (int i = 0; i < RAW_MODULE_SIZE; i++) {
auto [x,y] = RawToConvertedCoordinate(*this, module_number, i);
data[i] = geom.PxlToRes(static_cast<float>(x), static_cast<float>(y));
}
}
CompressedImageMode DiffractionExperiment::GetImageMode() const {
switch (GetByteDepthImage()) {
case 1:
return (IsPixelSigned() ? CompressedImageMode::Int8 : CompressedImageMode::Uint8);
case 2:
return (IsPixelSigned() ? CompressedImageMode::Int16 : CompressedImageMode::Uint16);
case 4:
return (IsPixelSigned() ? CompressedImageMode::Int32 : CompressedImageMode::Uint32);
default:
throw (JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Bit depth not supported"));
}
}
DiffractionExperiment &DiffractionExperiment::IndexingAlgorithm(IndexingAlgorithmEnum input) {
indexing.Algorithm(input);
return *this;
}
IndexingAlgorithmEnum DiffractionExperiment::GetIndexingAlgorithm() const {
// Rotation indexing accumulates a dense multi-frame reciprocal-space cloud that needs a GLOBAL
// lattice finder (3D FFT). ffbidx is a single-still, known-cell ORIENTATION finder: it cannot
// recover the global orientation from that cloud (it locks onto a local wedge -> garbage). So
// rotation always uses FFT (GPU) or FFTW (CPU), regardless of the requested algorithm or whether a
// cell is known - a reference MTZ (-z) or -C supplies a cell and used to silently flip Auto->FFBIDX,
// switching the rotation indexer to the one solver that cannot handle its input.
if (IsRotationIndexing())
return get_gpu_count() == 0 ? IndexingAlgorithmEnum::FFTW : IndexingAlgorithmEnum::FFT;
auto cell = GetUnitCell().has_value();
switch (indexing.GetAlgorithm()) {
case IndexingAlgorithmEnum::FFBIDX:
if (!cell)
return IndexingAlgorithmEnum::None;
return IndexingAlgorithmEnum::FFBIDX;
case IndexingAlgorithmEnum::Auto:
if (get_gpu_count() == 0)
return IndexingAlgorithmEnum::FFTW;
if (!cell)
return IndexingAlgorithmEnum::FFT;
return IndexingAlgorithmEnum::FFBIDX;
case IndexingAlgorithmEnum::FFT:
return IndexingAlgorithmEnum::FFT;
case IndexingAlgorithmEnum::FFTW:
return IndexingAlgorithmEnum::FFTW;
default:
return IndexingAlgorithmEnum::None;
}
}
IndexingSettings DiffractionExperiment::GetIndexingSettings() const {
return indexing;
}
DiffractionExperiment &DiffractionExperiment::ImportIndexingSettings(const IndexingSettings &input) {
indexing = input;
return *this;
}
float DiffractionExperiment::GetIndexingTolerance() const {
return indexing.GetTolerance();
}
DiffractionExperiment &DiffractionExperiment::IndexingTolerance(float input) {
indexing.Tolerance(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::GridScan(const std::optional<GridScanSettings> &input) {
dataset.GridScan(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::SampleTemperature_K(const std::optional<float> &input) {
dataset.SampleTemperature_K(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::RingCurrent_mA(const std::optional<float> &input) {
dataset.RingCurrent_mA(input);
return *this;
}
std::optional<float> DiffractionExperiment::GetSampleTemperature_K() const {
return dataset.GetSampleTemperature_K();
}
std::optional<float> DiffractionExperiment::GetRingCurrent_mA() const {
return dataset.GetRingCurrent_mA();
}
DiffractionExperiment &DiffractionExperiment::ImportBraggIntegrationSettings(const BraggIntegrationSettings &input) {
bragg_integration_settings = input;
return *this;
}
BraggIntegrationSettings DiffractionExperiment::GetBraggIntegrationSettings() const {
return bragg_integration_settings;
}
DiffractionExperiment &DiffractionExperiment::ImportScalingSettings(const ScalingSettings &input) {
scaling_settings = input;
return *this;
}
ScalingSettings DiffractionExperiment::GetScalingSettings() const {
return scaling_settings;
}
DiffractionExperiment &DiffractionExperiment::PoniRot1_rad(float input) {
dataset.PoniRot1_rad(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::PoniRot2_rad(float input) {
dataset.PoniRot2_rad(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::PoniRot3_rad(float input) {
dataset.PoniRot3_rad(input);
return *this;
}
float DiffractionExperiment::GetPoniRot1_rad() const {
return dataset.GetPoniRot1_rad();
}
float DiffractionExperiment::GetPoniRot2_rad() const {
return dataset.GetPoniRot2_rad();
}
float DiffractionExperiment::GetPoniRot3_rad() const {
return dataset.GetPoniRot3_rad();
}
GeomRefinementAlgorithmEnum DiffractionExperiment::GetGeomRefinementAlgorithm() const {
return indexing.GetGeomRefinementAlgorithm();
}
DiffractionExperiment &DiffractionExperiment::GeomRefinementAlgorithm(GeomRefinementAlgorithmEnum input) {
indexing.GeomRefinementAlgorithm(input);
return *this;
}
gemmi::CrystalSystem DiffractionExperiment::GetCrystalSystem() const {
auto sg = GetGemmiSpaceGroup();
if (!sg)
return gemmi::CrystalSystem::Monoclinic;
return sg->crystal_system();
}
std::string DiffractionExperiment::GetSpaceGroupName() const {
auto sg = GetGemmiSpaceGroup();
if (!sg)
return "";
return sg->short_name();
}
std::optional<gemmi::SpaceGroup> DiffractionExperiment::GetGemmiSpaceGroup() const {
auto sg = GetSpaceGroupNumber();
if (!sg)
return std::nullopt;
const gemmi::SpaceGroup *g = gemmi::find_spacegroup_by_number(sg.value());
if (g == nullptr)
return std::nullopt;
return *g;
}
char DiffractionExperiment::GetCentering() const {
auto sg = GetGemmiSpaceGroup();
if (!sg)
return 'P';
return sg->centring_type();
}
DiffractionExperiment &DiffractionExperiment::FluorescenceSpectrum(const XrayFluorescenceSpectrum &input) {
dataset.FluorescenceSpectrum(input);
return *this;
}
DiffractionExperiment &DiffractionExperiment::DetectIceRings(bool input) {
dataset.DetectIceRings(input);
return *this;
}
bool DiffractionExperiment::IsDetectIceRings() const {
return dataset.IsDetectIceRings();
}
const XrayFluorescenceSpectrum &DiffractionExperiment::GetFluorescenceSpectrum() const {
return dataset.GetFluorescenceSpectrum();
}
DarkMaskSettings DiffractionExperiment::GetDarkMaskSettings() const {
return dark_mask_settings;
}
DiffractionExperiment &DiffractionExperiment::ImportDarkMaskSettings(const DarkMaskSettings &input) {
dark_mask_settings = input;
return *this;
}
int64_t DiffractionExperiment::GetDarkMaskNumberOfFrames() const {
if (GetDetectorType() == DetectorType::DECTRIS)
return GetDarkMaskSettings().GetNumberOfFrames();
return 0;
}
bool DiffractionExperiment::IsRotationIndexing() const {
return GetGoniometer().has_value() && indexing.GetRotationIndexing();
}
DiffractionExperiment &DiffractionExperiment::RunNumber(uint64_t input) {
series_id = input;
return *this;
}
std::optional<double> DiffractionExperiment::GetRotationWedgeForScaling() const {
return scaling_settings.GetRotationWedgeForScaling();
}
bool DiffractionExperiment::GetRefineRotationWedgeInScaling() const {
return scaling_settings.GetRotationWedgeForScaling().has_value() && scaling_settings.GetRefineWedge();
}